@madarco/agentbox 0.7.0 → 0.9.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 (77) hide show
  1. package/dist/_cloud-attach-ZXBCNWJX.js +13 -0
  2. package/dist/{chunk-NW5NYTQM.js → chunk-BXQMIEHC.js} +459 -110
  3. package/dist/chunk-BXQMIEHC.js.map +1 -0
  4. package/dist/{chunk-UK72UQ5U.js → chunk-G3H2L3O2.js} +55 -4
  5. package/dist/chunk-G3H2L3O2.js.map +1 -0
  6. package/dist/{chunk-7KOEFGN2.js → chunk-GU5LW4B5.js} +385 -31
  7. package/dist/chunk-GU5LW4B5.js.map +1 -0
  8. package/dist/chunk-KL36BRN4.js +455 -0
  9. package/dist/chunk-KL36BRN4.js.map +1 -0
  10. package/dist/{chunk-V5KZGB5V.js → chunk-LEV3KICD.js} +18 -2
  11. package/dist/chunk-LEV3KICD.js.map +1 -0
  12. package/dist/chunk-MTVI44DW.js +662 -0
  13. package/dist/chunk-MTVI44DW.js.map +1 -0
  14. package/dist/{chunk-NAVL4R34.js → chunk-NCJP5MTN.js} +1281 -556
  15. package/dist/chunk-NCJP5MTN.js.map +1 -0
  16. package/dist/{cloud-poller-ZIWSADJB-JXFRJUEM.js → cloud-poller-SUNA6ZQC-2RG5WPRN.js} +2 -2
  17. package/dist/{dist-ETCFRVPA.js → dist-32EZBYG4.js} +50 -20
  18. package/dist/{dist-R67WMLCF.js → dist-CX5CGVEB.js} +120 -10
  19. package/dist/dist-CX5CGVEB.js.map +1 -0
  20. package/dist/{dist-QZGJIBT5.js → dist-GDHP34ZK.js} +141 -75
  21. package/dist/dist-GDHP34ZK.js.map +1 -0
  22. package/dist/dist-XML54CNB.js +849 -0
  23. package/dist/dist-XML54CNB.js.map +1 -0
  24. package/dist/index.js +3881 -867
  25. package/dist/index.js.map +1 -1
  26. package/dist/prepared-state-CL4CWXQA-H5THETIM.js +18 -0
  27. package/dist/prepared-state-CL4CWXQA-H5THETIM.js.map +1 -0
  28. package/package.json +7 -5
  29. package/runtime/daytona/custom-system-CLAUDE.md +39 -0
  30. package/runtime/docker/Dockerfile.box +22 -0
  31. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +1 -1
  32. package/runtime/docker/packages/ctl/dist/bin.cjs +1214 -98
  33. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-codex-hooks.json +66 -35
  34. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +15 -1
  35. package/runtime/docker/packages/sandbox-docker/scripts/claude-managed-settings.json +62 -1
  36. package/runtime/docker/packages/sandbox-docker/scripts/custom-system-CLAUDE.md +15 -4
  37. package/runtime/docker/packages/sandbox-docker/scripts/gh-shim +263 -0
  38. package/runtime/docker/packages/sandbox-docker/scripts/git-shim +131 -0
  39. package/runtime/docker/packages/sandbox-docker/scripts/opencode-agentbox-plugin.js +76 -0
  40. package/runtime/hetzner/agentbox-codex-hooks.json +66 -35
  41. package/runtime/hetzner/agentbox-setup-skill.md +1 -1
  42. package/runtime/hetzner/agentbox-vnc-start +15 -1
  43. package/runtime/hetzner/claude-managed-settings.json +62 -1
  44. package/runtime/hetzner/ctl.cjs +1214 -98
  45. package/runtime/hetzner/custom-system-CLAUDE.md +26 -14
  46. package/runtime/hetzner/gh-shim +263 -0
  47. package/runtime/hetzner/git-shim +131 -0
  48. package/runtime/hetzner/opencode-agentbox-plugin.js +76 -0
  49. package/runtime/hetzner/scripts/install-box.sh +11 -2
  50. package/runtime/relay/bin.cjs +1146 -63
  51. package/runtime/vercel/agentbox-checkpoint-cleanup +52 -0
  52. package/runtime/vercel/agentbox-codex-hooks.json +68 -0
  53. package/runtime/vercel/agentbox-open +28 -0
  54. package/runtime/vercel/agentbox-setup-skill.md +196 -0
  55. package/runtime/vercel/agentbox-vnc-start +91 -0
  56. package/runtime/vercel/claude-managed-settings.json +115 -0
  57. package/runtime/vercel/ctl.cjs +23466 -0
  58. package/runtime/vercel/custom-system-CLAUDE.md +50 -0
  59. package/runtime/vercel/gh-shim +263 -0
  60. package/runtime/vercel/git-shim +131 -0
  61. package/runtime/vercel/scripts/provision.sh +274 -0
  62. package/share/agentbox-setup/SKILL.md +1 -1
  63. package/share/host-skills/agentbox/SKILL.md +29 -0
  64. package/share/host-skills/agentbox-info/SKILL.md +211 -0
  65. package/share/host-skills/codex/agentbox.md +35 -0
  66. package/share/host-skills/opencode/agentbox.md +26 -0
  67. package/dist/_cloud-attach-DMVH6GWO.js +0 -12
  68. package/dist/chunk-7KOEFGN2.js.map +0 -1
  69. package/dist/chunk-NAVL4R34.js.map +0 -1
  70. package/dist/chunk-NW5NYTQM.js.map +0 -1
  71. package/dist/chunk-UK72UQ5U.js.map +0 -1
  72. package/dist/chunk-V5KZGB5V.js.map +0 -1
  73. package/dist/dist-QZGJIBT5.js.map +0 -1
  74. package/dist/dist-R67WMLCF.js.map +0 -1
  75. /package/dist/{_cloud-attach-DMVH6GWO.js.map → _cloud-attach-ZXBCNWJX.js.map} +0 -0
  76. /package/dist/{cloud-poller-ZIWSADJB-JXFRJUEM.js.map → cloud-poller-SUNA6ZQC-2RG5WPRN.js.map} +0 -0
  77. /package/dist/{dist-ETCFRVPA.js.map → dist-32EZBYG4.js.map} +0 -0
