claude-opencode-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/LICENSE +21 -0
  3. package/README.md +514 -0
  4. package/SECURITY.md +80 -0
  5. package/agents/deepseek-coder.md +32 -0
  6. package/agents/deepseek-researcher.md +36 -0
  7. package/agents/deepseek-reviewer.md +31 -0
  8. package/agents/deepseek-tester.md +31 -0
  9. package/dist/cli/init.d.ts +6 -0
  10. package/dist/cli/init.js +208 -0
  11. package/dist/cli/init.js.map +1 -0
  12. package/dist/config/config.d.ts +109 -0
  13. package/dist/config/config.js +163 -0
  14. package/dist/config/config.js.map +1 -0
  15. package/dist/errors.d.ts +29 -0
  16. package/dist/errors.js +68 -0
  17. package/dist/errors.js.map +1 -0
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.js +116 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/mcp/execute.d.ts +30 -0
  22. package/dist/mcp/execute.js +128 -0
  23. package/dist/mcp/execute.js.map +1 -0
  24. package/dist/mcp/server.d.ts +19 -0
  25. package/dist/mcp/server.js +38 -0
  26. package/dist/mcp/server.js.map +1 -0
  27. package/dist/mcp/session-utils.d.ts +5 -0
  28. package/dist/mcp/session-utils.js +55 -0
  29. package/dist/mcp/session-utils.js.map +1 -0
  30. package/dist/mcp/tool-utils.d.ts +11 -0
  31. package/dist/mcp/tool-utils.js +44 -0
  32. package/dist/mcp/tool-utils.js.map +1 -0
  33. package/dist/mcp/tools/abort-session.d.ts +3 -0
  34. package/dist/mcp/tools/abort-session.js +48 -0
  35. package/dist/mcp/tools/abort-session.js.map +1 -0
  36. package/dist/mcp/tools/create-session.d.ts +3 -0
  37. package/dist/mcp/tools/create-session.js +53 -0
  38. package/dist/mcp/tools/create-session.js.map +1 -0
  39. package/dist/mcp/tools/delegate-task.d.ts +4 -0
  40. package/dist/mcp/tools/delegate-task.js +98 -0
  41. package/dist/mcp/tools/delegate-task.js.map +1 -0
  42. package/dist/mcp/tools/get-diff.d.ts +3 -0
  43. package/dist/mcp/tools/get-diff.js +97 -0
  44. package/dist/mcp/tools/get-diff.js.map +1 -0
  45. package/dist/mcp/tools/get-session.d.ts +3 -0
  46. package/dist/mcp/tools/get-session.js +79 -0
  47. package/dist/mcp/tools/get-session.js.map +1 -0
  48. package/dist/mcp/tools/list-agents.d.ts +3 -0
  49. package/dist/mcp/tools/list-agents.js +72 -0
  50. package/dist/mcp/tools/list-agents.js.map +1 -0
  51. package/dist/mcp/tools/send-message.d.ts +3 -0
  52. package/dist/mcp/tools/send-message.js +59 -0
  53. package/dist/mcp/tools/send-message.js.map +1 -0
  54. package/dist/opencode/agents.d.ts +46 -0
  55. package/dist/opencode/agents.js +239 -0
  56. package/dist/opencode/agents.js.map +1 -0
  57. package/dist/opencode/binary.d.ts +19 -0
  58. package/dist/opencode/binary.js +129 -0
  59. package/dist/opencode/binary.js.map +1 -0
  60. package/dist/opencode/client.d.ts +32 -0
  61. package/dist/opencode/client.js +55 -0
  62. package/dist/opencode/client.js.map +1 -0
  63. package/dist/opencode/manager.d.ts +66 -0
  64. package/dist/opencode/manager.js +492 -0
  65. package/dist/opencode/manager.js.map +1 -0
  66. package/dist/opencode/permission-watcher.d.ts +26 -0
  67. package/dist/opencode/permission-watcher.js +144 -0
  68. package/dist/opencode/permission-watcher.js.map +1 -0
  69. package/dist/opencode/run.d.ts +39 -0
  70. package/dist/opencode/run.js +318 -0
  71. package/dist/opencode/run.js.map +1 -0
  72. package/dist/opencode/sessions.d.ts +26 -0
  73. package/dist/opencode/sessions.js +105 -0
  74. package/dist/opencode/sessions.js.map +1 -0
  75. package/dist/opencode/setup.d.ts +38 -0
  76. package/dist/opencode/setup.js +109 -0
  77. package/dist/opencode/setup.js.map +1 -0
  78. package/dist/security/paths.d.ts +20 -0
  79. package/dist/security/paths.js +93 -0
  80. package/dist/security/paths.js.map +1 -0
  81. package/dist/security/policy.d.ts +17 -0
  82. package/dist/security/policy.js +86 -0
  83. package/dist/security/policy.js.map +1 -0
  84. package/dist/types/index.d.ts +131 -0
  85. package/dist/types/index.js +2 -0
  86. package/dist/types/index.js.map +1 -0
  87. package/dist/util/fs.d.ts +12 -0
  88. package/dist/util/fs.js +73 -0
  89. package/dist/util/fs.js.map +1 -0
  90. package/dist/util/git.d.ts +54 -0
  91. package/dist/util/git.js +202 -0
  92. package/dist/util/git.js.map +1 -0
  93. package/dist/util/logger.d.ts +21 -0
  94. package/dist/util/logger.js +129 -0
  95. package/dist/util/logger.js.map +1 -0
  96. package/dist/util/text.d.ts +11 -0
  97. package/dist/util/text.js +126 -0
  98. package/dist/util/text.js.map +1 -0
  99. package/dist/workspace/context.d.ts +12 -0
  100. package/dist/workspace/context.js +67 -0
  101. package/dist/workspace/context.js.map +1 -0
  102. package/dist/workspace/resolver.d.ts +35 -0
  103. package/dist/workspace/resolver.js +112 -0
  104. package/dist/workspace/resolver.js.map +1 -0
  105. package/dist/workspace/validator.d.ts +13 -0
  106. package/dist/workspace/validator.js +102 -0
  107. package/dist/workspace/validator.js.map +1 -0
  108. package/docs/agent-configuration.md +100 -0
  109. package/docs/architecture.md +115 -0
  110. package/docs/claude-code-setup.md +116 -0
  111. package/docs/configuration.md +184 -0
  112. package/docs/contributing.md +71 -0
  113. package/docs/deepseek-setup.md +84 -0
  114. package/docs/development.md +146 -0
  115. package/docs/installation.md +88 -0
  116. package/docs/opencode-setup.md +97 -0
  117. package/docs/security-audit.md +80 -0
  118. package/docs/security.md +128 -0
  119. package/docs/troubleshooting.md +126 -0
  120. package/docs/workspace-model.md +126 -0
  121. package/package.json +79 -0
