| author | |
| committer | |
| log | 20f346d8ef58b21c339bc956c1728f4f16d09a87 |
| tree | a2b19b9e72680d2c76f24503650fd2e2fd0d630a |
| parent | c83bc5d48a2bf983acb1c8aaff3d34f86c14032e |
| signature | Signed by PGP key B5690EEEBB952194 |
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: = <=>6 files changed, 67 insertions(+), 5 deletions(-)
apps/server/src/codexAppServerManager.ts+3-2| ... | @@ -32,6 +32,7 @@ import { | ... | @@ -32,6 +32,7 @@ import { |
| 32 | type CodexAccountSnapshot, | 32 | type CodexAccountSnapshot, |
| 33 | } from "./provider/codexAccount.ts"; | 33 | } from "./provider/codexAccount.ts"; |
| 34 | import { buildCodexInitializeParams, killCodexChildProcess } from "./provider/codexAppServer.ts"; | 34 | import { buildCodexInitializeParams, killCodexChildProcess } from "./provider/codexAppServer.ts"; |
| 35 | import { expandHomePath } from "./pathExpansion.ts"; | ||
| 35 | 36 | ||
| 36 | export { buildCodexInitializeParams } from "./provider/codexAppServer.ts"; | 37 | export { buildCodexInitializeParams } from "./provider/codexAppServer.ts"; |
| 37 | export { readCodexAccountSnapshot, resolveCodexModelForAccount } from "./provider/codexAccount.ts"; | 38 | export { readCodexAccountSnapshot, resolveCodexModelForAccount } from "./provider/codexAccount.ts"; |
| ... | @@ -493,7 +494,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve | ... | @@ -493,7 +494,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve |
| 493 | cwd: resolvedCwd, | 494 | cwd: resolvedCwd, |
| 494 | env: { | 495 | env: { |
| 495 | ...process.env, | 496 | ...process.env, |
| 496 | ...(codexHomePath ? { CODEX_HOME: codexHomePath } : {}), | 497 | ...(codexHomePath ? { CODEX_HOME: expandHomePath(codexHomePath) } : {}), |
| 497 | }, | 498 | }, |
| 498 | stdio: ["pipe", "pipe", "pipe"], | 499 | stdio: ["pipe", "pipe", "pipe"], |
| 499 | shell: process.platform === "win32", | 500 | shell: process.platform === "win32", |
| ... | @@ -1571,7 +1572,7 @@ function assertSupportedCodexCliVersion(input: { | ... | @@ -1571,7 +1572,7 @@ function assertSupportedCodexCliVersion(input: { |
| 1571 | cwd: input.cwd, | 1572 | cwd: input.cwd, |
| 1572 | env: { | 1573 | env: { |
| 1573 | ...process.env, | 1574 | ...process.env, |
| 1574 | ...(input.homePath ? { CODEX_HOME: input.homePath } : {}), | 1575 | ...(input.homePath ? { CODEX_HOME: expandHomePath(input.homePath) } : {}), |
| 1575 | }, | 1576 | }, |
| 1576 | encoding: "utf8", | 1577 | encoding: "utf8", |
| 1577 | shell: process.platform === "win32", | 1578 | shell: process.platform === "win32", |
apps/server/src/git/Layers/CodexTextGeneration.ts+4-1| ... | @@ -8,6 +8,7 @@ import { sanitizeBranchFragment, sanitizeFeatureBranchName } from "@t3tools/shar | ... | @@ -8,6 +8,7 @@ import { sanitizeBranchFragment, sanitizeFeatureBranchName } from "@t3tools/shar |
| 8 | 8 | ||
| 9 | import { resolveAttachmentPath } from "../../attachmentStore.ts"; | 9 | import { resolveAttachmentPath } from "../../attachmentStore.ts"; |
| 10 | import { ServerConfig } from "../../config.ts"; | 10 | import { ServerConfig } from "../../config.ts"; |
| 11 | import { expandHomePath } from "../../pathExpansion.ts"; | ||
| 11 | import { TextGenerationError } from "@t3tools/contracts"; | 12 | import { TextGenerationError } from "@t3tools/contracts"; |
| 12 | import { | 13 | import { |
| 13 | type BranchNameGenerationInput, | 14 | type BranchNameGenerationInput, |
| ... | @@ -184,7 +185,9 @@ const makeCodexTextGeneration = Effect.gen(function* () { | ... | @@ -184,7 +185,9 @@ const makeCodexTextGeneration = Effect.gen(function* () { |
| 184 | { | 185 | { |
| 185 | env: { | 186 | env: { |
| 186 | ...process.env, | 187 | ...process.env, |
| 187 | ...(codexSettings?.homePath ? { CODEX_HOME: codexSettings.homePath } : {}), | 188 | ...(codexSettings?.homePath |
| 189 | ? { CODEX_HOME: expandHomePath(codexSettings.homePath) } | ||
| 190 | : {}), | ||
| 188 | }, | 191 | }, |
| 189 | cwd, | 192 | cwd, |
| 190 | shell: process.platform === "win32", | 193 | shell: process.platform === "win32", |
apps/server/src/pathExpansion.test.ts created+33| ... | @@ -0,0 +1,33 @@ | ||
| 1 | import { homedir } from "node:os"; | ||
| 2 | import { join } from "node:path"; | ||
| 3 | import { describe, expect, it } from "vitest"; | ||
| 4 | |||
| 5 | import { expandHomePath } from "./pathExpansion.ts"; | ||
| 6 | |||
| 7 | describe("expandHomePath", () => { | ||
| 8 | it("returns an empty string unchanged", () => { | ||
| 9 | expect(expandHomePath("")).toBe(""); | ||
| 10 | }); | ||
| 11 | |||
| 12 | it("returns paths without a leading tilde unchanged", () => { | ||
| 13 | expect(expandHomePath("/absolute/path")).toBe("/absolute/path"); | ||
| 14 | expect(expandHomePath("relative/path")).toBe("relative/path"); | ||
| 15 | expect(expandHomePath("some~weird~path")).toBe("some~weird~path"); | ||
| 16 | }); | ||
| 17 | |||
| 18 | it("expands a lone tilde to the home directory", () => { | ||
| 19 | expect(expandHomePath("~")).toBe(homedir()); | ||
| 20 | }); | ||
| 21 | |||
| 22 | it("expands ~/ to a subpath of the home directory", () => { | ||
| 23 | expect(expandHomePath("~/.codex-work")).toBe(join(homedir(), ".codex-work")); | ||
| 24 | }); | ||
| 25 | |||
| 26 | it("expands a Windows-style ~\\ prefix", () => { | ||
| 27 | expect(expandHomePath("~\\.codex")).toBe(join(homedir(), ".codex")); | ||
| 28 | }); | ||
| 29 | |||
| 30 | it("does not expand ~user paths", () => { | ||
| 31 | expect(expandHomePath("~alice/foo")).toBe("~alice/foo"); | ||
| 32 | }); | ||
| 33 | }); | ||
apps/server/src/pathExpansion.ts created+23| ... | @@ -0,0 +1,23 @@ | ||
| 1 | import { homedir } from "node:os"; | ||
| 2 | import { join } from "node:path"; | ||
| 3 | |||
| 4 | /** | ||
| 5 | * Expand a leading `~` (or `~/…`, `~\…`) in a user-supplied path to the | ||
| 6 | * current user's home directory. Spawned processes don't get shell | ||
| 7 | * expansion, so env vars like `CODEX_HOME=~/.codex-work` would be passed | ||
| 8 | * verbatim and treated as relative paths by the receiver. | ||
| 9 | * | ||
| 10 | * Matches the behavior of the other `expandHomePath` helpers in the | ||
| 11 | * workspace layers and CLI bootstrap: `~` alone and both `~/` and `~\` | ||
| 12 | * separators are handled. Returns the input unchanged if it doesn't | ||
| 13 | * start with `~` or is empty. Does not handle `~user` (other-user) | ||
| 14 | * expansion. | ||
| 15 | */ | ||
| 16 | export function expandHomePath(value: string): string { | ||
| 17 | if (!value) return value; | ||
| 18 | if (value === "~") return homedir(); | ||
| 19 | if (value.startsWith("~/") || value.startsWith("~\\")) { | ||
| 20 | return join(homedir(), value.slice(2)); | ||
| 21 | } | ||
| 22 | return value; | ||
| 23 | } | ||
apps/server/src/provider/Layers/CodexProvider.ts+2-1| ... | @@ -47,6 +47,7 @@ import { | ... | @@ -47,6 +47,7 @@ import { |
| 47 | } from "../codexAccount.ts"; | 47 | } from "../codexAccount.ts"; |
| 48 | import { probeCodexDiscovery } from "../codexAppServer.ts"; | 48 | import { probeCodexDiscovery } from "../codexAppServer.ts"; |
| 49 | import { CodexProvider } from "../Services/CodexProvider.ts"; | 49 | import { CodexProvider } from "../Services/CodexProvider.ts"; |
| 50 | import { expandHomePath } from "../../pathExpansion.ts"; | ||
| 50 | import { ServerSettingsService } from "../../serverSettings.ts"; | 51 | import { ServerSettingsService } from "../../serverSettings.ts"; |
| 51 | import { ServerSettingsError } from "@t3tools/contracts"; | 52 | import { ServerSettingsError } from "@t3tools/contracts"; |
| 52 | 53 | ||
| ... | @@ -325,7 +326,7 @@ const runCodexCommand = Effect.fn("runCodexCommand")(function* (args: ReadonlyAr | ... | @@ -325,7 +326,7 @@ const runCodexCommand = Effect.fn("runCodexCommand")(function* (args: ReadonlyAr |
| 325 | shell: process.platform === "win32", | 326 | shell: process.platform === "win32", |
| 326 | env: { | 327 | env: { |
| 327 | ...process.env, | 328 | ...process.env, |
| 328 | ...(codexSettings.homePath ? { CODEX_HOME: codexSettings.homePath } : {}), | 329 | ...(codexSettings.homePath ? { CODEX_HOME: expandHomePath(codexSettings.homePath) } : {}), |
| 329 | }, | 330 | }, |
| 330 | }); | 331 | }); |
| 331 | return yield* spawnAndCollect(codexSettings.binaryPath, command); | 332 | return yield* spawnAndCollect(codexSettings.binaryPath, command); |
apps/server/src/provider/codexAppServer.ts+2-1| ... | @@ -2,6 +2,7 @@ import { spawn, spawnSync, type ChildProcessWithoutNullStreams } from "node:chil | ... | @@ -2,6 +2,7 @@ import { spawn, spawnSync, type ChildProcessWithoutNullStreams } from "node:chil |
| 2 | import readline from "node:readline"; | 2 | import readline from "node:readline"; |
| 3 | import type { ServerProviderSkill } from "@t3tools/contracts"; | 3 | import type { ServerProviderSkill } from "@t3tools/contracts"; |
| 4 | import { readCodexAccountSnapshot, type CodexAccountSnapshot } from "./codexAccount.ts"; | 4 | import { readCodexAccountSnapshot, type CodexAccountSnapshot } from "./codexAccount.ts"; |
| 5 | import { expandHomePath } from "../pathExpansion.ts"; | ||
| 5 | 6 | ||
| 6 | interface JsonRpcProbeResponse { | 7 | interface JsonRpcProbeResponse { |
| 7 | readonly id?: unknown; | 8 | readonly id?: unknown; |
| ... | @@ -115,7 +116,7 @@ export async function probeCodexDiscovery(input: { | ... | @@ -115,7 +116,7 @@ export async function probeCodexDiscovery(input: { |
| 115 | const child = spawn(input.binaryPath, ["app-server"], { | 116 | const child = spawn(input.binaryPath, ["app-server"], { |
| 116 | env: { | 117 | env: { |
| 117 | ...process.env, | 118 | ...process.env, |
| 118 | ...(input.homePath ? { CODEX_HOME: input.homePath } : {}), | 119 | ...(input.homePath ? { CODEX_HOME: expandHomePath(input.homePath) } : {}), |
| 119 | }, | 120 | }, |
| 120 | stdio: ["pipe", "pipe", "pipe"], | 121 | stdio: ["pipe", "pipe", "pipe"], |
| 121 | shell: process.platform === "win32", | 122 | shell: process.platform === "win32", |