{"repo": "89luca89/clampdown", "files": {"pkg/agent/opencode.go": {"3507b5e2c8e13094c1a00527fe9ebef77fde2313": {"t": 1785745619.1721375, "n": 4167, "h": "c7b77322431d0947", "c": "// SPDX-License-Identifier: GPL-3.0-only\n\npackage agent\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"path/filepath\"\n)\n\n// OpenCode implements Agent for the OpenCode CLI (anomalyco/opencode).\ntype OpenCode struct{}\n\nfunc (o *OpenCode) Name() string { return \"opencode\" }\nfunc (o *OpenCode) Image() string { return \"ghcr.io/89luca89/clampdown-opencode:latest\" }\n\nfunc (o *OpenCode) EgressDomains() []string {\n\treturn []string{\n\t\t// OpenCode infrastructure\n\t\t\"opencode.ai\",\n\t\t\"models.dev\",\n\t\t\"mcp.exa.ai\",\n\t\t\"registry.npmjs.org\",\n\t\t// AI provider APIs (multi-provider agent)\n\t\t\"api.anthropic.com\",\n\t\t\"api.openai.com\",\n\t\t\"generativelanguage.googleapis.com\",\n\t\t\"api.groq.com\",\n\t\t\"api.deepseek.com\",\n\t\t\"api.mistral.ai\",\n\t\t\"api.x.ai\",\n\t\t\"openrouter.ai\",\n\t}\n}\n\nfunc (o *OpenCode) Mounts() []Mount { return nil }\n\nfunc (o *OpenCode) ConfigOverlays() []Mount {\n\tcfgDir := filepath.Join(Home, \".config\", \"opencode\")\n\treturn []Mount{\n\t\t{Src: filepath.Join(cfgDir, \"opencode.json\"), Dst: filepath.Join(cfgDir, \"opencode.json\")},\n\t\t{Src: filepath.Join(cfgDir, \"opencode.jsonc\"), Dst: filepath.Join(cfgDir, \"opencode.jsonc\")},\n\t}\n}\n\n// Env redirects TMPDIR because Bun-compiled binaries extract and dlopen native\n// .so files at startup. Default /tmp is mounted noexec, causing a silent hang.\nfunc (o *OpenCode) Env() map[string]string {\n\treturn map[string]string{\n\t\t\"TMPDIR\": filepath.Join(Home, \".config\", \"opencode\", \"tmp\"),\n\t}\n}\n\nfunc (o *OpenCode) Args(passthrough []string) []string {\n\treturn passthrough\n}\n\n// PromptFile returns ~/.config/opencode/AGENTS.md — OpenCode\n// auto-discovers AGENTS.md from ~/.config/opencode/ as global rules.\nfunc (o *OpenCode) PromptFile() string {\n\treturn filepath.Join(Home, \".config\", \"opencode\", \"AGENTS.md\")\n}\n\n// ProxyRoutes returns upstream API routes for all supported providers.\nfunc (o *OpenCode) ProxyRoutes() []ProxyRoute {\n\treturn []ProxyRoute{\n\t\t{Port: ProxyPort, Upstream: \"https://api.anthropic.com/v1\",\n\t\t\tKeyEnv: \"ANTHROPIC_API_KEY\", HeaderName: \"x-api-key\",\n\t\t\tBaseURLEnv: \"ANTHROPIC_BASE_URL\"},\n\t\t{Port: ProxyPort, Upstream: \"https://api.openai.com/v1\",\n\t\t\tKeyEnv: \"OPENAI_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tBaseURLEnv: \"OPENAI_BASE_URL\"},\n\t\t{Port: ProxyPort, Upstream: \"https://generativelanguage.googleapis.com\",\n\t\t\tKeyEnv: \"GOOGLE_GENERATIVE_AI_API_KEY\", KeyEnvFallback: \"GEMINI_API_KEY\",\n\t\t\tHeaderName: \"x-goog-api-key\",\n\t\t\tProviderID: \"google\"},\n\t\t{Port: ProxyPort, Upstream: \"https://api.groq.com/openai/v1\",\n\t\t\tKeyEnv: \"GROQ_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"groq\"},\n\t\t{Port: ProxyPort, Upstream: \"https://api.deepseek.com/v1\",\n\t\t\tKeyEnv: \"DEEPSEEK_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"deepseek\"},\n\t\t{Port: ProxyPort, Upstream: \"https://api.mistral.ai/v1\",\n\t\t\tKeyEnv: \"MISTRAL_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"mistral\"},\n\t\t{Port: ProxyPort, Upstream: \"https://api.x.ai/v1\",\n\t\t\tKeyEnv: \"XAI_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"xai\"},\n\t\t{Port: ProxyPort, Upstream: \"https://openrouter.ai/api/v1\",\n\t\t\tKeyEnv: \"OPENROUTER_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"openrouter\"},\n\t\t{Port: ProxyPort, Upstream: \"https://opencode.ai/zen/v1\",\n\t\t\tKeyEnv: \"OPENCODE_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"opencode\"},\n\t}\n}\n\n// ProxyEnvOverride builds OPENCODE_CONFIG_CONTENT for providers whose SDK\n// doesn't read a *_BASE_URL env var. The JSON is deep-merged by OpenCode\n// at highest precedence — no clobbering of user config.\nfunc (o *OpenCode) ProxyEnvOverride(routes []ProxyRoute) map[string]string {\n\tproviders := make(map[string]any)\n\tfor _, r := range routes {\n\t\tif r.ProviderID == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tproviders[r.ProviderID] = map[string]any{\n\t\t\t\"options\": map[string]any{\n\t\t\t\t\"baseURL\": fmt.Sprintf(\"http://localhost:%d\", r.Port),\n\t\t\t},\n\t\t}\n\t}\n\tif len(providers) == 0 {\n\t\treturn nil\n\t}\n\n\tcfg := map[string]any{\"provider\": providers}\n\tdata, _ := json.Marshal(cfg)\n\treturn map[string]string{\"OPENCODE_CONFIG_CONTENT\": string(data)}\n}\n"}, "42fb3785c758777662f9e9d8559832eba3d19659": {"t": 1785754920.6247306, "n": 4167, "h": "c7b77322431d0947", "c": "// SPDX-License-Identifier: GPL-3.0-only\n\npackage agent\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"path/filepath\"\n)\n\n// OpenCode implements Agent for the OpenCode CLI (anomalyco/opencode).\ntype OpenCode struct{}\n\nfunc (o *OpenCode) Name() string { return \"opencode\" }\nfunc (o *OpenCode) Image() string { return \"ghcr.io/89luca89/clampdown-opencode:latest\" }\n\nfunc (o *OpenCode) EgressDomains() []string {\n\treturn []string{\n\t\t// OpenCode infrastructure\n\t\t\"opencode.ai\",\n\t\t\"models.dev\",\n\t\t\"mcp.exa.ai\",\n\t\t\"registry.npmjs.org\",\n\t\t// AI provider APIs (multi-provider agent)\n\t\t\"api.anthropic.com\",\n\t\t\"api.openai.com\",\n\t\t\"generativelanguage.googleapis.com\",\n\t\t\"api.groq.com\",\n\t\t\"api.deepseek.com\",\n\t\t\"api.mistral.ai\",\n\t\t\"api.x.ai\",\n\t\t\"openrouter.ai\",\n\t}\n}\n\nfunc (o *OpenCode) Mounts() []Mount { return nil }\n\nfunc (o *OpenCode) ConfigOverlays() []Mount {\n\tcfgDir := filepath.Join(Home, \".config\", \"opencode\")\n\treturn []Mount{\n\t\t{Src: filepath.Join(cfgDir, \"opencode.json\"), Dst: filepath.Join(cfgDir, \"opencode.json\")},\n\t\t{Src: filepath.Join(cfgDir, \"opencode.jsonc\"), Dst: filepath.Join(cfgDir, \"opencode.jsonc\")},\n\t}\n}\n\n// Env redirects TMPDIR because Bun-compiled binaries extract and dlopen native\n// .so files at startup. Default /tmp is mounted noexec, causing a silent hang.\nfunc (o *OpenCode) Env() map[string]string {\n\treturn map[string]string{\n\t\t\"TMPDIR\": filepath.Join(Home, \".config\", \"opencode\", \"tmp\"),\n\t}\n}\n\nfunc (o *OpenCode) Args(passthrough []string) []string {\n\treturn passthrough\n}\n\n// PromptFile returns ~/.config/opencode/AGENTS.md — OpenCode\n// auto-discovers AGENTS.md from ~/.config/opencode/ as global rules.\nfunc (o *OpenCode) PromptFile() string {\n\treturn filepath.Join(Home, \".config\", \"opencode\", \"AGENTS.md\")\n}\n\n// ProxyRoutes returns upstream API routes for all supported providers.\nfunc (o *OpenCode) ProxyRoutes() []ProxyRoute {\n\treturn []ProxyRoute{\n\t\t{Port: ProxyPort, Upstream: \"https://api.anthropic.com/v1\",\n\t\t\tKeyEnv: \"ANTHROPIC_API_KEY\", HeaderName: \"x-api-key\",\n\t\t\tBaseURLEnv: \"ANTHROPIC_BASE_URL\"},\n\t\t{Port: ProxyPort, Upstream: \"https://api.openai.com/v1\",\n\t\t\tKeyEnv: \"OPENAI_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tBaseURLEnv: \"OPENAI_BASE_URL\"},\n\t\t{Port: ProxyPort, Upstream: \"https://generativelanguage.googleapis.com\",\n\t\t\tKeyEnv: \"GOOGLE_GENERATIVE_AI_API_KEY\", KeyEnvFallback: \"GEMINI_API_KEY\",\n\t\t\tHeaderName: \"x-goog-api-key\",\n\t\t\tProviderID: \"google\"},\n\t\t{Port: ProxyPort, Upstream: \"https://api.groq.com/openai/v1\",\n\t\t\tKeyEnv: \"GROQ_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"groq\"},\n\t\t{Port: ProxyPort, Upstream: \"https://api.deepseek.com/v1\",\n\t\t\tKeyEnv: \"DEEPSEEK_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"deepseek\"},\n\t\t{Port: ProxyPort, Upstream: \"https://api.mistral.ai/v1\",\n\t\t\tKeyEnv: \"MISTRAL_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"mistral\"},\n\t\t{Port: ProxyPort, Upstream: \"https://api.x.ai/v1\",\n\t\t\tKeyEnv: \"XAI_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"xai\"},\n\t\t{Port: ProxyPort, Upstream: \"https://openrouter.ai/api/v1\",\n\t\t\tKeyEnv: \"OPENROUTER_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"openrouter\"},\n\t\t{Port: ProxyPort, Upstream: \"https://opencode.ai/zen/v1\",\n\t\t\tKeyEnv: \"OPENCODE_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"opencode\"},\n\t}\n}\n\n// ProxyEnvOverride builds OPENCODE_CONFIG_CONTENT for providers whose SDK\n// doesn't read a *_BASE_URL env var. The JSON is deep-merged by OpenCode\n// at highest precedence — no clobbering of user config.\nfunc (o *OpenCode) ProxyEnvOverride(routes []ProxyRoute) map[string]string {\n\tproviders := make(map[string]any)\n\tfor _, r := range routes {\n\t\tif r.ProviderID == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tproviders[r.ProviderID] = map[string]any{\n\t\t\t\"options\": map[string]any{\n\t\t\t\t\"baseURL\": fmt.Sprintf(\"http://localhost:%d\", r.Port),\n\t\t\t},\n\t\t}\n\t}\n\tif len(providers) == 0 {\n\t\treturn nil\n\t}\n\n\tcfg := map[string]any{\"provider\": providers}\n\tdata, _ := json.Marshal(cfg)\n\treturn map[string]string{\"OPENCODE_CONFIG_CONTENT\": string(data)}\n}\n"}, "5ce7f10c47712d0495f8abf58b8534a4598b9725": {"t": 1785754922.0535502, "n": 4150, "h": "9c22cb7cb6bc756f", "c": "// SPDX-License-Identifier: GPL-3.0-only\n\npackage agent\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"path/filepath\"\n)\n\n// OpenCode implements Agent for the OpenCode CLI (anomalyco/opencode).\ntype OpenCode struct{}\n\nfunc (o *OpenCode) Name() string { return \"opencode\" }\nfunc (o *OpenCode) Image() string { return \"clampdown-opencode:latest\" }\n\nfunc (o *OpenCode) EgressDomains() []string {\n\treturn []string{\n\t\t// OpenCode infrastructure\n\t\t\"opencode.ai\",\n\t\t\"models.dev\",\n\t\t\"mcp.exa.ai\",\n\t\t\"registry.npmjs.org\",\n\t\t// AI provider APIs (multi-provider agent)\n\t\t\"api.anthropic.com\",\n\t\t\"api.openai.com\",\n\t\t\"generativelanguage.googleapis.com\",\n\t\t\"api.groq.com\",\n\t\t\"api.deepseek.com\",\n\t\t\"api.mistral.ai\",\n\t\t\"api.x.ai\",\n\t\t\"openrouter.ai\",\n\t}\n}\n\nfunc (o *OpenCode) Mounts() []Mount { return nil }\n\nfunc (o *OpenCode) ConfigOverlays() []Mount {\n\tcfgDir := filepath.Join(Home, \".config\", \"opencode\")\n\treturn []Mount{\n\t\t{Src: filepath.Join(cfgDir, \"opencode.json\"), Dst: filepath.Join(cfgDir, \"opencode.json\")},\n\t\t{Src: filepath.Join(cfgDir, \"opencode.jsonc\"), Dst: filepath.Join(cfgDir, \"opencode.jsonc\")},\n\t}\n}\n\n// Env redirects TMPDIR because Bun-compiled binaries extract and dlopen native\n// .so files at startup. Default /tmp is mounted noexec, causing a silent hang.\nfunc (o *OpenCode) Env() map[string]string {\n\treturn map[string]string{\n\t\t\"TMPDIR\": filepath.Join(Home, \".config\", \"opencode\", \"tmp\"),\n\t}\n}\n\nfunc (o *OpenCode) Args(passthrough []string) []string {\n\treturn passthrough\n}\n\n// PromptFile returns ~/.config/opencode/AGENTS.md — OpenCode\n// auto-discovers AGENTS.md from ~/.config/opencode/ as global rules.\nfunc (o *OpenCode) PromptFile() string {\n\treturn filepath.Join(Home, \".config\", \"opencode\", \"AGENTS.md\")\n}\n\n// ProxyRoutes returns upstream API routes for all supported providers.\nfunc (o *OpenCode) ProxyRoutes() []ProxyRoute {\n\treturn []ProxyRoute{\n\t\t{Port: ProxyPort, Upstream: \"https://api.anthropic.com/v1\",\n\t\t\tKeyEnv: \"ANTHROPIC_API_KEY\", HeaderName: \"x-api-key\",\n\t\t\tBaseURLEnv: \"ANTHROPIC_BASE_URL\"},\n\t\t{Port: ProxyPort, Upstream: \"https://api.openai.com/v1\",\n\t\t\tKeyEnv: \"OPENAI_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tBaseURLEnv: \"OPENAI_BASE_URL\"},\n\t\t{Port: ProxyPort, Upstream: \"https://generativelanguage.googleapis.com\",\n\t\t\tKeyEnv: \"GOOGLE_GENERATIVE_AI_API_KEY\", KeyEnvFallback: \"GEMINI_API_KEY\",\n\t\t\tHeaderName: \"x-goog-api-key\",\n\t\t\tProviderID: \"google\"},\n\t\t{Port: ProxyPort, Upstream: \"https://api.groq.com/openai/v1\",\n\t\t\tKeyEnv: \"GROQ_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"groq\"},\n\t\t{Port: ProxyPort, Upstream: \"https://api.deepseek.com/v1\",\n\t\t\tKeyEnv: \"DEEPSEEK_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"deepseek\"},\n\t\t{Port: ProxyPort, Upstream: \"https://api.mistral.ai/v1\",\n\t\t\tKeyEnv: \"MISTRAL_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"mistral\"},\n\t\t{Port: ProxyPort, Upstream: \"https://api.x.ai/v1\",\n\t\t\tKeyEnv: \"XAI_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"xai\"},\n\t\t{Port: ProxyPort, Upstream: \"https://openrouter.ai/api/v1\",\n\t\t\tKeyEnv: \"OPENROUTER_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"openrouter\"},\n\t\t{Port: ProxyPort, Upstream: \"https://opencode.ai/zen/v1\",\n\t\t\tKeyEnv: \"OPENCODE_API_KEY\", HeaderName: \"Authorization\", HeaderPrefix: \"Bearer \",\n\t\t\tProviderID: \"opencode\"},\n\t}\n}\n\n// ProxyEnvOverride builds OPENCODE_CONFIG_CONTENT for providers whose SDK\n// doesn't read a *_BASE_URL env var. The JSON is deep-merged by OpenCode\n// at highest precedence — no clobbering of user config.\nfunc (o *OpenCode) ProxyEnvOverride(routes []ProxyRoute) map[string]string {\n\tproviders := make(map[string]any)\n\tfor _, r := range routes {\n\t\tif r.ProviderID == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tproviders[r.ProviderID] = map[string]any{\n\t\t\t\"options\": map[string]any{\n\t\t\t\t\"baseURL\": fmt.Sprintf(\"http://localhost:%d\", r.Port),\n\t\t\t},\n\t\t}\n\t}\n\tif len(providers) == 0 {\n\t\treturn nil\n\t}\n\n\tcfg := map[string]any{\"provider\": providers}\n\tdata, _ := json.Marshal(cfg)\n\treturn map[string]string{\"OPENCODE_CONFIG_CONTENT\": string(data)}\n}\n"}, "301afed54cf0bce0d588a6490021cf7345d9ec79": {"t": 1785754924.4202585, "n": 2056, "h": "38da91d9eccdb231", "c": "// SPDX-License-Identifier: GPL-3.0-only\n\npackage agent\n\nimport \"path/filepath\"\n\n// OpenCode implements Agent for the OpenCode CLI (anomalyco/opencode).\ntype OpenCode struct{}\n\nfunc (o *OpenCode) Name() string { return \"opencode\" }\nfunc (o *OpenCode) Image() string { return \"clampdown-opencode:latest\" }\n\nfunc (o *OpenCode) EgressDomains() []string {\n\treturn []string{\n\t\t// OpenCode infrastructure\n\t\t\"opencode.ai\",\n\t\t\"models.dev\",\n\t\t\"mcp.exa.ai\",\n\t\t\"registry.npmjs.org\",\n\t\t// AI provider APIs (multi-provider agent)\n\t\t\"api.anthropic.com\",\n\t\t\"api.openai.com\",\n\t\t\"generativelanguage.googleapis.com\",\n\t\t\"api.groq.com\",\n\t\t\"api.deepseek.com\",\n\t\t\"api.mistral.ai\",\n\t\t\"api.x.ai\",\n\t\t\"openrouter.ai\",\n\t}\n}\n\nfunc (o *OpenCode) Mounts() []Mount { return nil }\n\nfunc (o *OpenCode) ConfigOverlays() []Mount {\n\tcfgDir := filepath.Join(Home, \".config\", \"opencode\")\n\treturn []Mount{\n\t\t{Src: filepath.Join(cfgDir, \"opencode.json\"), Dst: filepath.Join(cfgDir, \"opencode.json\")},\n\t\t{Src: filepath.Join(cfgDir, \"opencode.jsonc\"), Dst: filepath.Join(cfgDir, \"opencode.jsonc\")},\n\t}\n}\n\n// Env redirects TMPDIR because Bun-compiled binaries extract and dlopen native\n// .so files at startup. Default /tmp is mounted noexec, causing a silent hang.\nfunc (o *OpenCode) Env() map[string]string {\n\treturn map[string]string{\n\t\t\"TMPDIR\": filepath.Join(Home, \".config\", \"opencode\", \"tmp\"),\n\t}\n}\n\nfunc (o *OpenCode) Args(passthrough []string) []string {\n\treturn passthrough\n}\n\n// PromptFile returns ~/.config/opencode/AGENTS.md — OpenCode\n// auto-discovers AGENTS.md from ~/.config/opencode/ as global rules.\nfunc (o *OpenCode) PromptFile() string {\n\treturn filepath.Join(Home, \".config\", \"opencode\", \"AGENTS.md\")\n}\n\nfunc (o *OpenCode) ForwardEnv() []string {\n\t// OpenCode is multi-provider. Forward all common provider keys —\n\t// only those set on the host are passed through.\n\treturn []string{\n\t\t\"ANTHROPIC_API_KEY\",\n\t\t\"OPENAI_API_KEY\",\n\t\t\"GEMINI_API_KEY\",\n\t\t\"OPENROUTER_API_KEY\",\n\t\t\"GROQ_API_KEY\",\n\t\t\"DEEPSEEK_API_KEY\",\n\t\t\"MISTRAL_API_KEY\",\n\t\t\"XAI_API_KEY\",\n\t\t\"OPENCODE_API_KEY\",\n\t}\n}\n"}}}}