Skip to content

Commit e68d760

Browse files
committed
fix(env): proxy directly to process.env instead of snapshotting
Env.set/get/all/remove now read and write process.env directly, fixing the stale snapshot that caused external SDKs to never see env changes (fixes #12698). - Remove InstanceState-based snapshot - Layer.succeed with direct process.env proxy - Add Env.test() for isolated test environments - Remove obsolete workaround TODOs in provider.ts
1 parent 5b60e51 commit e68d760

2 files changed

Lines changed: 35 additions & 21 deletions

File tree

packages/opencode/src/env/index.ts

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Context, Effect, Layer } from "effect"
2-
import { InstanceState } from "@/effect/instance-state"
32
import { makeRuntime } from "@/effect/run-service"
43

54
export namespace Env {
@@ -14,28 +13,47 @@ export namespace Env {
1413

1514
export class Service extends Context.Service<Service, Interface>()("@opencode/Env") {}
1615

17-
export const layer = Layer.effect(
16+
export const layer = Layer.succeed(
1817
Service,
19-
Effect.gen(function* () {
20-
const state = yield* InstanceState.make<State>(Effect.fn("Env.state")(() => Effect.succeed({ ...process.env })))
21-
22-
const get = Effect.fn("Env.get")((key: string) => InstanceState.use(state, (env) => env[key]))
23-
const all = Effect.fn("Env.all")(() => InstanceState.get(state))
24-
const set = Effect.fn("Env.set")(function* (key: string, value: string) {
25-
const env = yield* InstanceState.get(state)
26-
env[key] = value
27-
})
28-
const remove = Effect.fn("Env.remove")(function* (key: string) {
29-
const env = yield* InstanceState.get(state)
30-
delete env[key]
31-
})
32-
33-
return Service.of({ get, all, set, remove })
18+
Service.of({
19+
get: Effect.fn("Env.get")((key: string) => Effect.succeed(process.env[key])),
20+
all: Effect.fn("Env.all")(() => Effect.succeed(process.env as State)),
21+
set: Effect.fn("Env.set")((key: string, value: string) =>
22+
Effect.sync(() => {
23+
process.env[key] = value
24+
}),
25+
),
26+
remove: Effect.fn("Env.remove")((key: string) =>
27+
Effect.sync(() => {
28+
delete process.env[key]
29+
}),
30+
),
3431
}),
3532
)
3633

3734
export const defaultLayer = layer
3835

36+
export function test(initial: State = {}) {
37+
const state = { ...initial }
38+
return Layer.succeed(
39+
Service,
40+
Service.of({
41+
get: Effect.fn("Env.get")((key: string) => Effect.succeed(state[key])),
42+
all: Effect.fn("Env.all")(() => Effect.succeed(state)),
43+
set: Effect.fn("Env.set")((key: string, value: string) =>
44+
Effect.sync(() => {
45+
state[key] = value
46+
}),
47+
),
48+
remove: Effect.fn("Env.remove")((key: string) =>
49+
Effect.sync(() => {
50+
delete state[key]
51+
}),
52+
),
53+
}),
54+
)
55+
}
56+
3957
const rt = makeRuntime(Service, defaultLayer)
4058

4159
export function get(key: string) {

packages/opencode/src/provider/provider.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,6 @@ export namespace Provider {
287287

288288
const awsAccessKeyId = env["AWS_ACCESS_KEY_ID"]
289289

290-
// TODO: Using process.env directly because Env.set only updates a process.env shallow copy,
291-
// until the scope of the Env API is clarified (test only or runtime?)
292290
const awsBearerToken = iife(() => {
293291
const envToken = process.env.AWS_BEARER_TOKEN_BEDROCK
294292
if (envToken) return envToken
@@ -503,8 +501,6 @@ export namespace Provider {
503501
}),
504502
"sap-ai-core": Effect.fnUntraced(function* () {
505503
const auth = yield* dep.auth("sap-ai-core")
506-
// TODO: Using process.env directly because Env.set only updates a shallow copy (not process.env),
507-
// until the scope of the Env API is clarified (test only or runtime?)
508504
const envServiceKey = iife(() => {
509505
const envAICoreServiceKey = process.env.AICORE_SERVICE_KEY
510506
if (envAICoreServiceKey) return envAICoreServiceKey

0 commit comments

Comments
 (0)