@@ -0,0 +1,239 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { parse as parseYaml } from "yaml";
5
+ import { BridgeError } from "../errors.js";
6
+ import { buildPermissions, PROFILE_CAPABILITIES, profileForAgent, } from "../security/policy.js";
7
+ export const BUILTIN_AGENT_NAMES = [
8
+ "deepseek-researcher",
9
+ "deepseek-reviewer",
10
+ "deepseek-coder",
11
+ "deepseek-tester",
12
+ ];
13
+ const PROFILE_BY_NAME = {
14
+ "deepseek-researcher": "read",
15
+ "deepseek-reviewer": "review",
16
+ "deepseek-coder": "code",
17
+ "deepseek-tester": "test",
18
+ };
19
+ export function packageRoot() {
20
+ const here = path.dirname(fileURLToPath(import.meta.url));
21
+ // src/opencode/ -> package root; dist/opencode/ -> package root
22
+ return path.resolve(here, "..", "..");
23
+ }
24
+ export function builtinAgentsDir() {
25
+ return path.join(packageRoot(), "agents");
26
+ }
27
+ export function parseAgentMarkdown(contents, file) {
28
+ const normalized = contents.replace(/\r\n/g, "\n");
29
+ if (!normalized.startsWith("---\n")) {
30
+ return { frontmatter: {}, body: normalized.trim(), file };
31
+ }
32
+ const end = normalized.indexOf("\n---", 4);
33
+ if (end === -1) {
34
+ return { frontmatter: {}, body: normalized.trim(), file };
35
+ }
36
+ const header = normalized.slice(4, end);
37
+ const body = normalized
38
+ .slice(end + 4)
39
+ .replace(/^\n+/, "")
40
+ .trim();
41
+ let frontmatter = {};
42
+ try {
43
+ const parsed = parseYaml(header);
44
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
45
+ frontmatter = parsed;
46
+ }
47
+ }
48
+ catch (error) {
49
+ throw new BridgeError("CONFIG_INVALID", `Invalid agent frontmatter in ${file}`, {
50
+ details: { file, reason: error instanceof Error ? error.message : String(error) },
51
+ cause: error,
52
+ });
53
+ }
54
+ return { frontmatter, body, file };
55
+ }
56
+ function normalizeMode(value) {
57
+ return value === "primary" || value === "subagent" || value === "all" ? value : "all";
58
+ }
59
+ const VALID_PROFILES = new Set(["read", "review", "code", "test"]);
60
+ function frontmatterProfile(frontmatter) {
61
+ return typeof frontmatter.profile === "string" && VALID_PROFILES.has(frontmatter.profile)
62
+ ? frontmatter.profile
63
+ : undefined;
64
+ }
65
+ async function readAgentFile(file, name) {
66
+ const contents = await fs.promises.readFile(file, "utf8");
67
+ const { frontmatter, body } = parseAgentMarkdown(contents, file);
68
+ return {
69
+ name,
70
+ description: typeof frontmatter.description === "string" && frontmatter.description.trim()
71
+ ? frontmatter.description.trim()
72
+ : `Delegated agent ${name}`,
73
+ mode: normalizeMode(frontmatter.mode),
74
+ prompt: body,
75
+ permission: {},
76
+ model: typeof frontmatter.model === "string" ? frontmatter.model : null,
77
+ ...(typeof frontmatter.temperature === "number"
78
+ ? { temperature: frontmatter.temperature }
79
+ : {}),
80
+ source: "builtin",
81
+ };
82
+ }
83
+ export async function loadBuiltinAgentDefinitions() {
84
+ const dir = builtinAgentsDir();
85
+ const definitions = [];
86
+ for (const name of BUILTIN_AGENT_NAMES) {
87
+ const file = path.join(dir, `${name}.md`);
88
+ try {
89
+ const definition = await readAgentFile(file, name);
90
+ definitions.push(definition);
91
+ }
92
+ catch (error) {
93
+ if (error.code === "ENOENT") {
94
+ throw new BridgeError("CONFIG_INVALID", `Built-in agent definition is missing: ${file}. Reinstall claude-opencode-mcp.`, { details: { file }, cause: error });
95
+ }
96
+ throw error;
97
+ }
98
+ }
99
+ return definitions;
100
+ }
101
+ export async function loadProjectAgentDefinitions(cwd) {
102
+ const dir = path.join(cwd, ".claude-opencode", "agents");
103
+ let entries;
104
+ try {
105
+ entries = await fs.promises.readdir(dir);
106
+ }
107
+ catch {
108
+ return [];
109
+ }
110
+ const definitions = [];
111
+ for (const entry of entries) {
112
+ if (!entry.endsWith(".md"))
113
+ continue;
114
+ const name = entry.slice(0, -3);
115
+ const file = path.join(dir, entry);
116
+ const contents = await fs.promises.readFile(file, "utf8");
117
+ const { frontmatter, body } = parseAgentMarkdown(contents, file);
118
+ definitions.push({
119
+ name,
120
+ description: typeof frontmatter.description === "string" && frontmatter.description.trim()
121
+ ? frontmatter.description.trim()
122
+ : `Project agent ${name}`,
123
+ mode: normalizeMode(frontmatter.mode),
124
+ prompt: body,
125
+ permission: {},
126
+ model: typeof frontmatter.model === "string" ? frontmatter.model : null,
127
+ ...(typeof frontmatter.temperature === "number"
128
+ ? { temperature: frontmatter.temperature }
129
+ : {}),
130
+ ...(frontmatterProfile(frontmatter) ? { profile: frontmatterProfile(frontmatter) } : {}),
131
+ source: "project",
132
+ });
133
+ }
134
+ return definitions;
135
+ }
136
+ function profileForDefinition(definition) {
137
+ const byName = PROFILE_BY_NAME[definition.name];
138
+ if (byName)
139
+ return byName;
140
+ if (definition.profile)
141
+ return definition.profile;
142
+ if (/coder|editor|implement/i.test(definition.name))
143
+ return "code";
144
+ if (/test/i.test(definition.name))
145
+ return "test";
146
+ if (/review|audit/i.test(definition.name))
147
+ return "review";
148
+ return "read";
149
+ }
150
+ export function buildAgentDefinitions(input) {
151
+ const { builtin, project, config } = input;
152
+ const byName = new Map();
153
+ for (const definition of builtin)
154
+ byName.set(definition.name, { ...definition });
155
+ for (const definition of project) {
156
+ const existing = byName.get(definition.name);
157
+ if (existing) {
158
+ byName.set(definition.name, {
159
+ ...existing,
160
+ description: definition.description,
161
+ prompt: definition.prompt,
162
+ mode: definition.mode,
163
+ model: definition.model ?? existing.model,
164
+ ...(definition.temperature !== undefined ? { temperature: definition.temperature } : {}),
165
+ source: "project",
166
+ });
167
+ }
168
+ else {
169
+ byName.set(definition.name, { ...definition });
170
+ }
171
+ }
172
+ for (const [name, override] of Object.entries(config.agents)) {
173
+ if (override.enabled === false) {
174
+ byName.delete(name);
175
+ continue;
176
+ }
177
+ const existing = byName.get(name);
178
+ const base = existing ?? {
179
+ name,
180
+ description: override.description ?? `Delegated agent ${name}`,
181
+ mode: "all",
182
+ prompt: override.prompt ?? "You are a delegated software engineering agent.",
183
+ permission: {},
184
+ model: null,
185
+ source: "project",
186
+ };
187
+ byName.set(name, {
188
+ ...base,
189
+ ...(override.description !== undefined ? { description: override.description } : {}),
190
+ ...(override.prompt !== undefined ? { prompt: override.prompt } : {}),
191
+ ...(override.model !== undefined ? { model: override.model } : {}),
192
+ ...(override.temperature !== undefined ? { temperature: override.temperature } : {}),
193
+ ...(override.profile !== undefined ? { profile: override.profile } : {}),
194
+ });
195
+ }
196
+ return [...byName.values()];
197
+ }
198
+ export function agentProfile(definition) {
199
+ return definition.profile ?? profileForDefinition(definition);
200
+ }
201
+ export function agentCapabilities(definition) {
202
+ return PROFILE_CAPABILITIES[agentProfile(definition)];
203
+ }
204
+ export function agentPermissions(definition, config) {
205
+ return buildPermissions(agentProfile(definition), config.security);
206
+ }
207
+ export function toOpenCodeAgentConfig(definitions, config) {
208
+ const result = {};
209
+ for (const definition of definitions) {
210
+ const model = definition.model ?? config.defaults.model ?? null;
211
+ result[definition.name] = {
212
+ description: definition.description,
213
+ mode: definition.mode,
214
+ prompt: definition.prompt,
215
+ permission: agentPermissions(definition, config),
216
+ ...(model ? { model: normalizeModel(model, config) } : {}),
217
+ ...(definition.temperature !== undefined ? { temperature: definition.temperature } : {}),
218
+ };
219
+ }
220
+ return result;
221
+ }
222
+ export function normalizeModel(model, config) {
223
+ const trimmed = model.trim();
224
+ if (!trimmed)
225
+ return trimmed;
226
+ if (trimmed.includes("/"))
227
+ return trimmed;
228
+ return `${config.defaults.provider}/${trimmed}`;
229
+ }
230
+ export function findAgentDefinition(definitions, name) {
231
+ return definitions.find((definition) => definition.name === name) ?? null;
232
+ }
233
+ export function isBuiltinAgent(name) {
234
+ return BUILTIN_AGENT_NAMES.includes(name);
235
+ }
236
+ export function fallbackProfileForUnknownAgent(name) {
237
+ return profileForAgent(name) ?? PROFILE_BY_NAME[name] ?? null;
238
+ }
239
+ //# sourceMappingURL=agents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/opencode/agents.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ export interface ResolvedBinary {
2
+ command: string;
3
+ version: string;
4
+ source: string;
5
+ }
6
+ export interface ResolveBinaryOptions {
7
+ configured?: string | null;
8
+ env?: NodeJS.ProcessEnv;
9
+ }
10
+ export declare function resolveOpenCodeBinary(options?: ResolveBinaryOptions): Promise<ResolvedBinary>;
11
+ /** Cached binary resolution so we only probe the filesystem once per process. */
12
+ export declare class BinaryResolver {
13
+ private readonly options;
14
+ private cached;
15
+ private cachedKey;
16
+ constructor(options?: ResolveBinaryOptions);
17
+ resolve(configured?: string | null, force?: boolean): Promise<ResolvedBinary>;
18
+ invalidate(): void;
19
+ }
@@ -0,0 +1,129 @@
1
+ import { execFile } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import { BridgeError } from "../errors.js";
6
+ import { packageRoot } from "./agents.js";
7
+ function executableNames() {
8
+ return process.platform === "win32" ? ["opencode.cmd", "opencode.exe", "opencode"] : ["opencode"];
9
+ }
10
+ function candidatePaths(configured, env) {
11
+ const candidates = [];
12
+ const explicit = configured ?? env.OPENCODE_BIN ?? null;
13
+ if (explicit?.trim()) {
14
+ candidates.push({
15
+ command: explicit.trim(),
16
+ source: "config/env OPENCODE_BIN",
17
+ mustExist: true,
18
+ });
19
+ }
20
+ for (const name of executableNames()) {
21
+ candidates.push({ command: name, source: "PATH", mustExist: false });
22
+ }
23
+ // Walk up from the package root so hoisted installs are found too: npm often
24
+ // places `opencode-ai` in a parent node_modules/.bin when this package is a
25
+ // dependency rather than the root project.
26
+ let dir = packageRoot();
27
+ const seen = new Set();
28
+ while (dir && !seen.has(dir)) {
29
+ seen.add(dir);
30
+ for (const name of executableNames()) {
31
+ candidates.push({
32
+ command: path.join(dir, "node_modules", ".bin", name),
33
+ source: `node_modules/.bin (${dir})`,
34
+ mustExist: true,
35
+ });
36
+ }
37
+ const parent = path.dirname(dir);
38
+ if (parent === dir)
39
+ break;
40
+ dir = parent;
41
+ }
42
+ for (const name of executableNames()) {
43
+ candidates.push({
44
+ command: path.join(process.cwd(), "node_modules", ".bin", name),
45
+ source: "project node_modules/.bin",
46
+ mustExist: true,
47
+ });
48
+ }
49
+ for (const name of executableNames()) {
50
+ candidates.push({
51
+ command: path.join(os.homedir(), ".opencode", "bin", name),
52
+ source: "~/.opencode/bin",
53
+ mustExist: true,
54
+ });
55
+ candidates.push({
56
+ command: path.join(os.homedir(), ".local", "bin", name),
57
+ source: "~/.local/bin",
58
+ mustExist: true,
59
+ });
60
+ candidates.push({ command: `/opt/homebrew/bin/${name}`, source: "homebrew", mustExist: true });
61
+ candidates.push({ command: `/usr/local/bin/${name}`, source: "usr-local", mustExist: true });
62
+ }
63
+ return candidates;
64
+ }
65
+ function probeVersion(command) {
66
+ return new Promise((resolve) => {
67
+ execFile(command, ["--version"], { timeout: 20_000, windowsHide: true }, (error, stdout, stderr) => {
68
+ if (error) {
69
+ resolve(null);
70
+ return;
71
+ }
72
+ const output = `${stdout}\n${stderr}`.trim();
73
+ const firstLine = output
74
+ .split("\n")
75
+ .map((line) => line.trim())
76
+ .filter(Boolean)[0];
77
+ resolve(firstLine ?? "unknown");
78
+ });
79
+ });
80
+ }
81
+ export async function resolveOpenCodeBinary(options = {}) {
82
+ const env = options.env ?? process.env;
83
+ const candidates = candidatePaths(options.configured, env);
84
+ const failures = [];
85
+ for (const candidate of candidates) {
86
+ if (candidate.mustExist) {
87
+ try {
88
+ await fs.promises.access(candidate.command, fs.constants.X_OK);
89
+ }
90
+ catch {
91
+ failures.push(`${candidate.command} (${candidate.source}): not found`);
92
+ continue;
93
+ }
94
+ }
95
+ const version = await probeVersion(candidate.command);
96
+ if (version) {
97
+ return { command: candidate.command, version, source: candidate.source };
98
+ }
99
+ failures.push(`${candidate.command} (${candidate.source}): not executable`);
100
+ }
101
+ throw new BridgeError("OPENCODE_NOT_AVAILABLE", [
102
+ "OpenCode executable was not found.",
103
+ "Install it with `npm install -g opencode-ai` (or `brew install sst/tap/opencode`),",
104
+ "or point the bridge at it with the OPENCODE_BIN environment variable",
105
+ "or the opencode.binary configuration option.",
106
+ ].join(" "), { details: { candidates: failures }, retryable: false });
107
+ }
108
+ /** Cached binary resolution so we only probe the filesystem once per process. */
109
+ export class BinaryResolver {
110
+ options;
111
+ cached = null;
112
+ cachedKey = null;
113
+ constructor(options = {}) {
114
+ this.options = options;
115
+ }
116
+ async resolve(configured, force = false) {
117
+ const key = configured ?? this.options.configured ?? null;
118
+ if (this.cached && !force && this.cachedKey === key)
119
+ return this.cached;
120
+ this.cached = await resolveOpenCodeBinary({ ...this.options, configured: key });
121
+ this.cachedKey = key;
122
+ return this.cached;
123
+ }
124
+ invalidate() {
125
+ this.cached = null;
126
+ this.cachedKey = null;
127
+ }
128
+ }
129
+ //# sourceMappingURL=binary.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binary.js","sourceRoot":"","sources":["../../src/opencode/binary.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAa1C,SAAS,eAAe;IACtB,OAAO,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,cAAc,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;AACpG,CAAC;AAED,SAAS,cAAc,CAAC,UAAqC,EAAE,GAAsB;IACnF,MAAM,UAAU,GAAmE,EAAE,CAAC;IACtF,MAAM,QAAQ,GAAG,UAAU,IAAI,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC;IACxD,IAAI,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;QACrB,UAAU,CAAC,IAAI,CAAC;YACd,OAAO,EAAE,QAAQ,CAAC,IAAI,EAAE;YACxB,MAAM,EAAE,yBAAyB;YACjC,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;IACL,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,EAAE,CAAC;QACrC,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;IACvE,CAAC;IACD,6EAA6E;IAC7E,4EAA4E;IAC5E,2CAA2C;IAC3C,IAAI,GAAG,GAAG,WAAW,EAAE,CAAC;IACxB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,EAAE,CAAC;YACrC,UAAU,CAAC,IAAI,CAAC;gBACd,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,IAAI,CAAC;gBACrD,MAAM,EAAE,sBAAsB,GAAG,GAAG;gBACpC,SAAS,EAAE,IAAI;aAChB,CAAC,CAAC;QACL,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM;QAC1B,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,EAAE,CAAC;QACrC,UAAU,CAAC,IAAI,CAAC;YACd,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,IAAI,CAAC;YAC/D,MAAM,EAAE,2BAA2B;YACnC,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;IACL,CAAC;IACD,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,EAAE,CAAC;QACrC,UAAU,CAAC,IAAI,CAAC;YACd,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC;YAC1D,MAAM,EAAE,iBAAiB;YACzB,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;QACH,UAAU,CAAC,IAAI,CAAC;YACd,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC;YACvD,MAAM,EAAE,cAAc;YACtB,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;QACH,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,qBAAqB,IAAI,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/F,UAAU,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,kBAAkB,IAAI,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/F,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,YAAY,CAAC,OAAe;IACnC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,QAAQ,CACN,OAAO,EACP,CAAC,WAAW,CAAC,EACb,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,EACtC,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;YACxB,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,CAAC,IAAI,CAAC,CAAC;gBACd,OAAO;YACT,CAAC;YACD,MAAM,MAAM,GAAG,GAAG,MAAM,KAAK,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC;YAC7C,MAAM,SAAS,GAAG,MAAM;iBACrB,KAAK,CAAC,IAAI,CAAC;iBACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;iBAC1B,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;YACtB,OAAO,CAAC,SAAS,IAAI,SAAS,CAAC,CAAC;QAClC,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,UAAgC,EAAE;IAElC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACvC,MAAM,UAAU,GAAG,cAAc,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IAC3D,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,IAAI,SAAS,CAAC,SAAS,EAAE,CAAC;YACxB,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACjE,CAAC;YAAC,MAAM,CAAC;gBACP,QAAQ,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,OAAO,KAAK,SAAS,CAAC,MAAM,cAAc,CAAC,CAAC;gBACvE,SAAS;YACX,CAAC;QACH,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACtD,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,EAAE,OAAO,EAAE,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,CAAC;QAC3E,CAAC;QACD,QAAQ,CAAC,IAAI,CAAC,GAAG,SAAS,CAAC,OAAO,KAAK,SAAS,CAAC,MAAM,mBAAmB,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,IAAI,WAAW,CACnB,wBAAwB,EACxB;QACE,oCAAoC;QACpC,oFAAoF;QACpF,sEAAsE;QACtE,8CAA8C;KAC/C,CAAC,IAAI,CAAC,GAAG,CAAC,EACX,EAAE,OAAO,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,CACxD,CAAC;AACJ,CAAC;AAED,iFAAiF;AACjF,MAAM,OAAO,cAAc;IAII;IAHrB,MAAM,GAA0B,IAAI,CAAC;IACrC,SAAS,GAAkB,IAAI,CAAC;IAExC,YAA6B,UAAgC,EAAE;QAAlC,YAAO,GAAP,OAAO,CAA2B;IAAG,CAAC;IAEnE,KAAK,CAAC,OAAO,CAAC,UAA0B,EAAE,KAAK,GAAG,KAAK;QACrD,MAAM,GAAG,GAAG,UAAU,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC;QAC1D,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC,MAAM,CAAC;QACxE,IAAI,CAAC,MAAM,GAAG,MAAM,qBAAqB,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;QAChF,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;QACrB,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,UAAU;QACR,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;IACxB,CAAC;CACF"}
@@ -0,0 +1,32 @@
1
+ import { type OpencodeClient } from "@opencode-ai/sdk";
2
+ export interface AuthCredentials {
3
+ username: string;
4
+ password: string;
5
+ }
6
+ export declare function basicAuthHeader(credentials: AuthCredentials): string;
7
+ export interface WorkspaceClientOptions {
8
+ baseUrl: string;
9
+ directory?: string;
10
+ auth?: AuthCredentials | null;
11
+ }
12
+ /**
13
+ * Build an OpenCode SDK client bound to a workspace directory.
14
+ *
15
+ * `directory` is sent as the `x-opencode-directory` header on every request
16
+ * (and as a query parameter on GET/HEAD) so the server routes the request to
17
+ * the correct project instance.
18
+ */
19
+ export declare function createWorkspaceClient(options: WorkspaceClientOptions): OpencodeClient;
20
+ export declare function normalizeBaseUrl(url: string): string;
21
+ export interface HealthResult {
22
+ healthy: boolean;
23
+ version: string;
24
+ }
25
+ /**
26
+ * Check `/global/health` directly.
27
+ *
28
+ * The v1 client bundled with @opencode-ai/sdk 1.x does not expose a health
29
+ * method (only the v2 namespace does), but the endpoint exists on every
30
+ * 1.x server, so the bridge talks to it over plain HTTP.
31
+ */
32
+ export declare function fetchHealth(baseUrl: string, auth?: AuthCredentials | null): Promise<HealthResult | null>;
@@ -0,0 +1,55 @@
1
+ import { createOpencodeClient } from "@opencode-ai/sdk";
2
+ export function basicAuthHeader(credentials) {
3
+ const raw = `${credentials.username}:${credentials.password}`;
4
+ return `Basic ${Buffer.from(raw, "utf8").toString("base64")}`;
5
+ }
6
+ /**
7
+ * Build an OpenCode SDK client bound to a workspace directory.
8
+ *
9
+ * `directory` is sent as the `x-opencode-directory` header on every request
10
+ * (and as a query parameter on GET/HEAD) so the server routes the request to
11
+ * the correct project instance.
12
+ */
13
+ export function createWorkspaceClient(options) {
14
+ const headers = {};
15
+ if (options.auth)
16
+ headers.Authorization = basicAuthHeader(options.auth);
17
+ const config = {
18
+ baseUrl: options.baseUrl,
19
+ headers,
20
+ };
21
+ if (options.directory)
22
+ config.directory = options.directory;
23
+ return createOpencodeClient(config);
24
+ }
25
+ export function normalizeBaseUrl(url) {
26
+ return url.endsWith("/") ? url.slice(0, -1) : url;
27
+ }
28
+ /**
29
+ * Check `/global/health` directly.
30
+ *
31
+ * The v1 client bundled with @opencode-ai/sdk 1.x does not expose a health
32
+ * method (only the v2 namespace does), but the endpoint exists on every
33
+ * 1.x server, so the bridge talks to it over plain HTTP.
34
+ */
35
+ export async function fetchHealth(baseUrl, auth) {
36
+ const headers = { accept: "application/json" };
37
+ if (auth)
38
+ headers.Authorization = basicAuthHeader(auth);
39
+ try {
40
+ const response = await fetch(`${normalizeBaseUrl(baseUrl)}/global/health`, {
41
+ headers,
42
+ signal: AbortSignal.timeout(10_000),
43
+ });
44
+ if (!response.ok)
45
+ return null;
46
+ const body = (await response.json());
47
+ if (body?.healthy !== true)
48
+ return null;
49
+ return { healthy: true, version: body.version ?? "unknown" };
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }
55
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/opencode/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAuB,MAAM,kBAAkB,CAAC;AAO7E,MAAM,UAAU,eAAe,CAAC,WAA4B;IAC1D,MAAM,GAAG,GAAG,GAAG,WAAW,CAAC,QAAQ,IAAI,WAAW,CAAC,QAAQ,EAAE,CAAC;IAC9D,OAAO,SAAS,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;AAChE,CAAC;AAQD;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAA+B;IACnE,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,IAAI,OAAO,CAAC,IAAI;QAAE,OAAO,CAAC,aAAa,GAAG,eAAe,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAExE,MAAM,MAAM,GAA+C;QACzD,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,OAAO;KACR,CAAC;IACF,IAAI,OAAO,CAAC,SAAS;QAAE,MAAM,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IAC5D,OAAO,oBAAoB,CAAC,MAAM,CAAC,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC1C,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AACpD,CAAC;AAOD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,OAAe,EACf,IAA6B;IAE7B,MAAM,OAAO,GAA2B,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IACvE,IAAI,IAAI;QAAE,OAAO,CAAC,aAAa,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;IACxD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,gBAAgB,CAAC,OAAO,CAAC,gBAAgB,EAAE;YACzE,OAAO;YACP,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC;SACpC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAC9B,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA4C,CAAC;QAChF,IAAI,IAAI,EAAE,OAAO,KAAK,IAAI;YAAE,OAAO,IAAI,CAAC;QACxC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,SAAS,EAAE,CAAC;IAC/D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,66 @@
1
+ import type { OpencodeClient } from "@opencode-ai/sdk";
2
+ import { type ResolvedConfig } from "../config/config.js";
3
+ import type { Logger } from "../util/logger.js";
4
+ import { type ResolveBinaryOptions } from "./binary.js";
5
+ import { type AuthCredentials } from "./client.js";
6
+ export interface ServerStatus {
7
+ key: string;
8
+ url: string;
9
+ cwd: string;
10
+ external: boolean;
11
+ version: string | null;
12
+ activeRuns: number;
13
+ lastUsedAt: number;
14
+ }
15
+ export interface EnsureServerInput {
16
+ cwd: string;
17
+ config: ResolvedConfig;
18
+ agentConfig: Record<string, unknown>;
19
+ }
20
+ export interface OpenCodeManagerOptions {
21
+ logger: Logger;
22
+ env?: NodeJS.ProcessEnv;
23
+ binaryOptions?: ResolveBinaryOptions;
24
+ }
25
+ export declare class OpenCodeManager {
26
+ private readonly servers;
27
+ /** Every spawned child, tracked from creation so shutdown can always kill it. */
28
+ private readonly children;
29
+ private readonly logger;
30
+ private readonly env;
31
+ private readonly resolver;
32
+ private stopping;
33
+ constructor(options: OpenCodeManagerOptions);
34
+ resolveAuth(config: ResolvedConfig): AuthCredentials | null;
35
+ /** Ensure an OpenCode server exists for the workspace and return its record key. */
36
+ ensure(input: EnsureServerInput): Promise<string>;
37
+ clientFor(input: EnsureServerInput): Promise<OpencodeClient>;
38
+ /**
39
+ * Return a client for a workspace only when a live server already serves it.
40
+ * Used by read-only tools (get_session, get_diff, abort_session) that must
41
+ * not start an OpenCode process as a side effect.
42
+ */
43
+ existingClient(cwd: string): OpencodeClient | null;
44
+ getExistingServerVersion(cwd: string): string | null;
45
+ private externalKey;
46
+ private ensureExternal;
47
+ private assertAgentsAvailable;
48
+ private ensureLocal;
49
+ private spawnServer;
50
+ private tryHealth;
51
+ private waitForHealth;
52
+ beginRun(key: string, sessionId?: string): void;
53
+ endRun(key: string, sessionId?: string): void;
54
+ isDead(key: string): boolean;
55
+ getStatus(): ServerStatus[];
56
+ getRunningServerKeys(): string[];
57
+ private evictIfNeeded;
58
+ stop(key: string): Promise<void>;
59
+ stopAll(): Promise<void>;
60
+ /**
61
+ * Synchronous last-resort cleanup for `process.on("exit")`: kills any child
62
+ * that was spawned but not yet registered (shutdown races) so no OpenCode
63
+ * process is orphaned when the bridge exits.
64
+ */
65
+ killAllSync(): void;
66
+ }