authorgravatar for gbarros1095@gmail.comGuilherme Barros <gbarros1095@gmail.com> 2026-08-22 22:00:21+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-22 16:00:21-04:00
log49c2b4471c59b21759eb2e254e9c4fa2579888a3
tree9a88d788d35ad603347572db4a30c928efaf1bb7
parent2c4158f87a1b6a586d0aa5e0338f122cb7887c4f
signature Signed by PGP key B5690EEEBB952194

fix(ssh): restore user PATH for remote servers (#7213)


5 files changed, 13 insertions(+), 14 deletions(-)

apps/server/src/provider/Layers/ClaudeProvider.ts+1-1
...@@ -862,7 +862,7 @@ export const checkClaudeProviderStatus = Effect.fn("checkClaudeProviderStatus")(...@@ -862,7 +862,7 @@ export const checkClaudeProviderStatus = Effect.fn("checkClaudeProviderStatus")(
862 status: "error",862 status: "error",
863 auth: { status: "unknown" },863 auth: { status: "unknown" },
864 message: isCommandMissingCause(error)864 message: isCommandMissingCause(error)
865 ? "Claude Agent CLI (`claude`) is not installed or not on PATH."865 ? "Claude Agent CLI (`claude`) was not found on PATH."
866 : "Failed to execute Claude Agent CLI health check.",866 : "Failed to execute Claude Agent CLI health check.",
867 },867 },
868 });868 });
apps/server/src/provider/Layers/CodexProvider.ts+1-1
...@@ -576,7 +576,7 @@ export const checkCodexProviderStatus = Effect.fn("checkCodexProviderStatus")(fu...@@ -576,7 +576,7 @@ export const checkCodexProviderStatus = Effect.fn("checkCodexProviderStatus")(fu
576 auth: { status: "unknown" },576 auth: { status: "unknown" },
577 message: installed577 message: installed
578 ? `Codex app-server provider probe failed: ${error.message}.`578 ? `Codex app-server provider probe failed: ${error.message}.`
579 : "Codex CLI (`codex`) is not installed or not on PATH.",579 : "Codex CLI (`codex`) was not found on PATH.",
580 },580 },
581 });581 });
582 }582 }
apps/server/src/provider/Layers/ProviderRegistry.test.ts+3-9
...@@ -494,10 +494,7 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsModule.layerTest(), Te...@@ -494,10 +494,7 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsModule.layerTest(), Te
494 assert.strictEqual(status.status, "error");494 assert.strictEqual(status.status, "error");
495 assert.strictEqual(status.installed, false);495 assert.strictEqual(status.installed, false);
496 assert.strictEqual(status.auth.status, "unknown");496 assert.strictEqual(status.auth.status, "unknown");
497 assert.strictEqual(497 assert.strictEqual(status.message, "Codex CLI (`codex`) was not found on PATH.");
498 status.message,
499 "Codex CLI (`codex`) is not installed or not on PATH.",
500 );
501 }),498 }),
502 );499 );
503500
...@@ -1462,7 +1459,7 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsModule.layerTest(), Te...@@ -1462,7 +1459,7 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsModule.layerTest(), Te
1462 assert.strictEqual(codexPersonal?.installed, false);1459 assert.strictEqual(codexPersonal?.installed, false);
1463 assert.strictEqual(1460 assert.strictEqual(
1464 codexPersonal?.message,1461 codexPersonal?.message,
1465 "Codex CLI (`codex`) is not installed or not on PATH.",1462 "Codex CLI (`codex`) was not found on PATH.",
1466 );1463 );
1467 }).pipe(Effect.provide(runtimeServices));1464 }).pipe(Effect.provide(runtimeServices));
1468 }),1465 }),
...@@ -2252,10 +2249,7 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsModule.layerTest(), Te...@@ -2252,10 +2249,7 @@ it.layer(Layer.mergeAll(NodeServices.layer, ServerSettingsModule.layerTest(), Te
2252 assert.strictEqual(status.status, "error");2249 assert.strictEqual(status.status, "error");
2253 assert.strictEqual(status.installed, false);2250 assert.strictEqual(status.installed, false);
2254 assert.strictEqual(status.auth.status, "unknown");2251 assert.strictEqual(status.auth.status, "unknown");
2255 assert.strictEqual(2252 assert.strictEqual(status.message, "Claude Agent CLI (`claude`) was not found on PATH.");
2256 status.message,
2257 "Claude Agent CLI (`claude`) is not installed or not on PATH.",
2258 );
2259 }).pipe(Effect.provide(failingSpawnerLayer("spawn claude ENOENT"))),2253 }).pipe(Effect.provide(failingSpawnerLayer("spawn claude ENOENT"))),
2260 );2254 );
22612255
packages/ssh/src/tunnel.test.ts+7-2
...@@ -243,8 +243,12 @@ describe("ssh tunnel scripts", () => {...@@ -243,8 +243,12 @@ describe("ssh tunnel scripts", () => {
243 username: "julius",243 username: "julius",
244 port: 2222,244 port: 2222,
245 } as const;245 } as const;
246 const spawner = ChildProcessSpawner.make(() =>246 const spawnedCommands: Array<ReadonlyArray<string>> = [];
247 Effect.succeed(makeSuccessfulProcess('loaded nvm default\n{"remotePort":3774}\n')),247 const spawner = ChildProcessSpawner.make((command) =>
248 Effect.sync(() => {
249 spawnedCommands.push(commandArgs(command));
250 return makeSuccessfulProcess('loaded nvm default\n{"remotePort":3774}\n');
251 }),
248 );252 );
249 const spawnerLayer = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner);253 const spawnerLayer = Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner);
250 const processLayer = Layer.merge(NodeServices.layer, spawnerLayer);254 const processLayer = Layer.merge(NodeServices.layer, spawnerLayer);
...@@ -252,6 +256,7 @@ describe("ssh tunnel scripts", () => {...@@ -252,6 +256,7 @@ describe("ssh tunnel scripts", () => {
252 return Effect.gen(function* () {256 return Effect.gen(function* () {
253 const result = yield* launchOrReuseRemoteServer(target);257 const result = yield* launchOrReuseRemoteServer(target);
254 assert.equal(result.remotePort, 3774);258 assert.equal(result.remotePort, 3774);
259 assert.deepEqual(spawnedCommands[0]?.slice(-5, -1), ["sh", "-l", "-s", "--"]);
255 }).pipe(Effect.provide(processLayer));260 }).pipe(Effect.provide(processLayer));
256 });261 });
257262
packages/ssh/src/tunnel.ts+1-1
...@@ -724,7 +724,7 @@ export const launchOrReuseRemoteServer = Effect.fn("ssh/tunnel.launchOrReuseRemo...@@ -724,7 +724,7 @@ export const launchOrReuseRemoteServer = Effect.fn("ssh/tunnel.launchOrReuseRemo
724 stateKey: remoteStateKey(target),724 stateKey: remoteStateKey(target),
725 });725 });
726 const result = yield* runSshCommand(target, {726 const result = yield* runSshCommand(target, {
727 remoteCommandArgs: ["sh", "-s", "--", remoteStateKey(target)],727 remoteCommandArgs: ["sh", "-l", "-s", "--", remoteStateKey(target)],
728 stdin: buildRemoteLaunchScript(runner),728 stdin: buildRemoteLaunchScript(runner),
729 timeoutMs: REMOTE_LAUNCH_TIMEOUT_MS,729 timeoutMs: REMOTE_LAUNCH_TIMEOUT_MS,
730 ...(input?.authSecret === undefined ? {} : { authSecret: input.authSecret }),730 ...(input?.authSecret === undefined ? {} : { authSecret: input.authSecret }),