fix(opencode): refresh bridge MCP env per command

This commit is contained in:
777genius 2026-05-16 02:21:02 +03:00
parent 48826af00b
commit f3f38dd0e9
4 changed files with 73 additions and 3 deletions

View file

@ -1,5 +1,5 @@
#!/usr/bin/env node
import { pathToFileURL } from 'node:url';
import { pathToFileURL } from 'url';
import { FastMCP } from 'fastmcp';

View file

@ -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({

View file

@ -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> {

View file

@ -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,
});
}