fix(opencode): make mcp env handling portable
This commit is contained in:
parent
d1adfdba16
commit
8f62645677
3 changed files with 38 additions and 25 deletions
|
|
@ -180,9 +180,15 @@ function localMcpEnvironment(command: string, environment?: Record<string, strin
|
|||
return [[key, value] as const]
|
||||
}),
|
||||
)
|
||||
return {
|
||||
const defaults = {
|
||||
...inherited,
|
||||
...(command === "opencode" ? { BUN_BE_BUN: "1" } : {}),
|
||||
}
|
||||
if (process.platform !== "win32" || !environment) return { ...defaults, ...environment }
|
||||
|
||||
const configured = new Set(Object.keys(environment).map((key) => key.toUpperCase()))
|
||||
return {
|
||||
...Object.fromEntries(Object.entries(defaults).filter(([key]) => !configured.has(key.toUpperCase()))),
|
||||
...environment,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
import readline from "node:readline"
|
||||
import { writeFile } from "node:fs/promises"
|
||||
|
||||
await Bun.write(process.env.MCP_ENV_OUTPUT!, JSON.stringify(process.env))
|
||||
await writeFile(process.env.MCP_ENV_OUTPUT, JSON.stringify(process.env))
|
||||
|
||||
const lines = readline.createInterface({ input: process.stdin })
|
||||
lines.on("close", () => process.exit(0))
|
||||
lines.on("line", (line) => {
|
||||
const request = JSON.parse(line) as { id?: number; method: string; params?: { protocolVersion?: string } }
|
||||
const request = JSON.parse(line)
|
||||
if (request.method !== "initialize") return
|
||||
process.stdout.write(
|
||||
`${JSON.stringify({
|
||||
|
|
@ -6,54 +6,60 @@ import { TestInstance } from "../fixture/fixture"
|
|||
import { testEffect } from "../lib/effect"
|
||||
|
||||
const it = testEffect(MCP.defaultLayer)
|
||||
const inherited = [
|
||||
"APPDATA",
|
||||
"HOME",
|
||||
"LANG",
|
||||
"LOCALAPPDATA",
|
||||
"PATH",
|
||||
"PATHEXT",
|
||||
"SYSTEMROOT",
|
||||
"TEMP",
|
||||
"TMPDIR",
|
||||
"USERPROFILE",
|
||||
] as const
|
||||
|
||||
it.instance(
|
||||
"local subprocess receives only baseline and configured environment",
|
||||
() =>
|
||||
Effect.gen(function* () {
|
||||
const test = yield* TestInstance
|
||||
const values = {
|
||||
APPDATA: path.join(test.directory, "appdata"),
|
||||
LC_TIME: "C",
|
||||
OPENCODE_MCP_PARENT_SECRET: "parent-secret",
|
||||
PATHEXT: ".EXE;.CMD",
|
||||
SYSTEMROOT: path.join(test.directory, "windows"),
|
||||
TMPDIR: test.directory,
|
||||
}
|
||||
const previous = Object.fromEntries(Object.keys(values).map((key) => [key, process.env[key]]))
|
||||
Object.assign(process.env, values)
|
||||
const previous = process.env.OPENCODE_MCP_PARENT_SECRET
|
||||
process.env.OPENCODE_MCP_PARENT_SECRET = "parent-secret"
|
||||
|
||||
yield* MCP.Service.use((mcp) =>
|
||||
Effect.gen(function* () {
|
||||
const output = path.join(test.directory, "environment.json")
|
||||
const result = yield* mcp.add("environment", {
|
||||
type: "local",
|
||||
command: [process.execPath, path.join(import.meta.dir, "../fixture/mcp-environment.ts")],
|
||||
command: [process.execPath, path.join(import.meta.dir, "../fixture/mcp-environment.js")],
|
||||
environment: {
|
||||
MCP_ENV_OUTPUT: output,
|
||||
MCP_EXPLICIT_TOKEN: "configured-token",
|
||||
...(process.platform === "win32" ? { Path: path.dirname(process.execPath) } : {}),
|
||||
},
|
||||
})
|
||||
|
||||
if (!("environment" in result.status)) throw new Error("Expected MCP status map")
|
||||
expect(result.status.environment).toEqual({ status: "connected" })
|
||||
const env = (yield* Effect.promise(() => Bun.file(output).json())) as Record<string, string>
|
||||
expect(env.OPENCODE_MCP_PARENT_SECRET).toBeUndefined()
|
||||
expect(env.MCP_EXPLICIT_TOKEN).toBe("configured-token")
|
||||
expect(env.PATH).toBe(process.env.PATH!)
|
||||
expect(env.HOME).toBe(process.env.HOME!)
|
||||
expect(env.TMPDIR).toBe(values.TMPDIR)
|
||||
expect(env.LC_TIME).toBe(values.LC_TIME)
|
||||
expect(env.APPDATA).toBe(values.APPDATA)
|
||||
expect(env.PATHEXT).toBe(values.PATHEXT)
|
||||
expect(env.SYSTEMROOT).toBe(values.SYSTEMROOT)
|
||||
inherited.forEach((key) => {
|
||||
if (process.platform === "win32" && key === "PATH") return
|
||||
if (process.env[key] !== undefined) expect(env[key]).toBe(process.env[key])
|
||||
})
|
||||
if (process.platform === "win32") {
|
||||
expect(Object.entries(env).find(([key]) => key.toUpperCase() === "PATH")?.[1]).toBe(
|
||||
path.dirname(process.execPath),
|
||||
)
|
||||
}
|
||||
}).pipe(Effect.ensuring(mcp.disconnect("environment").pipe(Effect.ignore))),
|
||||
).pipe(
|
||||
Effect.ensuring(
|
||||
Effect.sync(() => {
|
||||
Object.entries(previous).forEach(([key, value]) => {
|
||||
if (value === undefined) delete process.env[key]
|
||||
else process.env[key] = value
|
||||
})
|
||||
if (previous === undefined) delete process.env.OPENCODE_MCP_PARENT_SECRET
|
||||
else process.env.OPENCODE_MCP_PARENT_SECRET = previous
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue