@madarco/agentbox 0.12.0 → 0.14.0

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.
Files changed (74) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +21 -7
  3. package/dist/{_cloud-attach-XKO4SHR3.js → _cloud-attach-GUBB5RH2.js} +4 -4
  4. package/dist/{chunk-R5XIDQFR.js → chunk-BKU34KYY.js} +170 -6
  5. package/dist/chunk-BKU34KYY.js.map +1 -0
  6. package/dist/{chunk-HFV6THYG.js → chunk-BYCLD6D6.js} +308 -36
  7. package/dist/chunk-BYCLD6D6.js.map +1 -0
  8. package/dist/chunk-LDMYHWUS.js +346 -0
  9. package/dist/chunk-LDMYHWUS.js.map +1 -0
  10. package/dist/{chunk-2LF5YILI.js → chunk-RSKG7AFU.js} +80 -6
  11. package/dist/chunk-RSKG7AFU.js.map +1 -0
  12. package/dist/{chunk-DHJ7OMIP.js → chunk-TBSIJVSN.js} +149 -47
  13. package/dist/chunk-TBSIJVSN.js.map +1 -0
  14. package/dist/{chunk-IZXPJPPV.js → chunk-TCS5HXJX.js} +389 -176
  15. package/dist/chunk-TCS5HXJX.js.map +1 -0
  16. package/dist/{chunk-ECLLV5JH.js → chunk-VATTS2MR.js} +156 -5
  17. package/dist/chunk-VATTS2MR.js.map +1 -0
  18. package/dist/{chunk-SNTHHWKY.js → chunk-XKH7NTT7.js} +80 -22
  19. package/dist/chunk-XKH7NTT7.js.map +1 -0
  20. package/dist/dist-34RKQ74M.js +662 -0
  21. package/dist/dist-34RKQ74M.js.map +1 -0
  22. package/dist/{dist-47LVLYUV.js → dist-3IMQNTTV.js} +14 -69
  23. package/dist/dist-3IMQNTTV.js.map +1 -0
  24. package/dist/{dist-RZZSSUNB.js → dist-4DPOL5A7.js} +5 -3
  25. package/dist/{dist-24PY2ZMO.js → dist-57M6ZA7H.js} +25 -177
  26. package/dist/dist-57M6ZA7H.js.map +1 -0
  27. package/dist/{dist-SWUOU34W.js → dist-J2IHD5T7.js} +37 -226
  28. package/dist/dist-J2IHD5T7.js.map +1 -0
  29. package/dist/index.js +1524 -921
  30. package/dist/index.js.map +1 -1
  31. package/dist/{prepared-state-MQHD3M5F-KE4DT3GX.js → prepared-state-MQHD3M5F-Q27AZU53.js} +2 -2
  32. package/package.json +9 -7
  33. package/runtime/docker/Dockerfile.box +21 -26
  34. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +37 -1
  35. package/runtime/docker/packages/ctl/dist/bin.cjs +46 -17
  36. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +17 -6
  37. package/runtime/docker/packages/sandbox-docker/scripts/chromium-resolver +57 -0
  38. package/runtime/docker/packages/sandbox-docker/scripts/claude-managed-settings.json +2 -1
  39. package/runtime/e2b/agentbox-checkpoint-cleanup +52 -0
  40. package/runtime/e2b/agentbox-codex-hooks.json +68 -0
  41. package/runtime/e2b/agentbox-open +28 -0
  42. package/runtime/e2b/agentbox-setup-skill.md +233 -0
  43. package/runtime/e2b/agentbox-vnc-start +102 -0
  44. package/runtime/e2b/attach-helper.cjs +167 -0
  45. package/runtime/e2b/claude-managed-settings.json +116 -0
  46. package/runtime/e2b/ctl.cjs +23864 -0
  47. package/runtime/e2b/custom-system-CLAUDE.md +46 -0
  48. package/runtime/e2b/gh-shim +344 -0
  49. package/runtime/e2b/git-shim +131 -0
  50. package/runtime/e2b/scripts/build-template.sh +295 -0
  51. package/runtime/hetzner/agentbox-setup-skill.md +37 -1
  52. package/runtime/hetzner/agentbox-vnc-start +17 -6
  53. package/runtime/hetzner/claude-managed-settings.json +2 -1
  54. package/runtime/hetzner/ctl.cjs +46 -17
  55. package/runtime/relay/bin.cjs +305 -230
  56. package/runtime/vercel/agentbox-setup-skill.md +37 -1
  57. package/runtime/vercel/agentbox-vnc-start +17 -6
  58. package/runtime/vercel/claude-managed-settings.json +2 -1
  59. package/runtime/vercel/ctl.cjs +46 -17
  60. package/share/agentbox-setup/SKILL.md +37 -1
  61. package/share/host-skills/agentbox-info/SKILL.md +26 -34
  62. package/dist/chunk-2LF5YILI.js.map +0 -1
  63. package/dist/chunk-DHJ7OMIP.js.map +0 -1
  64. package/dist/chunk-ECLLV5JH.js.map +0 -1
  65. package/dist/chunk-HFV6THYG.js.map +0 -1
  66. package/dist/chunk-IZXPJPPV.js.map +0 -1
  67. package/dist/chunk-R5XIDQFR.js.map +0 -1
  68. package/dist/chunk-SNTHHWKY.js.map +0 -1
  69. package/dist/dist-24PY2ZMO.js.map +0 -1
  70. package/dist/dist-47LVLYUV.js.map +0 -1
  71. package/dist/dist-SWUOU34W.js.map +0 -1
  72. /package/dist/{_cloud-attach-XKO4SHR3.js.map → _cloud-attach-GUBB5RH2.js.map} +0 -0
  73. /package/dist/{dist-RZZSSUNB.js.map → dist-4DPOL5A7.js.map} +0 -0
  74. /package/dist/{prepared-state-MQHD3M5F-KE4DT3GX.js.map → prepared-state-MQHD3M5F-Q27AZU53.js.map} +0 -0
@@ -1,42 +1,48 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  DEFAULT_RELAY_PORT,
4
+ loadEffectiveConfig,
4
5
  readBoxStatus
5
- } from "./chunk-IZXPJPPV.js";
6
+ } from "./chunk-TCS5HXJX.js";
6
7
 
7
8
  // src/commands/_cloud-attach.ts
8
- import { spawn as spawn3 } from "child_process";
9
+ import { spawn as spawn4 } from "child_process";
9
10
  import { appendFileSync } from "fs";
10
11
  import { homedir } from "os";
11
12
  import { join as join2 } from "path";
12
13
  import { spinner } from "@clack/prompts";
13
14
 
14
15
  // src/provider/registry.ts
15
- var KNOWN = ["docker", "daytona", "hetzner", "vercel"];
16
+ var KNOWN = ["docker", "daytona", "hetzner", "vercel", "e2b"];
16
17
  function isKnownProvider(name) {
17
18
  return KNOWN.includes(name);
18
19
  }
19
20
  async function getProvider(name) {
20
21
  switch (name) {
21
22
  case "docker": {
22
- const mod = await import("./dist-RZZSSUNB.js");
23
+ const mod = await import("./dist-4DPOL5A7.js");
23
24
  return mod.dockerProvider;
24
25
  }
25
26
  case "daytona": {
26
- const mod = await import("./dist-47LVLYUV.js");
27
+ const mod = await import("./dist-3IMQNTTV.js");
27
28
  await mod.ensureDaytonaCredentials();
28
29
  return mod.daytonaProvider;
29
30
  }
30
31
  case "hetzner": {
31
- const mod = await import("./dist-SWUOU34W.js");
32
+ const mod = await import("./dist-J2IHD5T7.js");
32
33
  await mod.ensureHetznerCredentials();
33
34
  return mod.hetznerProvider;
34
35
  }
35
36
  case "vercel": {
36
- const mod = await import("./dist-24PY2ZMO.js");
37
+ const mod = await import("./dist-57M6ZA7H.js");
37
38
  await mod.ensureVercelCredentials();
38
39
  return mod.vercelProvider;
39
40
  }
41
+ case "e2b": {
42
+ const mod = await import("./dist-34RKQ74M.js");
43
+ await mod.ensureE2bCredentials();
44
+ return mod.e2bProvider;
45
+ }
40
46
  default:
41
47
  throw new Error(`unknown sandbox provider: ${String(name)}`);
42
48
  }
@@ -56,20 +62,20 @@ async function providerForCreate(choice) {
56
62
  }
57
63
 
58
64
  // src/wrapped-pty/run.ts
59
- import { spawn as spawn2, spawnSync } from "child_process";
65
+ import { spawn as spawn3, spawnSync as spawnSync2 } from "child_process";
60
66
 
61
67
  // src/pty/pty-backend.ts
62
68
  async function loadPtyBackend() {
63
69
  try {
64
70
  const ptyMod = await import("@homebridge/node-pty-prebuilt-multiarch");
65
71
  const xtermMod = await import("@xterm/headless");
66
- const spawn4 = ptyMod["spawn"] ?? ptyMod["default"]?.["spawn"];
72
+ const spawn5 = ptyMod["spawn"] ?? ptyMod["default"]?.["spawn"];
67
73
  const Terminal = xtermMod["Terminal"] ?? xtermMod["default"]?.["Terminal"];
68
- if (typeof spawn4 !== "function" || typeof Terminal !== "function") {
74
+ if (typeof spawn5 !== "function" || typeof Terminal !== "function") {
69
75
  return null;
70
76
  }
71
77
  return {
72
- ptySpawn: spawn4,
78
+ ptySpawn: spawn5,
73
79
  termCtor: Terminal
74
80
  };
75
81
  } catch {
@@ -82,10 +88,16 @@ import { spawn } from "child_process";
82
88
  function detectHostTerminal(env = process.env) {
83
89
  const tmux = env["TMUX"];
84
90
  if (tmux && tmux.length > 0) return "tmux";
91
+ const cmuxSocket = env["CMUX_SOCKET_PATH"];
92
+ if (cmuxSocket && cmuxSocket.length > 0) return "cmux";
85
93
  const termProgram = env["TERM_PROGRAM"];
86
94
  if (termProgram === "iTerm.app") return "iterm2";
87
95
  return "unknown";
88
96
  }
97
+ function cmuxBinary(env = process.env) {
98
+ const bundled = env["CMUX_BUNDLED_CLI_PATH"];
99
+ return bundled && bundled.length > 0 ? bundled : "cmux";
100
+ }
89
101
  function shellQuote(s) {
90
102
  if (s.length === 0) return "''";
91
103
  return "'" + s.replace(/'/g, "'\\''") + "'";
@@ -98,6 +110,7 @@ function shellJoin(argv) {
98
110
  }
99
111
  async function spawnInNewTerminal(args) {
100
112
  if (args.host === "tmux") return spawnInTmux(args);
113
+ if (args.host === "cmux") return spawnInCmux(args);
101
114
  return spawnInITerm2(args);
102
115
  }
103
116
  async function spawnInTmux(args) {
@@ -124,6 +137,63 @@ async function spawnInTmux(args) {
124
137
  note: `Attached in new ${noteKind}.`
125
138
  };
126
139
  }
140
+ async function spawnInCmux(args) {
141
+ const bin = cmuxBinary();
142
+ if (args.mode === "window") {
143
+ const r = await runQuiet(bin, [
144
+ "new-workspace",
145
+ "--name",
146
+ args.title,
147
+ "--cwd",
148
+ args.cwd,
149
+ "--command",
150
+ shellJoin(args.argv),
151
+ "--focus",
152
+ "true"
153
+ ]);
154
+ if (r.code !== 0) {
155
+ return {
156
+ launched: false,
157
+ note: "",
158
+ error: `cmux new-workspace exited ${String(r.code)}: ${r.stderr.trim()}`
159
+ };
160
+ }
161
+ return { launched: true, note: "Attached in new cmux workspace." };
162
+ }
163
+ const createArgv = args.mode === "split" ? ["new-split", "right", "--focus", "true"] : ["new-surface", "--focus", "true"];
164
+ const noteKind = args.mode === "split" ? "cmux split" : "cmux tab";
165
+ const created = await runQuiet(bin, createArgv);
166
+ if (created.code !== 0) {
167
+ return {
168
+ launched: false,
169
+ note: "",
170
+ error: `cmux ${createArgv[0]} exited ${String(created.code)}: ${created.stderr.trim()}`
171
+ };
172
+ }
173
+ const surfaceRef = parseCmuxRef(created.stdout);
174
+ if (!surfaceRef) {
175
+ return {
176
+ launched: false,
177
+ note: "",
178
+ error: `cmux ${createArgv[0]} gave no surface ref: ${created.stdout.trim()}`
179
+ };
180
+ }
181
+ const cmdLine = `cd ${shellQuote(args.cwd)} && exec ${shellJoin(args.argv)}`;
182
+ const sent = await runQuiet(bin, ["send", "--surface", surfaceRef, `${cmdLine}
183
+ `]);
184
+ if (sent.code !== 0) {
185
+ return {
186
+ launched: false,
187
+ note: "",
188
+ error: `cmux send exited ${String(sent.code)}: ${sent.stderr.trim()}`
189
+ };
190
+ }
191
+ return { launched: true, note: `Attached in new ${noteKind}.` };
192
+ }
193
+ function parseCmuxRef(stdout) {
194
+ const m = stdout.match(/\b(?:surface|pane):\d+\b/);
195
+ return m ? m[0] : void 0;
196
+ }
127
197
  async function spawnInITerm2(args) {
128
198
  const inner = shellJoin(args.argv);
129
199
  const cmdLine = `cd ${shellQuote(args.cwd)} && exec ${inner}`;
@@ -174,20 +244,139 @@ async function spawnInITerm2(args) {
174
244
  }
175
245
  function runQuiet(cmd, argv) {
176
246
  return new Promise((resolve) => {
177
- const child = spawn(cmd, argv, { stdio: ["ignore", "ignore", "pipe"] });
247
+ const child = spawn(cmd, argv, { stdio: ["ignore", "pipe", "pipe"] });
248
+ let stdout = "";
178
249
  let stderr = "";
250
+ child.stdout?.on("data", (chunk) => {
251
+ stdout += chunk.toString("utf8");
252
+ });
179
253
  child.stderr?.on("data", (chunk) => {
180
254
  stderr += chunk.toString("utf8");
181
255
  });
182
256
  child.on("error", (err) => {
183
- resolve({ code: 127, stderr: err.message });
257
+ resolve({ code: 127, stdout, stderr: err.message });
184
258
  });
185
259
  child.on("exit", (code) => {
186
- resolve({ code: typeof code === "number" ? code : 1, stderr });
260
+ resolve({ code: typeof code === "number" ? code : 1, stdout, stderr });
187
261
  });
188
262
  });
189
263
  }
190
264
 
265
+ // src/terminal/cmux-status.ts
266
+ import { spawn as spawn2, spawnSync } from "child_process";
267
+ var AGENT_LABEL = {
268
+ claude: "claude",
269
+ codex: "codex",
270
+ opencode: "opencode"
271
+ };
272
+ function mapActivityToWorkspace(mode, activity) {
273
+ if (mode === "shell") return null;
274
+ const label = AGENT_LABEL[mode];
275
+ switch (activity) {
276
+ case "working":
277
+ case "compacting":
278
+ return { description: `${label} \xB7 working`, color: "Blue" };
279
+ case "question":
280
+ case "waiting":
281
+ return { description: `${label} \xB7 needs input`, color: "Amber" };
282
+ case "end-plan":
283
+ return { description: `${label} \xB7 plan ready`, color: "Amber" };
284
+ case "error":
285
+ return { description: `${label} \xB7 error`, color: "Red" };
286
+ case "idle":
287
+ return { description: `${label} \xB7 idle`, color: "" };
288
+ case "unknown":
289
+ case void 0:
290
+ default:
291
+ return null;
292
+ }
293
+ }
294
+ function isAttentionState(activity) {
295
+ return activity === "question" || activity === "waiting" || activity === "end-plan" || activity === "error";
296
+ }
297
+ function attentionReason(activity) {
298
+ switch (activity) {
299
+ case "end-plan":
300
+ return "plan ready";
301
+ case "error":
302
+ return "error";
303
+ case "question":
304
+ case "waiting":
305
+ default:
306
+ return "needs input";
307
+ }
308
+ }
309
+ function cmuxStatusActive(env = process.env) {
310
+ const sock = env["CMUX_SOCKET_PATH"];
311
+ return typeof sock === "string" && sock.length > 0;
312
+ }
313
+ async function cmuxStatusEnabled(env = process.env) {
314
+ if (!cmuxStatusActive(env)) return false;
315
+ try {
316
+ const cfg = await loadEffectiveConfig(process.cwd());
317
+ return cfg.effective.attach.cmuxStatus;
318
+ } catch {
319
+ return true;
320
+ }
321
+ }
322
+ function runCmux(argv) {
323
+ try {
324
+ const child = spawn2(cmuxBinary(), argv, { stdio: "ignore" });
325
+ child.on("error", () => {
326
+ });
327
+ child.unref();
328
+ } catch {
329
+ }
330
+ }
331
+ function captureCmuxWorkspace(env = process.env) {
332
+ const wsId = env["CMUX_WORKSPACE_ID"];
333
+ if (!wsId) return null;
334
+ try {
335
+ const r = spawnSync(cmuxBinary(), ["list-workspaces", "--json", "--id-format", "both"], {
336
+ encoding: "utf8"
337
+ });
338
+ if (r.status !== 0 || !r.stdout) return null;
339
+ const data = JSON.parse(r.stdout);
340
+ for (const w of data.workspaces ?? []) {
341
+ if (!JSON.stringify(w).includes(wsId)) continue;
342
+ const description = typeof w["description"] === "string" ? w["description"] : "";
343
+ const color = typeof w["custom_color"] === "string" ? w["custom_color"] : "";
344
+ return { description, color };
345
+ }
346
+ } catch {
347
+ }
348
+ return null;
349
+ }
350
+ function applyCmuxAgentState(mode, activity) {
351
+ const view = mapActivityToWorkspace(mode, activity);
352
+ if (!view) return;
353
+ runCmux(["workspace-action", "--action", "set-description", "--description", view.description]);
354
+ if (view.color) {
355
+ runCmux(["workspace-action", "--action", "set-color", "--color", view.color]);
356
+ } else {
357
+ runCmux(["workspace-action", "--action", "clear-color"]);
358
+ }
359
+ }
360
+ function markCmuxTabAttention(mode, boxName, activity, env = process.env) {
361
+ const label = mode === "shell" ? "shell" : AGENT_LABEL[mode];
362
+ const argv = ["notify", "--title", boxName, "--body", `${label} \xB7 ${attentionReason(activity)}`];
363
+ const surface = env["CMUX_SURFACE_ID"];
364
+ if (surface && surface.length > 0) argv.push("--surface", surface);
365
+ runCmux(argv);
366
+ }
367
+ function restoreCmuxWorkspace(orig) {
368
+ if (orig && orig.description) {
369
+ runCmux(["workspace-action", "--action", "set-description", "--description", orig.description]);
370
+ } else {
371
+ runCmux(["workspace-action", "--action", "clear-description"]);
372
+ }
373
+ if (orig && orig.color) {
374
+ runCmux(["workspace-action", "--action", "set-color", "--color", orig.color]);
375
+ } else {
376
+ runCmux(["workspace-action", "--action", "clear-color"]);
377
+ }
378
+ }
379
+
191
380
  // src/terminal/title.ts
192
381
  var ESC = "\x1B";
193
382
  var BEL = "\x07";
@@ -665,7 +854,7 @@ var ADVANCED_HINT_GROUPS = [
665
854
  ["u", "url"],
666
855
  ["t", "stop"],
667
856
  ["p", "pause"],
668
- ["d", "destroy"],
857
+ ["k", "destroy"],
669
858
  ["q", "quit"]
670
859
  ];
671
860
  function statusLine(box, w, stateLabel, groups = HINT_GROUPS, fallbackGroups) {
@@ -728,12 +917,16 @@ var DETACHABLE_LEADER_HINTS = [
728
917
  ["c", "code"],
729
918
  ["s", "screen"],
730
919
  ["u", "url"],
920
+ ["t", "shell"],
921
+ ["k", "destroy"],
731
922
  ["d", "detach"]
732
923
  ];
733
924
  var PLAIN_LEADER_HINTS = [
734
925
  ["c", "code"],
735
926
  ["s", "screen"],
736
- ["u", "url"]
927
+ ["u", "url"],
928
+ ["t", "shell"],
929
+ ["k", "destroy"]
737
930
  ];
738
931
  function padTo(visible, width) {
739
932
  if (visible.length === width) return visible;
@@ -1062,7 +1255,8 @@ var CHECKPOINT_DROP_GRACE_MS = 4e3;
1062
1255
  var ACTION_FLASH = {
1063
1256
  screen: "Opening noVNC viewer\u2026",
1064
1257
  code: "Launching VS Code / Cursor\u2026",
1065
- url: "Opening box URL\u2026"
1258
+ url: "Opening box URL\u2026",
1259
+ shell: "Opening shell in box\u2026"
1066
1260
  };
1067
1261
  var ACTION_CMD = {
1068
1262
  screen: { sub: "screen", flags: [] },
@@ -1154,6 +1348,7 @@ async function runWrappedAttach(opts) {
1154
1348
  let reconnecting = false;
1155
1349
  let reconnectAbort = null;
1156
1350
  let userDetached = false;
1351
+ let userKilled = false;
1157
1352
  let checkpointNoticeAt = 0;
1158
1353
  let checkpointNoticeClearedAt = 0;
1159
1354
  const reservedRows = () => FOOTER_ROWS + bandReservedRows;
@@ -1249,7 +1444,7 @@ async function runWrappedAttach(opts) {
1249
1444
  });
1250
1445
  };
1251
1446
  wireOutput();
1252
- const leaderChords = detachable ? { c: "code", s: "screen", u: "url", d: "detach" } : { c: "code", s: "screen", u: "url" };
1447
+ const leaderChords = detachable ? { c: "code", s: "screen", u: "url", t: "shell", k: "kill", d: "detach" } : { c: "code", s: "screen", u: "url", t: "shell", k: "kill" };
1253
1448
  const runAction = (name) => {
1254
1449
  if (name === "detach") {
1255
1450
  if (!reconnecting) {
@@ -1258,20 +1453,76 @@ async function runWrappedAttach(opts) {
1258
1453
  }
1259
1454
  return;
1260
1455
  }
1456
+ if (name === "kill") {
1457
+ if (capturingPrompt) return;
1458
+ const ev = {
1459
+ id: "local:kill",
1460
+ kind: "confirm",
1461
+ message: `Destroy ${opts.boxName}? This wipes the box and all its data.`,
1462
+ detail: "This cannot be undone.",
1463
+ defaultAnswer: "n",
1464
+ context: { command: "destroy box" }
1465
+ };
1466
+ capturingPrompt = ev;
1467
+ applyBandChange();
1468
+ void router.capture(ev).then((body) => {
1469
+ if (body.answer !== "y" || body.cancelled) return;
1470
+ userDetached = true;
1471
+ userKilled = true;
1472
+ flashMessage = `Destroying ${opts.boxName}\u2026`;
1473
+ recomputeFooter();
1474
+ redrawChrome();
1475
+ const cli = process.argv[1];
1476
+ if (typeof cli === "string" && cli.length > 0) {
1477
+ try {
1478
+ spawn3(process.execPath, [cli, "destroy", opts.boxId, "-y"], {
1479
+ detached: true,
1480
+ stdio: "ignore"
1481
+ }).unref();
1482
+ } catch (e) {
1483
+ logErr(`leader-action kill spawn failed: ${e.message}`);
1484
+ }
1485
+ }
1486
+ }).catch((e) => {
1487
+ if (capturingPrompt === ev) {
1488
+ capturingPrompt = null;
1489
+ applyBandChange();
1490
+ }
1491
+ const msg = e instanceof Error ? e.message : String(e);
1492
+ if (msg !== "resolved-elsewhere") logErr(`kill confirm rejected: ${msg}`);
1493
+ });
1494
+ return;
1495
+ }
1261
1496
  const cliEntry = process.argv[1];
1262
- if (typeof cliEntry === "string" && cliEntry.length > 0) {
1263
- const cmd = ACTION_CMD[name];
1264
- try {
1265
- spawn2(
1266
- process.execPath,
1267
- [cliEntry, cmd.sub, opts.boxId, ...cmd.flags],
1268
- { detached: true, stdio: "ignore" }
1269
- ).unref();
1270
- } catch (e) {
1271
- logErr(`leader-action spawn (${name}) failed: ${e.message}`);
1497
+ if (name === "shell") {
1498
+ const host = detectHostTerminal();
1499
+ if (typeof cliEntry === "string" && cliEntry.length > 0 && host !== "unknown") {
1500
+ void spawnInNewTerminal({
1501
+ host,
1502
+ mode: "tab",
1503
+ argv: [process.execPath, cliEntry, "shell", opts.boxId, "--new"],
1504
+ cwd: process.cwd(),
1505
+ title: `${opts.boxName} shell`
1506
+ }).then((r) => {
1507
+ if (!r.launched && r.error) logErr(`leader-action shell: ${r.error}`);
1508
+ }).catch((e) => logErr(`leader-action shell failed: ${e.message}`));
1509
+ }
1510
+ flashMessage = host === "unknown" ? `Run: agentbox shell ${opts.boxId} --new` : ACTION_FLASH.shell;
1511
+ } else {
1512
+ if (typeof cliEntry === "string" && cliEntry.length > 0) {
1513
+ const cmd = ACTION_CMD[name];
1514
+ try {
1515
+ spawn3(
1516
+ process.execPath,
1517
+ [cliEntry, cmd.sub, opts.boxId, ...cmd.flags],
1518
+ { detached: true, stdio: "ignore" }
1519
+ ).unref();
1520
+ } catch (e) {
1521
+ logErr(`leader-action spawn (${name}) failed: ${e.message}`);
1522
+ }
1272
1523
  }
1524
+ flashMessage = ACTION_FLASH[name];
1273
1525
  }
1274
- flashMessage = ACTION_FLASH[name];
1275
1526
  if (flashTimer) clearTimeout(flashTimer);
1276
1527
  flashTimer = setTimeout(() => {
1277
1528
  flashTimer = null;
@@ -1318,7 +1569,9 @@ async function runWrappedAttach(opts) {
1318
1569
  pty.write(b.toString("utf8"));
1319
1570
  },
1320
1571
  onAnswer: (body) => {
1321
- void postAnswer({ relayBaseUrl: opts.relayBaseUrl, body });
1572
+ if (!body.id.startsWith("local:")) {
1573
+ void postAnswer({ relayBaseUrl: opts.relayBaseUrl, body });
1574
+ }
1322
1575
  capturingPrompt = null;
1323
1576
  applyBandChange();
1324
1577
  },
@@ -1385,6 +1638,8 @@ async function runWrappedAttach(opts) {
1385
1638
  }
1386
1639
  }
1387
1640
  });
1641
+ let cmuxOn = false;
1642
+ let cmuxOrig = null;
1388
1643
  const pollStatus = async () => {
1389
1644
  try {
1390
1645
  const status = await readBoxStatus({
@@ -1395,6 +1650,12 @@ async function runWrappedAttach(opts) {
1395
1650
  const body = opts.mode === "codex" ? status?.codex : opts.mode === "opencode" ? status?.opencode : opts.mode === "shell" ? void 0 : status?.claude;
1396
1651
  const nextTitle = body?.sessionTitle?.trim() || void 0;
1397
1652
  const nextActivity = body?.state || void 0;
1653
+ if (cmuxOn && nextActivity !== lastActivity) {
1654
+ applyCmuxAgentState(opts.mode, nextActivity);
1655
+ if (isAttentionState(nextActivity) && !isAttentionState(lastActivity)) {
1656
+ markCmuxTabAttention(opts.mode, opts.boxName, nextActivity);
1657
+ }
1658
+ }
1398
1659
  const desiredTitle = nextTitle ?? opts.boxName;
1399
1660
  if (desiredTitle !== lastEmittedTitle) {
1400
1661
  lastEmittedTitle = desiredTitle;
@@ -1417,6 +1678,10 @@ async function runWrappedAttach(opts) {
1417
1678
  logErr(`status poll failed: ${e.message}`);
1418
1679
  }
1419
1680
  };
1681
+ if (opts.mode !== "shell") {
1682
+ cmuxOn = await cmuxStatusEnabled();
1683
+ if (cmuxOn) cmuxOrig = captureCmuxWorkspace();
1684
+ }
1420
1685
  void pollStatus();
1421
1686
  const statusTimer = setInterval(() => {
1422
1687
  void pollStatus();
@@ -1505,6 +1770,7 @@ async function runWrappedAttach(opts) {
1505
1770
  process.stdin.off("data", onStdinData);
1506
1771
  process.stdout.off("resize", onResize);
1507
1772
  clearInterval(statusTimer);
1773
+ if (cmuxOn) restoreCmuxWorkspace(cmuxOrig);
1508
1774
  stopSpinner();
1509
1775
  if (flashTimer) clearTimeout(flashTimer);
1510
1776
  if (process.stdin.isTTY) process.stdin.setRawMode(false);
@@ -1520,13 +1786,16 @@ async function runWrappedAttach(opts) {
1520
1786
  teardownPaint += cursorMoveTo(rsFinal, csFinal);
1521
1787
  process.stdout.write(teardownPaint);
1522
1788
  popTerminalTitle();
1523
- if (exitCode === 0 && opts.detachNotice) {
1789
+ if (userKilled) {
1790
+ process.stdout.write(`\x1B[1A\x1B[2K\rbox ${opts.boxName} destroyed
1791
+ `);
1792
+ } else if (exitCode === 0 && opts.detachNotice) {
1524
1793
  process.stdout.write("\x1B[1A\x1B[2K\r" + opts.detachNotice + "\n");
1525
1794
  }
1526
1795
  return exitCode;
1527
1796
  }
1528
1797
  function runFallback(command, argv, env) {
1529
- const child = spawnSync(command, argv, {
1798
+ const child = spawnSync2(command, argv, {
1530
1799
  stdio: "inherit",
1531
1800
  env: env ? { ...process.env, ...env } : process.env
1532
1801
  });
@@ -1698,12 +1967,15 @@ function abortableSleep(ms, signal) {
1698
1967
  );
1699
1968
  });
1700
1969
  }
1970
+ function agentStartBanner(binary) {
1971
+ return `printf " agentbox: starting ${binary} (first paint may take a few seconds)...\\r\\n"; `;
1972
+ }
1701
1973
  function buildCloudAttachInnerCommand(binary, extraArgs) {
1702
1974
  if (!extraArgs || extraArgs.length === 0) {
1703
- return `bash -lc exec\\ ${binary}`;
1975
+ return `bash -lc '${agentStartBanner(binary)}exec ${binary}'`;
1704
1976
  }
1705
1977
  const blob = Buffer.from(extraArgs.join("\n"), "utf8").toString("base64");
1706
- return `bash -lc 'mapfile -t A <<< "$(echo ${blob} | base64 -d)"; exec ${binary} "\${A[@]}"'`;
1978
+ return `bash -lc '${agentStartBanner(binary)}mapfile -t A <<< "$(echo ${blob} | base64 -d)"; exec ${binary} "\${A[@]}"'`;
1707
1979
  }
1708
1980
  async function cloudAgentAttach(args) {
1709
1981
  const provider = await providerForBox(args.box);
@@ -1817,7 +2089,7 @@ async function cloudAgentStartDetached(args) {
1817
2089
  }
1818
2090
  function runDetached(argv, env) {
1819
2091
  return new Promise((resolve) => {
1820
- const child = spawn3(argv[0], argv.slice(1), {
2092
+ const child = spawn4(argv[0], argv.slice(1), {
1821
2093
  stdio: "ignore",
1822
2094
  env: env ? { ...process.env, ...env } : process.env
1823
2095
  });
@@ -1857,4 +2129,4 @@ export {
1857
2129
  cloudAgentAttach,
1858
2130
  cloudAgentStartDetached
1859
2131
  };
1860
- //# sourceMappingURL=chunk-HFV6THYG.js.map
2132
+ //# sourceMappingURL=chunk-BYCLD6D6.js.map