From 937916d5d961d1ba860458665f41a5e03f64b354 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Sat, 7 Nov 2026 11:51:03 -0500 Subject: [PATCH 1/5] fix(provider): expand a leading ~ in GROK_HOME Signed-off-by: Yordis Prieto --- apps/server/src/terminal/Manager.ts | 5 ++--- .../provider-core/src/server/instanceEnvironment.test.ts | 4 ++++ packages/provider-core/src/server/instanceEnvironment.ts | 7 ++----- packages/provider-core/src/server/pathExpansion.ts | 6 ++++++ 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/apps/server/src/terminal/Manager.ts b/apps/server/src/terminal/Manager.ts index 3ee43635f45c5..e56bdede5e76e 100644 --- a/apps/server/src/terminal/Manager.ts +++ b/apps/server/src/terminal/Manager.ts @@ -69,7 +69,7 @@ import { makeClaudeEnvironment } from "../provider/Drivers/ClaudeHome.ts"; import { deriveProviderInstanceConfigMap } from "../provider/ProviderInstanceRegistryHydration.ts"; import * as ServerSettings from "../serverSettings.ts"; import { increment, terminalSessionsTotal } from "../observability/Metrics.ts"; -import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion"; +import { expandHomeVariable } from "@t3tools/provider-core/server/pathExpansion"; import * as ProcessRunner from "../processRunner.ts"; import * as PortScanner from "../preview/PortScanner.ts"; import * as NativeTelemetryClient from "../resourceTelemetry/NativeTelemetryClient.ts"; @@ -1367,8 +1367,7 @@ function createTerminalSpawnEnv( platform === "win32" ? Object.keys(spawnEnv).find((candidate) => candidate.toLowerCase() === key.toLowerCase()) : undefined; - spawnEnv[existingKey ?? key] = - key === "CODEX_HOME" || key === "CLAUDE_CONFIG_DIR" ? expandHomePath(value, home) : value; + spawnEnv[existingKey ?? key] = expandHomeVariable(key, value, home); } } if (!spawnEnv.COLORTERM && runtimeEnv?.COLORTERM === undefined) { diff --git a/packages/provider-core/src/server/instanceEnvironment.test.ts b/packages/provider-core/src/server/instanceEnvironment.test.ts index 114905d3f2115..374559f7f099a 100644 --- a/packages/provider-core/src/server/instanceEnvironment.test.ts +++ b/packages/provider-core/src/server/instanceEnvironment.test.ts @@ -17,11 +17,13 @@ describe("mergeProviderInstanceEnvironment", () => { const baseEnv = { CODEX_HOME: "~/.inherited-codex", CLAUDE_CONFIG_DIR: "~/.inherited-claude", + GROK_HOME: "~/.inherited-grok", }; const environment = yield* mergeProviderInstanceEnvironment( [ { name: "CODEX_HOME", value, sensitive: false }, { name: "CLAUDE_CONFIG_DIR", value, sensitive: false }, + { name: "GROK_HOME", value, sensitive: false }, { name: "CUSTOM_VALUE", value, sensitive: false }, ], baseEnv, @@ -30,11 +32,13 @@ describe("mergeProviderInstanceEnvironment", () => { expect(environment).toEqual({ CODEX_HOME: path.join("/home/ada", tail), CLAUDE_CONFIG_DIR: path.join("/home/ada", tail), + GROK_HOME: path.join("/home/ada", tail), CUSTOM_VALUE: value, }); expect(baseEnv).toEqual({ CODEX_HOME: "~/.inherited-codex", CLAUDE_CONFIG_DIR: "~/.inherited-claude", + GROK_HOME: "~/.inherited-grok", }); }).pipe( Effect.provideService(HostProcess.HomeDirectory, "/home/ada"), diff --git a/packages/provider-core/src/server/instanceEnvironment.ts b/packages/provider-core/src/server/instanceEnvironment.ts index 470f7e70dc616..0ade572984275 100644 --- a/packages/provider-core/src/server/instanceEnvironment.ts +++ b/packages/provider-core/src/server/instanceEnvironment.ts @@ -2,7 +2,7 @@ import type { ProviderInstanceEnvironment } from "@t3tools/contracts"; import * as HostProcess from "@t3tools/shared/HostProcess"; import * as Effect from "effect/Effect"; -import { expandHomePath } from "./pathExpansion.ts"; +import { expandHomeVariable } from "./pathExpansion.ts"; export const mergeProviderInstanceEnvironment = Effect.fn(function* ( environment: ProviderInstanceEnvironment | undefined, @@ -27,10 +27,7 @@ export const mergeProviderInstanceEnvironment = Effect.fn(function* ( if (inherited.toUpperCase() === name) delete next[inherited]; } } - next[name] = - name === "CODEX_HOME" || name === "CLAUDE_CONFIG_DIR" - ? expandHomePath(variable.value, home) - : variable.value; + next[name] = expandHomeVariable(name, variable.value, home); } return next; }); diff --git a/packages/provider-core/src/server/pathExpansion.ts b/packages/provider-core/src/server/pathExpansion.ts index 9a3822b836312..6c5eab795000e 100644 --- a/packages/provider-core/src/server/pathExpansion.ts +++ b/packages/provider-core/src/server/pathExpansion.ts @@ -10,3 +10,9 @@ export function expandHomePath(value: string, home: string): string { } return value; } + +const HOME_PATH_VARIABLES = new Set(["CODEX_HOME", "CLAUDE_CONFIG_DIR", "GROK_HOME"]); + +export function expandHomeVariable(name: string, value: string, home: string): string { + return HOME_PATH_VARIABLES.has(name) ? expandHomePath(value, home) : value; +} From 00596b38b5cd2c453e99c001f02c5a6ab58e47fb Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Sat, 7 Nov 2026 12:14:28 -0500 Subject: [PATCH 2/5] fix(server): expand provider homes named in any case on Windows Signed-off-by: Yordis Prieto --- apps/server/src/terminal/Manager.test.ts | 12 ++++++++++++ apps/server/src/terminal/Manager.ts | 6 +++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/apps/server/src/terminal/Manager.test.ts b/apps/server/src/terminal/Manager.test.ts index d89316cc74951..4855545296cd1 100644 --- a/apps/server/src/terminal/Manager.test.ts +++ b/apps/server/src/terminal/Manager.test.ts @@ -1944,6 +1944,18 @@ it.layer( }), ); + it.effect("expands a provider home named in any case on Windows", () => + Effect.gen(function* () { + const path = yield* Path.Path; + const home = yield* HostProcess.HomeDirectory; + const { manager, ptyAdapter } = yield* createManager(5); + + yield* manager.open(openInput({ env: { grok_home: "~/.grok-work" } })); + + expect(ptyAdapter.spawnInputs[0]?.env.grok_home).toBe(path.join(home, ".grok-work")); + }).pipe(Effect.provide(layerWithHostPlatform("win32"))), + ); + it.effect("fails closed when a provider instance is missing", () => Effect.gen(function* () { const providerInstanceId = ProviderInstanceId.make("deleted_instance"); diff --git a/apps/server/src/terminal/Manager.ts b/apps/server/src/terminal/Manager.ts index e56bdede5e76e..839d7ad27e7b0 100644 --- a/apps/server/src/terminal/Manager.ts +++ b/apps/server/src/terminal/Manager.ts @@ -1367,7 +1367,11 @@ function createTerminalSpawnEnv( platform === "win32" ? Object.keys(spawnEnv).find((candidate) => candidate.toLowerCase() === key.toLowerCase()) : undefined; - spawnEnv[existingKey ?? key] = expandHomeVariable(key, value, home); + spawnEnv[existingKey ?? key] = expandHomeVariable( + platform === "win32" ? key.toUpperCase() : key, + value, + home, + ); } } if (!spawnEnv.COLORTERM && runtimeEnv?.COLORTERM === undefined) { From e0629a6cd71bf3a6cd79c51d9f7e595ce92ae928 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Sat, 7 Nov 2026 12:59:54 -0500 Subject: [PATCH 3/5] test(server): pin the inherited Windows key in the home expansion test Signed-off-by: Yordis Prieto --- apps/server/src/terminal/Manager.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/server/src/terminal/Manager.test.ts b/apps/server/src/terminal/Manager.test.ts index 4855545296cd1..6fbcd4ffba84b 100644 --- a/apps/server/src/terminal/Manager.test.ts +++ b/apps/server/src/terminal/Manager.test.ts @@ -1948,11 +1948,13 @@ it.layer( Effect.gen(function* () { const path = yield* Path.Path; const home = yield* HostProcess.HomeDirectory; - const { manager, ptyAdapter } = yield* createManager(5); + const { manager, ptyAdapter } = yield* createManager(5, { + env: { GROK_HOME: "C:\\inherited-grok" }, + }); yield* manager.open(openInput({ env: { grok_home: "~/.grok-work" } })); - expect(ptyAdapter.spawnInputs[0]?.env.grok_home).toBe(path.join(home, ".grok-work")); + expect(ptyAdapter.spawnInputs[0]?.env.GROK_HOME).toBe(path.join(home, ".grok-work")); }).pipe(Effect.provide(layerWithHostPlatform("win32"))), ); From 660bf221e4c51ad7e9da0f7677e36a573c440166 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Sat, 7 Nov 2026 17:10:38 -0500 Subject: [PATCH 4/5] fix(provider): expand ~ in CLAUDE_SECURESTORAGE_CONFIG_DIR - It names a Claude config directory like CLAUDE_CONFIG_DIR, so a ~ path there should expand the same way before the spawn Signed-off-by: Yordis Prieto --- .../provider-core/src/server/instanceEnvironment.test.ts | 4 ++++ packages/provider-core/src/server/pathExpansion.ts | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/packages/provider-core/src/server/instanceEnvironment.test.ts b/packages/provider-core/src/server/instanceEnvironment.test.ts index 374559f7f099a..d7a78f2b14450 100644 --- a/packages/provider-core/src/server/instanceEnvironment.test.ts +++ b/packages/provider-core/src/server/instanceEnvironment.test.ts @@ -17,12 +17,14 @@ describe("mergeProviderInstanceEnvironment", () => { const baseEnv = { CODEX_HOME: "~/.inherited-codex", CLAUDE_CONFIG_DIR: "~/.inherited-claude", + CLAUDE_SECURESTORAGE_CONFIG_DIR: "~/.inherited-claude-secure", GROK_HOME: "~/.inherited-grok", }; const environment = yield* mergeProviderInstanceEnvironment( [ { name: "CODEX_HOME", value, sensitive: false }, { name: "CLAUDE_CONFIG_DIR", value, sensitive: false }, + { name: "CLAUDE_SECURESTORAGE_CONFIG_DIR", value, sensitive: false }, { name: "GROK_HOME", value, sensitive: false }, { name: "CUSTOM_VALUE", value, sensitive: false }, ], @@ -32,12 +34,14 @@ describe("mergeProviderInstanceEnvironment", () => { expect(environment).toEqual({ CODEX_HOME: path.join("/home/ada", tail), CLAUDE_CONFIG_DIR: path.join("/home/ada", tail), + CLAUDE_SECURESTORAGE_CONFIG_DIR: path.join("/home/ada", tail), GROK_HOME: path.join("/home/ada", tail), CUSTOM_VALUE: value, }); expect(baseEnv).toEqual({ CODEX_HOME: "~/.inherited-codex", CLAUDE_CONFIG_DIR: "~/.inherited-claude", + CLAUDE_SECURESTORAGE_CONFIG_DIR: "~/.inherited-claude-secure", GROK_HOME: "~/.inherited-grok", }); }).pipe( diff --git a/packages/provider-core/src/server/pathExpansion.ts b/packages/provider-core/src/server/pathExpansion.ts index 6c5eab795000e..1aaef6e0903d0 100644 --- a/packages/provider-core/src/server/pathExpansion.ts +++ b/packages/provider-core/src/server/pathExpansion.ts @@ -11,7 +11,12 @@ export function expandHomePath(value: string, home: string): string { return value; } -const HOME_PATH_VARIABLES = new Set(["CODEX_HOME", "CLAUDE_CONFIG_DIR", "GROK_HOME"]); +const HOME_PATH_VARIABLES = new Set([ + "CODEX_HOME", + "CLAUDE_CONFIG_DIR", + "CLAUDE_SECURESTORAGE_CONFIG_DIR", + "GROK_HOME", +]); export function expandHomeVariable(name: string, value: string, home: string): string { return HOME_PATH_VARIABLES.has(name) ? expandHomePath(value, home) : value; From 0b7ee5afbe75b92f4f0c99faeb645c5762f8de4c Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Sat, 7 Nov 2026 17:21:04 -0500 Subject: [PATCH 5/5] revert: leave CLAUDE_SECURESTORAGE_CONFIG_DIR unexpanded - Claude derives its macOS Keychain entry from the literal value, so expanding a configured ~ path would orphan existing logins Signed-off-by: Yordis Prieto --- .../provider-core/src/server/instanceEnvironment.test.ts | 4 ---- packages/provider-core/src/server/pathExpansion.ts | 7 +------ 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/provider-core/src/server/instanceEnvironment.test.ts b/packages/provider-core/src/server/instanceEnvironment.test.ts index d7a78f2b14450..374559f7f099a 100644 --- a/packages/provider-core/src/server/instanceEnvironment.test.ts +++ b/packages/provider-core/src/server/instanceEnvironment.test.ts @@ -17,14 +17,12 @@ describe("mergeProviderInstanceEnvironment", () => { const baseEnv = { CODEX_HOME: "~/.inherited-codex", CLAUDE_CONFIG_DIR: "~/.inherited-claude", - CLAUDE_SECURESTORAGE_CONFIG_DIR: "~/.inherited-claude-secure", GROK_HOME: "~/.inherited-grok", }; const environment = yield* mergeProviderInstanceEnvironment( [ { name: "CODEX_HOME", value, sensitive: false }, { name: "CLAUDE_CONFIG_DIR", value, sensitive: false }, - { name: "CLAUDE_SECURESTORAGE_CONFIG_DIR", value, sensitive: false }, { name: "GROK_HOME", value, sensitive: false }, { name: "CUSTOM_VALUE", value, sensitive: false }, ], @@ -34,14 +32,12 @@ describe("mergeProviderInstanceEnvironment", () => { expect(environment).toEqual({ CODEX_HOME: path.join("/home/ada", tail), CLAUDE_CONFIG_DIR: path.join("/home/ada", tail), - CLAUDE_SECURESTORAGE_CONFIG_DIR: path.join("/home/ada", tail), GROK_HOME: path.join("/home/ada", tail), CUSTOM_VALUE: value, }); expect(baseEnv).toEqual({ CODEX_HOME: "~/.inherited-codex", CLAUDE_CONFIG_DIR: "~/.inherited-claude", - CLAUDE_SECURESTORAGE_CONFIG_DIR: "~/.inherited-claude-secure", GROK_HOME: "~/.inherited-grok", }); }).pipe( diff --git a/packages/provider-core/src/server/pathExpansion.ts b/packages/provider-core/src/server/pathExpansion.ts index 1aaef6e0903d0..6c5eab795000e 100644 --- a/packages/provider-core/src/server/pathExpansion.ts +++ b/packages/provider-core/src/server/pathExpansion.ts @@ -11,12 +11,7 @@ export function expandHomePath(value: string, home: string): string { return value; } -const HOME_PATH_VARIABLES = new Set([ - "CODEX_HOME", - "CLAUDE_CONFIG_DIR", - "CLAUDE_SECURESTORAGE_CONFIG_DIR", - "GROK_HOME", -]); +const HOME_PATH_VARIABLES = new Set(["CODEX_HOME", "CLAUDE_CONFIG_DIR", "GROK_HOME"]); export function expandHomeVariable(name: string, value: string, home: string): string { return HOME_PATH_VARIABLES.has(name) ? expandHomePath(value, home) : value;