@@ -2,29 +2,37 @@
2
2
  import {
3
3
  DEFAULT_RELAY_PORT,
4
4
  readBoxStatus
5
- } from "./chunk-NAVL4R34.js";
5
+ } from "./chunk-NCJP5MTN.js";
6
+
7
+ // src/commands/_cloud-attach.ts
8
+ import { spawn as spawn3 } from "child_process";
6
9
 
7
10
  // src/provider/registry.ts
8
- var KNOWN = ["docker", "daytona", "hetzner"];
11
+ var KNOWN = ["docker", "daytona", "hetzner", "vercel"];
9
12
  function isKnownProvider(name) {
10
13
  return KNOWN.includes(name);
11
14
  }
12
15
  async function getProvider(name) {
13
16
  switch (name) {
14
17
  case "docker": {
15
- const mod = await import("./dist-ETCFRVPA.js");
18
+ const mod = await import("./dist-32EZBYG4.js");
16
19
  return mod.dockerProvider;
17
20
  }
18
21
  case "daytona": {
19
- const mod = await import("./dist-R67WMLCF.js");
22
+ const mod = await import("./dist-CX5CGVEB.js");
20
23
  await mod.ensureDaytonaCredentials();
21
24
  return mod.daytonaProvider;
22
25
  }
23
26
  case "hetzner": {
24
- const mod = await import("./dist-QZGJIBT5.js");
27
+ const mod = await import("./dist-GDHP34ZK.js");
25
28
  await mod.ensureHetznerCredentials();
26
29
  return mod.hetznerProvider;
27
30
  }
31
+ case "vercel": {
32
+ const mod = await import("./dist-XML54CNB.js");
33
+ await mod.ensureVercelCredentials();
34
+ return mod.vercelProvider;
35
+ }
28
36
  default:
29
37
  throw new Error(`unknown sandbox provider: ${String(name)}`);
30
38
  }
@@ -51,13 +59,13 @@ async function loadPtyBackend() {
51
59
  try {
52
60
  const ptyMod = await import("@homebridge/node-pty-prebuilt-multiarch");
53
61
  const xtermMod = await import("@xterm/headless");
54
- const spawn3 = ptyMod["spawn"] ?? ptyMod["default"]?.["spawn"];
62
+ const spawn4 = ptyMod["spawn"] ?? ptyMod["default"]?.["spawn"];
55
63
  const Terminal = xtermMod["Terminal"] ?? xtermMod["default"]?.["Terminal"];
56
- if (typeof spawn3 !== "function" || typeof Terminal !== "function") {
64
+ if (typeof spawn4 !== "function" || typeof Terminal !== "function") {
57
65
  return null;
58
66
  }
59
67
  return {
60
- ptySpawn: spawn3,
68
+ ptySpawn: spawn4,
61
69
  termCtor: Terminal
62
70
  };
63
71
  } catch {
@@ -116,23 +124,38 @@ async function spawnInITerm2(args) {
116
124
  const inner = shellJoin(args.argv);
117
125
  const cmdLine = `cd ${shellQuote(args.cwd)} && exec ${inner}`;
118
126
  const cmdLit = `"${appleScriptEscape(cmdLine)}"`;
119
- let script;
127
+ let lines;
120
128
  let noteKind;
121
129
  switch (args.mode) {
122
130
  case "split":
123
- script = `tell application "iTerm" to tell current session of current window to tell (split vertically with default profile) to write text ${cmdLit}`;
131
+ lines = [
132
+ 'tell application "iTerm"',
133
+ " tell current session of current window to set _s to (split vertically with default profile)",
134
+ ` tell _s to write text ${cmdLit}`,
135
+ "end tell"
136
+ ];
124
137
  noteKind = "iTerm2 split";
125
138
  break;
126
139
  case "tab":
127
- script = `tell application "iTerm" to tell current window to create tab with default profile command ${cmdLit}`;
140
+ lines = [
141
+ 'tell application "iTerm"',
142
+ " tell current window to set _t to (create tab with default profile)",
143
+ ` tell current session of _t to write text ${cmdLit}`,
144
+ "end tell"
145
+ ];
128
146
  noteKind = "iTerm2 tab";
129
147
  break;
130
148
  case "window":
131
- script = `tell application "iTerm" to create window with default profile command ${cmdLit}`;
149
+ lines = [
150
+ 'tell application "iTerm"',
151
+ " set _w to (create window with default profile)",
152
+ ` tell current session of _w to write text ${cmdLit}`,
153
+ "end tell"
154
+ ];
132
155
  noteKind = "iTerm2 window";
133
156
  break;
134
157
  }
135
- const r = await runQuiet("osascript", ["-e", script]);
158
+ const r = await runQuiet("osascript", ["-e", lines.join("\n")]);
136
159
  if (r.code !== 0) {
137
160
  return {
138
161
  launched: false,
@@ -161,6 +184,25 @@ function runQuiet(cmd, argv) {
161
184
  });
162
185
  }
163
186
 
187
+ // src/terminal/title.ts
188
+ var ESC = "\x1B";
189
+ var BEL = "\x07";
190
+ function sanitize(title) {
191
+ return title.replace(/[\x00-\x1f\x7f]/g, " ").trim();
192
+ }
193
+ function setTerminalTitle(title, stream = process.stdout) {
194
+ if (!stream.isTTY) return;
195
+ stream.write(`${ESC}]0;${sanitize(title)}${BEL}`);
196
+ }
197
+ function pushTerminalTitle(stream = process.stdout) {
198
+ if (!stream.isTTY) return;
199
+ stream.write(`${ESC}[22;2t`);
200
+ }
201
+ function popTerminalTitle(stream = process.stdout) {
202
+ if (!stream.isTTY) return;
203
+ stream.write(`${ESC}[23;2t`);
204
+ }
205
+
164
206
  // src/wrapped-pty/input-router.ts
165
207
  var KEY_ENTER = 13;
166
208
  var KEY_LF = 10;
@@ -171,12 +213,58 @@ var KEY_Y_UP = 89;
171
213
  var KEY_N_LOW = 110;
172
214
  var KEY_N_UP = 78;
173
215
  var KEY_LEADER = 1;
216
+ var KEY_CTRL_V = 22;
217
+ var KEY_A_LOW = 97;
218
+ function parseCsiKey(buf, i) {
219
+ if (buf[i] !== KEY_ESC || buf[i + 1] !== 91) return null;
220
+ const params = [];
221
+ let val = -1;
222
+ for (let j = i + 2; j < buf.length; j++) {
223
+ const b = buf[j];
224
+ if (b !== void 0 && b >= 48 && b <= 57) {
225
+ val = (val < 0 ? 0 : val) * 10 + (b - 48);
226
+ continue;
227
+ }
228
+ if (b === 59) {
229
+ params.push(val);
230
+ val = -1;
231
+ continue;
232
+ }
233
+ if (b === 58) {
234
+ params.push(val);
235
+ val = -1;
236
+ while (j + 1 < buf.length && buf[j + 1] !== 59 && buf[j + 1] !== 117 && buf[j + 1] !== 126) {
237
+ j++;
238
+ }
239
+ continue;
240
+ }
241
+ if (b === 117 || b === 126) {
242
+ if (val >= 0) params.push(val);
243
+ const len = j - i + 1;
244
+ const modsToCtrl = (m) => (m - 1 & 4) !== 0;
245
+ if (b === 117) {
246
+ const code2 = params[0];
247
+ if (code2 === void 0 || code2 < 0) return null;
248
+ return { len, code: code2, ctrl: modsToCtrl(params[1] ?? 1) };
249
+ }
250
+ if (params[0] !== 27) return null;
251
+ const code = params[2];
252
+ if (code === void 0 || code < 0) return null;
253
+ return { len, code, ctrl: modsToCtrl(params[1] ?? 1) };
254
+ }
255
+ return null;
256
+ }
257
+ return null;
258
+ }
174
259
  var DEFAULT_LEADER_TIMEOUT_MS = 2e3;
175
260
  function createInputRouter(opts) {
176
261
  let active = null;
177
262
  let disposed = false;
178
263
  const leaderChords = opts.leaderChords ?? {};
179
264
  const leaderEnabled = Object.keys(leaderChords).length > 0;
265
+ const onPasteImage = opts.onPasteImage;
266
+ const pasteEnabled = typeof onPasteImage === "function";
267
+ let pasteInFlight = false;
180
268
  const leaderTimeoutMs = opts.leaderTimeoutMs ?? DEFAULT_LEADER_TIMEOUT_MS;
181
269
  const setTimer = opts.setTimer ?? ((ms, fn) => setTimeout(fn, ms));
182
270
  const clearTimer = opts.clearTimer ?? ((h) => clearTimeout(h));
@@ -254,25 +342,74 @@ function createInputRouter(opts) {
254
342
  return;
255
343
  }
256
344
  };
345
+ const triggerPaste = () => {
346
+ if (pasteInFlight) return;
347
+ pasteInFlight = true;
348
+ const done = () => {
349
+ pasteInFlight = false;
350
+ if (!disposed) opts.onForward(Buffer.from([KEY_CTRL_V]));
351
+ };
352
+ void Promise.resolve().then(() => onPasteImage?.()).then(done, done);
353
+ };
257
354
  const feedSteady = (buf) => {
258
355
  let chunkStart = 0;
259
356
  const flushChunk = (end) => {
260
357
  if (end > chunkStart) opts.onForward(buf.subarray(chunkStart, end));
261
358
  chunkStart = end;
262
359
  };
263
- for (let i = 0; i < buf.length; i++) {
360
+ let i = 0;
361
+ while (i < buf.length) {
264
362
  const byte = buf[i];
265
- if (byte === void 0) continue;
363
+ if (byte === void 0) {
364
+ i++;
365
+ continue;
366
+ }
266
367
  if (leader) {
368
+ const k = parseCsiKey(buf, i);
369
+ if (k) {
370
+ if (k.ctrl && k.code === KEY_A_LOW) {
371
+ exitLeader();
372
+ opts.onForward(Buffer.from([KEY_LEADER]));
373
+ } else {
374
+ const action = leaderChords[String.fromCharCode(k.code).toLowerCase()];
375
+ exitLeader();
376
+ if (action) opts.onAction?.(action);
377
+ else opts.onForward(buf.subarray(i, i + k.len));
378
+ }
379
+ i += k.len;
380
+ chunkStart = i;
381
+ continue;
382
+ }
267
383
  resolveLeaderByte(byte);
268
- chunkStart = i + 1;
384
+ i += 1;
385
+ chunkStart = i;
269
386
  continue;
270
387
  }
271
- if (byte === KEY_LEADER) {
388
+ if (leaderEnabled && byte === KEY_LEADER) {
272
389
  flushChunk(i);
273
- chunkStart = i + 1;
274
390
  enterLeader();
391
+ i += 1;
392
+ chunkStart = i;
393
+ continue;
394
+ }
395
+ if (leaderEnabled && byte === KEY_ESC) {
396
+ const k = parseCsiKey(buf, i);
397
+ if (k && k.ctrl && k.code === KEY_A_LOW) {
398
+ flushChunk(i);
399
+ enterLeader();
400
+ i += k.len;
401
+ chunkStart = i;
402
+ continue;
403
+ }
404
+ }
405
+ if (pasteEnabled && byte === KEY_CTRL_V) {
406
+ flushChunk(i);
407
+ i += 1;
408
+ chunkStart = i;
409
+ triggerPaste();
410
+ continue;
275
411
  }
412
+ i += 1;
276
413
  }
277
414
  flushChunk(buf.length);
278
415
  };
@@ -296,7 +433,7 @@ function createInputRouter(opts) {
296
433
  }
297
434
  return;
298
435
  }
299
- if (!leaderEnabled) {
436
+ if (!leaderEnabled && !pasteEnabled) {
300
437
  opts.onForward(buf);
301
438
  return;
302
439
  }
@@ -862,14 +999,14 @@ async function runWrappedAttach(opts) {
862
999
  }
863
1000
  }
864
1001
  if (!process.stdout.isTTY || !process.stdin.isTTY) {
865
- return runFallback(command, opts.dockerArgv);
1002
+ return runFallback(command, opts.dockerArgv, opts.env);
866
1003
  }
867
1004
  const backend = await loadPtyBackend();
868
1005
  if (!backend) {
869
1006
  process.stderr.write(
870
1007
  "agentbox: permission prompts disabled (node-pty backend unavailable)\n"
871
1008
  );
872
- return runFallback(command, opts.dockerArgv);
1009
+ return runFallback(command, opts.dockerArgv, opts.env);
873
1010
  }
874
1011
  const cols = process.stdout.columns ?? 80;
875
1012
  const rows = process.stdout.rows ?? 24;
@@ -878,8 +1015,11 @@ async function runWrappedAttach(opts) {
878
1015
  name: "xterm-256color",
879
1016
  cols,
880
1017
  rows: innerRows,
881
- env: process.env
1018
+ env: opts.env ? { ...process.env, ...opts.env } : process.env
882
1019
  });
1020
+ pushTerminalTitle();
1021
+ let lastEmittedTitle = opts.boxName;
1022
+ setTerminalTitle(lastEmittedTitle);
883
1023
  const detachable = opts.detachable ?? opts.mode === "claude";
884
1024
  let leaderActive = false;
885
1025
  const buildIdle = (sessionTitle, claudeActivity) => ({
@@ -970,6 +1110,32 @@ async function runWrappedAttach(opts) {
970
1110
  recomputeFooter();
971
1111
  redrawFooter();
972
1112
  };
1113
+ const handlePasteImage = async () => {
1114
+ if (!opts.onPasteImage) return;
1115
+ if (flashTimer) {
1116
+ clearTimeout(flashTimer);
1117
+ flashTimer = null;
1118
+ }
1119
+ flashMessage = "Pasting image\u2026";
1120
+ recomputeFooter();
1121
+ redrawFooter();
1122
+ let result = "error";
1123
+ try {
1124
+ result = await opts.onPasteImage();
1125
+ } catch (e) {
1126
+ logErr(`paste-image failed: ${e.message}`);
1127
+ }
1128
+ flashMessage = result === "pasted" ? "Image pasted" : result === "no-image" ? "No image in clipboard" : "Image paste failed";
1129
+ flashTimer = setTimeout(() => {
1130
+ flashTimer = null;
1131
+ flashMessage = null;
1132
+ recomputeFooter();
1133
+ redrawFooter();
1134
+ }, FLASH_DURATION_MS);
1135
+ if (typeof flashTimer.unref === "function") flashTimer.unref();
1136
+ recomputeFooter();
1137
+ redrawFooter();
1138
+ };
973
1139
  const router = createInputRouter({
974
1140
  onForward: (b) => {
975
1141
  pty.write(b.toString("utf8"));
@@ -988,7 +1154,8 @@ async function runWrappedAttach(opts) {
988
1154
  },
989
1155
  onAction: (name) => {
990
1156
  runAction(name);
991
- }
1157
+ },
1158
+ onPasteImage: opts.onPasteImage ? handlePasteImage : void 0
992
1159
  });
993
1160
  if (process.stdin.isTTY) process.stdin.setRawMode(true);
994
1161
  process.stdin.resume();
@@ -1049,8 +1216,14 @@ async function runWrappedAttach(opts) {
1049
1216
  name: opts.boxName,
1050
1217
  projectIndex: opts.projectIndex
1051
1218
  });
1052
- const nextTitle = status?.claude?.sessionTitle?.trim() || void 0;
1053
- const nextActivity = status?.claude?.state || void 0;
1219
+ const body = opts.mode === "codex" ? status?.codex : opts.mode === "opencode" ? status?.opencode : opts.mode === "shell" ? void 0 : status?.claude;
1220
+ const nextTitle = body?.sessionTitle?.trim() || void 0;
1221
+ const nextActivity = body?.state || void 0;
1222
+ const desiredTitle = nextTitle ?? opts.boxName;
1223
+ if (desiredTitle !== lastEmittedTitle) {
1224
+ lastEmittedTitle = desiredTitle;
1225
+ setTerminalTitle(desiredTitle);
1226
+ }
1054
1227
  if (nextTitle === lastSessionTitle && nextActivity === lastActivity) return;
1055
1228
  lastSessionTitle = nextTitle;
1056
1229
  lastActivity = nextActivity;
@@ -1089,16 +1262,165 @@ async function runWrappedAttach(opts) {
1089
1262
  process.stdout.write(
1090
1263
  "\x1B[r" + cursorMoveTo(rsFinal, 1) + `\x1B[2K` + cursorMoveTo(rsFinal, csFinal)
1091
1264
  );
1265
+ popTerminalTitle();
1092
1266
  if (exitCode === 0 && opts.detachNotice) {
1093
1267
  process.stdout.write("\x1B[1A\x1B[2K\r" + opts.detachNotice + "\n");
1094
1268
  }
1095
1269
  return exitCode;
1096
1270
  }
1097
- function runFallback(command, argv) {
1098
- const child = spawnSync(command, argv, { stdio: "inherit" });
1271
+ function runFallback(command, argv, env) {
1272
+ const child = spawnSync(command, argv, {
1273
+ stdio: "inherit",
1274
+ env: env ? { ...process.env, ...env } : process.env
1275
+ });
1099
1276
  return child.status ?? 0;
1100
1277
  }
1101
1278
 
1279
+ // src/lib/paste-image.ts
1280
+ import { rm as rm2 } from "fs/promises";
1281
+ import { dirname } from "path";
1282
+
1283
+ // src/lib/host-clipboard.ts
1284
+ import { mkdtemp, rm, stat, writeFile } from "fs/promises";
1285
+ import { tmpdir } from "os";
1286
+ import { join } from "path";
1287
+ import { execa } from "execa";
1288
+ async function captureClipboardImage() {
1289
+ if (process.platform !== "darwin" && process.platform !== "linux") return null;
1290
+ const dir = await mkdtemp(join(tmpdir(), "agentbox-clip-"));
1291
+ const pngPath = join(dir, "clip.png");
1292
+ const ok = process.platform === "darwin" ? await captureDarwin(dir, pngPath) : await captureLinux(pngPath);
1293
+ if (ok) return pngPath;
1294
+ await rm(dir, { recursive: true, force: true }).catch(() => {
1295
+ });
1296
+ return null;
1297
+ }
1298
+ async function clipboardCaptureAvailable() {
1299
+ if (process.platform === "darwin") return true;
1300
+ if (process.platform === "linux") return await linuxClipboardTool() !== null;
1301
+ return false;
1302
+ }
1303
+ function captureScriptArgs(pngPath, tiffPath) {
1304
+ return [
1305
+ "try",
1306
+ " set theData to (the clipboard as \xABclass PNGf\xBB)",
1307
+ ` set fh to open for access (POSIX file ${JSON.stringify(pngPath)}) with write permission`,
1308
+ " set eof fh to 0",
1309
+ " write theData to fh",
1310
+ " close access fh",
1311
+ ' return "PNG"',
1312
+ "on error",
1313
+ " try",
1314
+ " set theData to (the clipboard as \xABclass TIFF\xBB)",
1315
+ ` set fh to open for access (POSIX file ${JSON.stringify(tiffPath)}) with write permission`,
1316
+ " set eof fh to 0",
1317
+ " write theData to fh",
1318
+ " close access fh",
1319
+ ' return "TIFF"',
1320
+ " on error",
1321
+ ' return "NONE"',
1322
+ " end try",
1323
+ "end try"
1324
+ ].map((line) => ["-e", line]).flat();
1325
+ }
1326
+ async function captureDarwin(dir, pngPath) {
1327
+ const tiffPath = join(dir, "clip.tiff");
1328
+ const res = await execa("osascript", captureScriptArgs(pngPath, tiffPath), {
1329
+ reject: false
1330
+ });
1331
+ const kind = res.stdout.trim();
1332
+ if (kind === "PNG") return fileHasBytes(pngPath);
1333
+ if (kind === "TIFF" && await fileHasBytes(tiffPath)) {
1334
+ const conv = await execa(
1335
+ "sips",
1336
+ ["-s", "format", "png", tiffPath, "--out", pngPath],
1337
+ { reject: false }
1338
+ );
1339
+ if (conv.exitCode === 0) return fileHasBytes(pngPath);
1340
+ }
1341
+ return false;
1342
+ }
1343
+ async function linuxClipboardTool() {
1344
+ if (process.env["WAYLAND_DISPLAY"] && await hasCmd("wl-paste")) return "wayland";
1345
+ if (process.env["DISPLAY"] && await hasCmd("xclip")) return "x11";
1346
+ return null;
1347
+ }
1348
+ async function captureLinux(pngPath) {
1349
+ const tool = await linuxClipboardTool();
1350
+ if (!tool) return false;
1351
+ let buf = null;
1352
+ if (tool === "wayland") {
1353
+ const types = await execa("wl-paste", ["--list-types"], { reject: false });
1354
+ if (types.exitCode !== 0 || !/image\/png/i.test(types.stdout)) return false;
1355
+ const r = await execa("wl-paste", ["--type", "image/png"], {
1356
+ encoding: "buffer",
1357
+ reject: false
1358
+ });
1359
+ if (r.exitCode === 0) buf = asBuffer(r.stdout);
1360
+ } else {
1361
+ const sel = ["-selection", "clipboard"];
1362
+ const targets = await execa("xclip", [...sel, "-t", "TARGETS", "-o"], {
1363
+ reject: false
1364
+ });
1365
+ if (targets.exitCode !== 0 || !/image\/png/i.test(targets.stdout)) return false;
1366
+ const r = await execa("xclip", [...sel, "-t", "image/png", "-o"], {
1367
+ encoding: "buffer",
1368
+ reject: false
1369
+ });
1370
+ if (r.exitCode === 0) buf = asBuffer(r.stdout);
1371
+ }
1372
+ if (!buf || !isPng(buf)) return false;
1373
+ await writeFile(pngPath, buf);
1374
+ return true;
1375
+ }
1376
+ async function hasCmd(cmd) {
1377
+ const r = await execa("sh", ["-c", `command -v ${cmd}`], { reject: false });
1378
+ return r.exitCode === 0;
1379
+ }
1380
+ function asBuffer(out) {
1381
+ if (Buffer.isBuffer(out)) return out;
1382
+ if (out instanceof Uint8Array) return Buffer.from(out);
1383
+ return null;
1384
+ }
1385
+ function isPng(buf) {
1386
+ return buf.length >= 8 && buf[0] === 137 && buf[1] === 80 && buf[2] === 78 && buf[3] === 71;
1387
+ }
1388
+ async function fileHasBytes(path) {
1389
+ try {
1390
+ const s = await stat(path);
1391
+ return s.isFile() && s.size > 0;
1392
+ } catch {
1393
+ return false;
1394
+ }
1395
+ }
1396
+
1397
+ // src/lib/paste-image.ts
1398
+ function loadClipboardScript(boxPngPath) {
1399
+ return [
1400
+ "pgrep -x Xvnc >/dev/null 2>&1 || /usr/local/bin/agentbox-vnc-start >/dev/null 2>&1 || true",
1401
+ "for _ in $(seq 1 30); do [ -S /tmp/.X11-unix/X1 ] && break; sleep 0.2; done",
1402
+ `setsid sh -c 'DISPLAY=:1 xclip -selection clipboard -t image/png -i ${boxPngPath}' </dev/null >/dev/null 2>&1 &`
1403
+ ].join("; ");
1404
+ }
1405
+ async function pasteHostClipboardImage(provider, box) {
1406
+ if (typeof provider.uploadPath !== "function") return "error";
1407
+ const hostPng = await captureClipboardImage();
1408
+ if (!hostPng) return "no-image";
1409
+ const boxPng = `/tmp/agentbox-clip-${String(Date.now())}.png`;
1410
+ try {
1411
+ await provider.uploadPath(box, hostPng, boxPng);
1412
+ await provider.exec(box, ["sh", "-lc", loadClipboardScript(boxPng)], {
1413
+ user: "vscode"
1414
+ });
1415
+ return "pasted";
1416
+ } catch {
1417
+ return "error";
1418
+ } finally {
1419
+ await rm2(dirname(hostPng), { recursive: true, force: true }).catch(() => {
1420
+ });
1421
+ }
1422
+ }
1423
+
1102
1424
  // src/commands/_cloud-attach.ts
1103
1425
  var RELAY_HOST_URL = `http://127.0.0.1:${String(DEFAULT_RELAY_PORT)}`;
1104
1426
  function buildCloudAttachInnerCommand(binary, extraArgs) {
@@ -1106,7 +1428,7 @@ function buildCloudAttachInnerCommand(binary, extraArgs) {
1106
1428
  return `bash -lc exec\\ ${binary}`;
1107
1429
  }
1108
1430
  const blob = Buffer.from(extraArgs.join("\n"), "utf8").toString("base64");
1109
- return `bash -lc 'mapfile -t A < <(echo ${blob} | base64 -d); exec ${binary} "\${A[@]}"'`;
1431
+ return `bash -lc 'mapfile -t A <<< "$(echo ${blob} | base64 -d)"; exec ${binary} "\${A[@]}"'`;
1110
1432
  }
1111
1433
  async function cloudAgentAttach(args) {
1112
1434
  const provider = await providerForBox(args.box);
@@ -1114,29 +1436,54 @@ async function cloudAgentAttach(args) {
1114
1436
  throw new Error(`provider '${provider.name}' does not support interactive attach`);
1115
1437
  }
1116
1438
  const command = buildCloudAttachInnerCommand(args.binary, args.extraArgs);
1439
+ const safeOpenIn = args.box.provider === "daytona" ? "same" : args.openIn;
1440
+ if (safeOpenIn && safeOpenIn !== "same" && args.extraArgs && args.extraArgs.length > 0) {
1441
+ const pre = await provider.buildAttach(args.box, "agent", {
1442
+ sessionName: args.sessionName,
1443
+ command,
1444
+ detached: true
1445
+ });
1446
+ try {
1447
+ await runDetached(pre.argv, pre.env);
1448
+ } finally {
1449
+ if (pre.cleanup) await pre.cleanup();
1450
+ }
1451
+ }
1117
1452
  const spec = await provider.buildAttach(args.box, "agent", {
1118
1453
  sessionName: args.sessionName,
1119
1454
  command
1120
1455
  });
1121
- const safeOpenIn = args.box.provider === "daytona" ? "same" : args.openIn;
1456
+ const canPaste = args.mode === "claude" && await clipboardCaptureAvailable();
1122
1457
  try {
1123
1458
  const code = await runWrappedAttach({
1124
1459
  container: args.box.name,
1125
1460
  command: spec.argv[0],
1126
1461
  dockerArgv: spec.argv.slice(1),
1462
+ env: spec.env,
1127
1463
  relayBaseUrl: RELAY_HOST_URL,
1128
1464
  boxId: args.box.id,
1129
1465
  boxName: args.box.name,
1130
1466
  projectIndex: args.box.projectIndex,
1131
1467
  mode: args.mode,
1132
1468
  detachable: true,
1133
- openIn: safeOpenIn
1469
+ openIn: safeOpenIn,
1470
+ onPasteImage: canPaste ? () => pasteHostClipboardImage(provider, args.box) : void 0
1134
1471
  });
1135
1472
  process.exit(code);
1136
1473
  } finally {
1137
1474
  if (spec.cleanup) await spec.cleanup();
1138
1475
  }
1139
1476
  }
1477
+ function runDetached(argv, env) {
1478
+ return new Promise((resolve) => {
1479
+ const child = spawn3(argv[0], argv.slice(1), {
1480
+ stdio: "ignore",
1481
+ env: env ? { ...process.env, ...env } : process.env
1482
+ });
1483
+ child.on("error", () => resolve());
1484
+ child.on("exit", () => resolve());
1485
+ });
1486
+ }
1140
1487
 
1141
1488
  export {
1142
1489
  isKnownProvider,
@@ -1144,8 +1491,13 @@ export {
1144
1491
  providerForBox,
1145
1492
  providerForCreate,
1146
1493
  loadPtyBackend,
1494
+ detectHostTerminal,
1495
+ setTerminalTitle,
1496
+ pushTerminalTitle,
1497
+ popTerminalTitle,
1147
1498
  NEW_BOX_ID,
1148
1499
  NEW_BOX_LABEL,
1500
+ stripTitleGlyph,
1149
1501
  sidebarLines,
1150
1502
  menuLines,
1151
1503
  lifecycleMenuLines,
@@ -1156,7 +1508,9 @@ export {
1156
1508
  subscribePrompts,
1157
1509
  postAnswer,
1158
1510
  runWrappedAttach,
1511
+ clipboardCaptureAvailable,
1512
+ pasteHostClipboardImage,
1159
1513
  buildCloudAttachInnerCommand,
1160
1514
  cloudAgentAttach
1161
1515
  };
1162
- //# sourceMappingURL=chunk-7KOEFGN2.js.map
1516
+ //# sourceMappingURL=chunk-GU5LW4B5.js.map