agent-relay-runner 0.94.1 → 0.94.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-runner",
3
- "version": "0.94.1",
3
+ "version": "0.94.3",
4
4
  "description": "Unified provider lifecycle runner for Agent Relay",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,7 +20,7 @@
20
20
  "directory": "runner"
21
21
  },
22
22
  "dependencies": {
23
- "agent-relay-sdk": "0.2.74"
23
+ "agent-relay-sdk": "0.2.75"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/bun": "latest",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-relay-runner",
3
3
  "description": "Thin Agent Relay runner bridge for Claude Code",
4
- "version": "0.94.1",
4
+ "version": "0.94.3",
5
5
  "agentRelayContracts": {
6
6
  "providerPluginProtocol": 1
7
7
  }
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, writeFileSync } from "node:fs";
1
+ import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
2
2
  import { readFile } from "node:fs/promises";
3
3
  import { homedir, tmpdir } from "node:os";
4
4
  import { join, resolve } from "node:path";
@@ -277,30 +277,21 @@ export class ClaudeAdapter implements ProviderAdapter {
277
277
  };
278
278
  }
279
279
 
280
- buildTmuxArgs(config: RunnerSpawnConfig, spawnArgs: SpawnArgs): { sessionName: string; socketName: string; args: string[]; launcherScript?: string } {
280
+ buildTmuxArgs(config: RunnerSpawnConfig, spawnArgs: SpawnArgs): { sessionName: string; socketName: string; args: string[]; launcherScript: string } {
281
281
  const sessionName = config.tmuxSession || tmuxSessionName(config.providerConfig.headless.tmuxPrefix, config.instanceId, config.label);
282
282
  const socketName = tmuxSocketName(sessionName);
283
283
  const shellCmd = [spawnArgs.command, ...spawnArgs.args].map(shellQuote).join(" ");
284
284
  const tmuxArgs = ["new-session", "-d", "-s", sessionName, "-x", "200", "-y", "50"];
285
285
 
286
286
  const envKeys = tmuxEnvKeys(spawnArgs.env, config.providerConfig.env);
287
- for (const key of envKeys) {
288
- if (spawnArgs.env[key] !== undefined) {
289
- tmuxArgs.push("-e", `${key}=${spawnArgs.env[key]}`);
290
- }
291
- }
292
-
293
- // tmux new-session has a hard limit on command length. When the inline shell
294
- // command is too long (e.g. fork with conversation history in --append-system-prompt),
295
- // externalize it to a launcher script that tmux executes instead.
296
- const MAX_TMUX_CMD_LEN = 2000;
297
- let launcherScript: string | undefined;
298
- if (shellCmd.length > MAX_TMUX_CMD_LEN) {
299
- launcherScript = writeLauncherScript(sessionName, shellCmd);
300
- tmuxArgs.push("-c", spawnArgs.cwd, `bash ${shellQuote(launcherScript)}`);
301
- } else {
302
- tmuxArgs.push("-c", spawnArgs.cwd, shellCmd);
303
- }
287
+ const env: Record<string, string> = {};
288
+ for (const key of envKeys) if (spawnArgs.env[key] !== undefined) env[key] = spawnArgs.env[key]!;
289
+
290
+ // tmux new-session has a hard command-length limit that includes every argv item,
291
+ // including `-e KEY=value`. Keep tmux argv bounded by putting both env payloads
292
+ // (profile JSON, tokens, etc.) and the provider command/prompt in a session script.
293
+ const launcherScript = writeLauncherScript(sessionName, env, shellCmd);
294
+ tmuxArgs.push("-c", spawnArgs.cwd, `bash ${shellQuote(launcherScript)}`);
304
295
  return { sessionName, socketName, args: tmuxArgs, launcherScript };
305
296
  }
306
297
 
@@ -646,16 +637,25 @@ async function submitTextToTmux(sessionName: string, text: string, socketName?:
646
637
  }
647
638
  }
648
639
 
649
- const LAUNCHER_DIR = join(tmpdir(), "agent-relay-launchers");
640
+ const LAUNCHER_DIR = join(tmpdir(), `agent-relay-launchers-${typeof process.getuid === "function" ? process.getuid() : "user"}`);
650
641
 
651
- function writeLauncherScript(sessionName: string, shellCmd: string): string {
652
- mkdirSync(LAUNCHER_DIR, { recursive: true });
642
+ function writeLauncherScript(sessionName: string, env: Record<string, string>, shellCmd: string): string {
643
+ mkdirSync(LAUNCHER_DIR, { recursive: true, mode: 0o700 });
644
+ chmodSync(LAUNCHER_DIR, 0o700);
653
645
  const sanitized = sanitizeFsName(sessionName, { replacement: "-", collapse: false });
654
646
  const scriptPath = join(LAUNCHER_DIR, `${sanitized}.sh`);
655
- writeFileSync(scriptPath, `#!/usr/bin/env bash\nexec ${shellCmd}\n`, { mode: 0o755 });
647
+ const exports = Object.entries(env)
648
+ .map(([key, value]) => `export ${shellEnvKey(key)}=${shellQuote(value)}`)
649
+ .join("\n");
650
+ writeFileSync(scriptPath, `#!/usr/bin/env bash\n${exports ? `${exports}\n` : ""}exec ${shellCmd}\n`, { mode: 0o700 });
656
651
  return scriptPath;
657
652
  }
658
653
 
654
+ function shellEnvKey(key: string): string {
655
+ if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) return key;
656
+ throw new Error(`invalid environment variable name for Claude launcher: ${key}`);
657
+ }
658
+
659
659
  export function tmuxEnvKeys(env: Record<string, string>, providerEnv: Record<string, string>): string[] {
660
660
  const keys = new Set<string>();
661
661
  for (const key of Object.keys(env)) {