fix(opencode): refresh bridge MCP env per command
This commit is contained in:
parent
48826af00b
commit
f3f38dd0e9
4 changed files with 73 additions and 3 deletions
|
|
@ -1,5 +1,5 @@
|
|||
#!/usr/bin/env node
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { pathToFileURL } from 'url';
|
||||
|
||||
import { FastMCP } from 'fastmcp';
|
||||
|
||||
|
|
|
|||
|
|
@ -415,10 +415,30 @@ async function createOpenCodeRuntimeAdapterRegistry(
|
|||
}
|
||||
|
||||
reportProgress('runtime-bridge', 'Preparing OpenCode bridge...');
|
||||
const resolveBridgeCommandEnv = async (): Promise<NodeJS.ProcessEnv> => {
|
||||
const nextEnv = { ...bridgeEnv };
|
||||
if (!bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL) {
|
||||
return nextEnv;
|
||||
}
|
||||
try {
|
||||
const mcpHttpServer = await agentTeamsMcpHttpServer.ensureStarted();
|
||||
bridgeEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL = mcpHttpServer.url;
|
||||
nextEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL = mcpHttpServer.url;
|
||||
} catch (error) {
|
||||
delete nextEnv.CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL;
|
||||
logger.warn(
|
||||
`[OpenCode] Runtime adapter bridge MCP HTTP server refresh failed: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`
|
||||
);
|
||||
}
|
||||
return nextEnv;
|
||||
};
|
||||
const bridgeClient = new OpenCodeBridgeCommandClient({
|
||||
binaryPath,
|
||||
tempDirectory: join(app.getPath('temp'), 'claude-team-opencode-bridge'),
|
||||
env: bridgeEnv,
|
||||
envProvider: resolveBridgeCommandEnv,
|
||||
});
|
||||
const bridgeControlDir = join(app.getPath('userData'), 'opencode-bridge');
|
||||
const clientIdentity = createOpenCodeBridgeClientIdentity({
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ export interface OpenCodeBridgeCommandClientOptions {
|
|||
diagnosticIdFactory?: () => string;
|
||||
clock?: () => Date;
|
||||
env?: NodeJS.ProcessEnv;
|
||||
envProvider?: () => NodeJS.ProcessEnv | Promise<NodeJS.ProcessEnv>;
|
||||
keepInputFile?: boolean;
|
||||
}
|
||||
|
||||
|
|
@ -102,6 +103,7 @@ export class OpenCodeBridgeCommandClient {
|
|||
private readonly diagnosticIdFactory: () => string;
|
||||
private readonly clock: () => Date;
|
||||
private readonly env: NodeJS.ProcessEnv;
|
||||
private readonly envProvider: (() => NodeJS.ProcessEnv | Promise<NodeJS.ProcessEnv>) | null;
|
||||
private readonly keepInputFile: boolean;
|
||||
|
||||
constructor(options: OpenCodeBridgeCommandClientOptions) {
|
||||
|
|
@ -114,6 +116,7 @@ export class OpenCodeBridgeCommandClient {
|
|||
options.diagnosticIdFactory ?? (() => `opencode-bridge-diagnostic-${randomUUID()}`);
|
||||
this.clock = options.clock ?? (() => new Date());
|
||||
this.env = applyOpenCodeAutoUpdatePolicy(options.env ?? process.env);
|
||||
this.envProvider = options.envProvider ?? null;
|
||||
this.keepInputFile = options.keepInputFile ?? false;
|
||||
}
|
||||
|
||||
|
|
@ -147,7 +150,7 @@ export class OpenCodeBridgeCommandClient {
|
|||
timeoutMs: options.timeoutMs,
|
||||
stdoutLimitBytes: options.stdoutLimitBytes ?? DEFAULT_STDOUT_LIMIT_BYTES,
|
||||
stderrLimitBytes: options.stderrLimitBytes ?? DEFAULT_STDERR_LIMIT_BYTES,
|
||||
env: this.env,
|
||||
env: await this.resolveEnv(),
|
||||
});
|
||||
|
||||
if (processResult.timedOut) {
|
||||
|
|
@ -195,6 +198,13 @@ export class OpenCodeBridgeCommandClient {
|
|||
}
|
||||
}
|
||||
|
||||
private async resolveEnv(): Promise<NodeJS.ProcessEnv> {
|
||||
if (!this.envProvider) {
|
||||
return this.env;
|
||||
}
|
||||
return applyOpenCodeAutoUpdatePolicy(await this.envProvider());
|
||||
}
|
||||
|
||||
private async writeInputFile<TBody>(
|
||||
envelope: OpenCodeBridgeCommandEnvelope<TBody>
|
||||
): Promise<string> {
|
||||
|
|
|
|||
|
|
@ -189,6 +189,43 @@ describe('OpenCodeBridgeCommandClient', () => {
|
|||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves command env lazily for each bridge command', async () => {
|
||||
runner.nextResult = {
|
||||
stdout: `${JSON.stringify(bridgeSuccess({ data: { runId: 'run-1' } }))}\n`,
|
||||
stderr: '',
|
||||
exitCode: 0,
|
||||
timedOut: false,
|
||||
};
|
||||
let envVersion = 0;
|
||||
const client = createClient({
|
||||
envProvider: () => {
|
||||
envVersion += 1;
|
||||
return {
|
||||
PATH: '/usr/bin',
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL: `http://127.0.0.1:${5000 + envVersion}/mcp`,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
await client.execute('opencode.launchTeam', { runId: 'run-1' }, {
|
||||
cwd: '/tmp/project',
|
||||
timeoutMs: 10_000,
|
||||
});
|
||||
await client.execute('opencode.launchTeam', { runId: 'run-2' }, {
|
||||
cwd: '/tmp/project',
|
||||
timeoutMs: 10_000,
|
||||
});
|
||||
|
||||
expect(runner.calls[0].env).toMatchObject({
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL: 'http://127.0.0.1:5001/mcp',
|
||||
OPENCODE_DISABLE_AUTOUPDATE: '1',
|
||||
});
|
||||
expect(runner.calls[1].env).toMatchObject({
|
||||
CLAUDE_MULTIMODEL_AGENT_TEAMS_MCP_URL: 'http://127.0.0.1:5002/mcp',
|
||||
OPENCODE_DISABLE_AUTOUPDATE: '1',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('redactBridgeDiagnosticText', () => {
|
||||
|
|
@ -205,7 +242,9 @@ describe('redactBridgeDiagnosticText', () => {
|
|||
});
|
||||
});
|
||||
|
||||
function createClient(): OpenCodeBridgeCommandClient {
|
||||
function createClient(
|
||||
overrides: Partial<ConstructorParameters<typeof OpenCodeBridgeCommandClient>[0]> = {}
|
||||
): OpenCodeBridgeCommandClient {
|
||||
return new OpenCodeBridgeCommandClient({
|
||||
binaryPath: '/usr/local/bin/agent-teams-controller',
|
||||
tempDirectory: tempDir,
|
||||
|
|
@ -215,6 +254,7 @@ function createClient(): OpenCodeBridgeCommandClient {
|
|||
diagnosticIdFactory: () => 'diag-1',
|
||||
clock: () => new Date('2026-04-21T12:00:00.000Z'),
|
||||
env: { PATH: '/usr/bin' },
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue