@madarco/agentbox 0.8.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 (46) hide show
  1. package/dist/{_cloud-attach-T727ZPRV.js → _cloud-attach-ZXBCNWJX.js} +4 -4
  2. package/dist/{chunk-67N47KUS.js → chunk-BXQMIEHC.js} +106 -31
  3. package/dist/chunk-BXQMIEHC.js.map +1 -0
  4. package/dist/{chunk-FODMEHD3.js → chunk-GU5LW4B5.js} +341 -25
  5. package/dist/chunk-GU5LW4B5.js.map +1 -0
  6. package/dist/{chunk-BGK32PZE.js → chunk-KL36BRN4.js} +2 -2
  7. package/dist/chunk-KL36BRN4.js.map +1 -0
  8. package/dist/chunk-MTVI44DW.js +662 -0
  9. package/dist/chunk-MTVI44DW.js.map +1 -0
  10. package/dist/{chunk-6OZDFNBF.js → chunk-NCJP5MTN.js} +201 -44
  11. package/dist/chunk-NCJP5MTN.js.map +1 -0
  12. package/dist/{dist-LOZBWMBF.js → dist-32EZBYG4.js} +9 -3
  13. package/dist/{dist-L4LCG5SJ.js → dist-CX5CGVEB.js} +4 -4
  14. package/dist/{dist-ZODPD2I6.js → dist-GDHP34ZK.js} +8 -10
  15. package/dist/dist-GDHP34ZK.js.map +1 -0
  16. package/dist/dist-XML54CNB.js +849 -0
  17. package/dist/dist-XML54CNB.js.map +1 -0
  18. package/dist/index.js +636 -340
  19. package/dist/index.js.map +1 -1
  20. package/dist/{prepared-state-CL4CWXQA-ME4HSKDE.js → prepared-state-CL4CWXQA-H5THETIM.js} +2 -2
  21. package/package.json +7 -5
  22. package/runtime/docker/packages/ctl/dist/bin.cjs +98 -29
  23. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +15 -1
  24. package/runtime/hetzner/agentbox-vnc-start +15 -1
  25. package/runtime/hetzner/ctl.cjs +98 -29
  26. package/runtime/relay/bin.cjs +229 -37
  27. package/runtime/vercel/agentbox-checkpoint-cleanup +52 -0
  28. package/runtime/vercel/agentbox-codex-hooks.json +68 -0
  29. package/runtime/vercel/agentbox-open +28 -0
  30. package/runtime/vercel/agentbox-setup-skill.md +196 -0
  31. package/runtime/vercel/agentbox-vnc-start +91 -0
  32. package/runtime/vercel/claude-managed-settings.json +115 -0
  33. package/runtime/vercel/ctl.cjs +23466 -0
  34. package/runtime/vercel/custom-system-CLAUDE.md +50 -0
  35. package/runtime/vercel/gh-shim +263 -0
  36. package/runtime/vercel/git-shim +131 -0
  37. package/runtime/vercel/scripts/provision.sh +274 -0
  38. package/dist/chunk-67N47KUS.js.map +0 -1
  39. package/dist/chunk-6OZDFNBF.js.map +0 -1
  40. package/dist/chunk-BGK32PZE.js.map +0 -1
  41. package/dist/chunk-FODMEHD3.js.map +0 -1
  42. package/dist/dist-ZODPD2I6.js.map +0 -1
  43. /package/dist/{_cloud-attach-T727ZPRV.js.map → _cloud-attach-ZXBCNWJX.js.map} +0 -0
  44. /package/dist/{dist-LOZBWMBF.js.map → dist-32EZBYG4.js.map} +0 -0
  45. /package/dist/{dist-L4LCG5SJ.js.map → dist-CX5CGVEB.js.map} +0 -0
  46. /package/dist/{prepared-state-CL4CWXQA-ME4HSKDE.js.map → prepared-state-CL4CWXQA-H5THETIM.js.map} +0 -0
@@ -2,32 +2,37 @@
2
2
  import {
3
3
  DEFAULT_RELAY_PORT,
4
4
  readBoxStatus
5
- } from "./chunk-6OZDFNBF.js";
5
+ } from "./chunk-NCJP5MTN.js";
6
6
 
7
7
  // src/commands/_cloud-attach.ts
8
8
  import { spawn as spawn3 } from "child_process";
9
9
 
10
10
  // src/provider/registry.ts
11
- var KNOWN = ["docker", "daytona", "hetzner"];
11
+ var KNOWN = ["docker", "daytona", "hetzner", "vercel"];
12
12
  function isKnownProvider(name) {
13
13
  return KNOWN.includes(name);
14
14
  }
15
15
  async function getProvider(name) {
16
16
  switch (name) {
17
17
  case "docker": {
18
- const mod = await import("./dist-LOZBWMBF.js");
18
+ const mod = await import("./dist-32EZBYG4.js");
19
19
  return mod.dockerProvider;
20
20
  }
21
21
  case "daytona": {
22
- const mod = await import("./dist-L4LCG5SJ.js");
22
+ const mod = await import("./dist-CX5CGVEB.js");
23
23
  await mod.ensureDaytonaCredentials();
24
24
  return mod.daytonaProvider;
25
25
  }
26
26
  case "hetzner": {
27
- const mod = await import("./dist-ZODPD2I6.js");
27
+ const mod = await import("./dist-GDHP34ZK.js");
28
28
  await mod.ensureHetznerCredentials();
29
29
  return mod.hetznerProvider;
30
30
  }
31
+ case "vercel": {
32
+ const mod = await import("./dist-XML54CNB.js");
33
+ await mod.ensureVercelCredentials();
34
+ return mod.vercelProvider;
35
+ }
31
36
  default:
32
37
  throw new Error(`unknown sandbox provider: ${String(name)}`);
33
38
  }
@@ -179,6 +184,25 @@ function runQuiet(cmd, argv) {
179
184
  });
180
185
  }
181
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
+
182
206
  // src/wrapped-pty/input-router.ts
183
207
  var KEY_ENTER = 13;
184
208
  var KEY_LF = 10;
@@ -189,12 +213,58 @@ var KEY_Y_UP = 89;
189
213
  var KEY_N_LOW = 110;
190
214
  var KEY_N_UP = 78;
191
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
+ }
192
259
  var DEFAULT_LEADER_TIMEOUT_MS = 2e3;
193
260
  function createInputRouter(opts) {
194
261
  let active = null;
195
262
  let disposed = false;
196
263
  const leaderChords = opts.leaderChords ?? {};
197
264
  const leaderEnabled = Object.keys(leaderChords).length > 0;
265
+ const onPasteImage = opts.onPasteImage;
266
+ const pasteEnabled = typeof onPasteImage === "function";
267
+ let pasteInFlight = false;
198
268
  const leaderTimeoutMs = opts.leaderTimeoutMs ?? DEFAULT_LEADER_TIMEOUT_MS;
199
269
  const setTimer = opts.setTimer ?? ((ms, fn) => setTimeout(fn, ms));
200
270
  const clearTimer = opts.clearTimer ?? ((h) => clearTimeout(h));
@@ -272,25 +342,74 @@ function createInputRouter(opts) {
272
342
  return;
273
343
  }
274
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
+ };
275
354
  const feedSteady = (buf) => {
276
355
  let chunkStart = 0;
277
356
  const flushChunk = (end) => {
278
357
  if (end > chunkStart) opts.onForward(buf.subarray(chunkStart, end));
279
358
  chunkStart = end;
280
359
  };
281
- for (let i = 0; i < buf.length; i++) {
360
+ let i = 0;
361
+ while (i < buf.length) {
282
362
  const byte = buf[i];
283
- if (byte === void 0) continue;
363
+ if (byte === void 0) {
364
+ i++;
365
+ continue;
366
+ }
284
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
+ }
285
383
  resolveLeaderByte(byte);
286
- chunkStart = i + 1;
384
+ i += 1;
385
+ chunkStart = i;
287
386
  continue;
288
387
  }
289
- if (byte === KEY_LEADER) {
388
+ if (leaderEnabled && byte === KEY_LEADER) {
290
389
  flushChunk(i);
291
- chunkStart = i + 1;
292
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
+ }
293
404
  }
405
+ if (pasteEnabled && byte === KEY_CTRL_V) {
406
+ flushChunk(i);
407
+ i += 1;
408
+ chunkStart = i;
409
+ triggerPaste();
410
+ continue;
411
+ }
412
+ i += 1;
294
413
  }
295
414
  flushChunk(buf.length);
296
415
  };
@@ -314,7 +433,7 @@ function createInputRouter(opts) {
314
433
  }
315
434
  return;
316
435
  }
317
- if (!leaderEnabled) {
436
+ if (!leaderEnabled && !pasteEnabled) {
318
437
  opts.onForward(buf);
319
438
  return;
320
439
  }
@@ -880,14 +999,14 @@ async function runWrappedAttach(opts) {
880
999
  }
881
1000
  }
882
1001
  if (!process.stdout.isTTY || !process.stdin.isTTY) {
883
- return runFallback(command, opts.dockerArgv);
1002
+ return runFallback(command, opts.dockerArgv, opts.env);
884
1003
  }
885
1004
  const backend = await loadPtyBackend();
886
1005
  if (!backend) {
887
1006
  process.stderr.write(
888
1007
  "agentbox: permission prompts disabled (node-pty backend unavailable)\n"
889
1008
  );
890
- return runFallback(command, opts.dockerArgv);
1009
+ return runFallback(command, opts.dockerArgv, opts.env);
891
1010
  }
892
1011
  const cols = process.stdout.columns ?? 80;
893
1012
  const rows = process.stdout.rows ?? 24;
@@ -896,8 +1015,11 @@ async function runWrappedAttach(opts) {
896
1015
  name: "xterm-256color",
897
1016
  cols,
898
1017
  rows: innerRows,
899
- env: process.env
1018
+ env: opts.env ? { ...process.env, ...opts.env } : process.env
900
1019
  });
1020
+ pushTerminalTitle();
1021
+ let lastEmittedTitle = opts.boxName;
1022
+ setTerminalTitle(lastEmittedTitle);
901
1023
  const detachable = opts.detachable ?? opts.mode === "claude";
902
1024
  let leaderActive = false;
903
1025
  const buildIdle = (sessionTitle, claudeActivity) => ({
@@ -988,6 +1110,32 @@ async function runWrappedAttach(opts) {
988
1110
  recomputeFooter();
989
1111
  redrawFooter();
990
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
+ };
991
1139
  const router = createInputRouter({
992
1140
  onForward: (b) => {
993
1141
  pty.write(b.toString("utf8"));
@@ -1006,7 +1154,8 @@ async function runWrappedAttach(opts) {
1006
1154
  },
1007
1155
  onAction: (name) => {
1008
1156
  runAction(name);
1009
- }
1157
+ },
1158
+ onPasteImage: opts.onPasteImage ? handlePasteImage : void 0
1010
1159
  });
1011
1160
  if (process.stdin.isTTY) process.stdin.setRawMode(true);
1012
1161
  process.stdin.resume();
@@ -1067,8 +1216,14 @@ async function runWrappedAttach(opts) {
1067
1216
  name: opts.boxName,
1068
1217
  projectIndex: opts.projectIndex
1069
1218
  });
1070
- const nextTitle = status?.claude?.sessionTitle?.trim() || void 0;
1071
- 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
+ }
1072
1227
  if (nextTitle === lastSessionTitle && nextActivity === lastActivity) return;
1073
1228
  lastSessionTitle = nextTitle;
1074
1229
  lastActivity = nextActivity;
@@ -1107,16 +1262,165 @@ async function runWrappedAttach(opts) {
1107
1262
  process.stdout.write(
1108
1263
  "\x1B[r" + cursorMoveTo(rsFinal, 1) + `\x1B[2K` + cursorMoveTo(rsFinal, csFinal)
1109
1264
  );
1265
+ popTerminalTitle();
1110
1266
  if (exitCode === 0 && opts.detachNotice) {
1111
1267
  process.stdout.write("\x1B[1A\x1B[2K\r" + opts.detachNotice + "\n");
1112
1268
  }
1113
1269
  return exitCode;
1114
1270
  }
1115
- function runFallback(command, argv) {
1116
- 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
+ });
1117
1276
  return child.status ?? 0;
1118
1277
  }
1119
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
+
1120
1424
  // src/commands/_cloud-attach.ts
1121
1425
  var RELAY_HOST_URL = `http://127.0.0.1:${String(DEFAULT_RELAY_PORT)}`;
1122
1426
  function buildCloudAttachInnerCommand(binary, extraArgs) {
@@ -1124,7 +1428,7 @@ function buildCloudAttachInnerCommand(binary, extraArgs) {
1124
1428
  return `bash -lc exec\\ ${binary}`;
1125
1429
  }
1126
1430
  const blob = Buffer.from(extraArgs.join("\n"), "utf8").toString("base64");
1127
- 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[@]}"'`;
1128
1432
  }
1129
1433
  async function cloudAgentAttach(args) {
1130
1434
  const provider = await providerForBox(args.box);
@@ -1140,7 +1444,7 @@ async function cloudAgentAttach(args) {
1140
1444
  detached: true
1141
1445
  });
1142
1446
  try {
1143
- await runDetached(pre.argv);
1447
+ await runDetached(pre.argv, pre.env);
1144
1448
  } finally {
1145
1449
  if (pre.cleanup) await pre.cleanup();
1146
1450
  }
@@ -1149,27 +1453,33 @@ async function cloudAgentAttach(args) {
1149
1453
  sessionName: args.sessionName,
1150
1454
  command
1151
1455
  });
1456
+ const canPaste = args.mode === "claude" && await clipboardCaptureAvailable();
1152
1457
  try {
1153
1458
  const code = await runWrappedAttach({
1154
1459
  container: args.box.name,
1155
1460
  command: spec.argv[0],
1156
1461
  dockerArgv: spec.argv.slice(1),
1462
+ env: spec.env,
1157
1463
  relayBaseUrl: RELAY_HOST_URL,
1158
1464
  boxId: args.box.id,
1159
1465
  boxName: args.box.name,
1160
1466
  projectIndex: args.box.projectIndex,
1161
1467
  mode: args.mode,
1162
1468
  detachable: true,
1163
- openIn: safeOpenIn
1469
+ openIn: safeOpenIn,
1470
+ onPasteImage: canPaste ? () => pasteHostClipboardImage(provider, args.box) : void 0
1164
1471
  });
1165
1472
  process.exit(code);
1166
1473
  } finally {
1167
1474
  if (spec.cleanup) await spec.cleanup();
1168
1475
  }
1169
1476
  }
1170
- function runDetached(argv) {
1477
+ function runDetached(argv, env) {
1171
1478
  return new Promise((resolve) => {
1172
- const child = spawn3(argv[0], argv.slice(1), { stdio: "ignore" });
1479
+ const child = spawn3(argv[0], argv.slice(1), {
1480
+ stdio: "ignore",
1481
+ env: env ? { ...process.env, ...env } : process.env
1482
+ });
1173
1483
  child.on("error", () => resolve());
1174
1484
  child.on("exit", () => resolve());
1175
1485
  });
@@ -1182,8 +1492,12 @@ export {
1182
1492
  providerForCreate,
1183
1493
  loadPtyBackend,
1184
1494
  detectHostTerminal,
1495
+ setTerminalTitle,
1496
+ pushTerminalTitle,
1497
+ popTerminalTitle,
1185
1498
  NEW_BOX_ID,
1186
1499
  NEW_BOX_LABEL,
1500
+ stripTitleGlyph,
1187
1501
  sidebarLines,
1188
1502
  menuLines,
1189
1503
  lifecycleMenuLines,
@@ -1194,7 +1508,9 @@ export {
1194
1508
  subscribePrompts,
1195
1509
  postAnswer,
1196
1510
  runWrappedAttach,
1511
+ clipboardCaptureAvailable,
1512
+ pasteHostClipboardImage,
1197
1513
  buildCloudAttachInnerCommand,
1198
1514
  cloudAgentAttach
1199
1515
  };
1200
- //# sourceMappingURL=chunk-FODMEHD3.js.map
1516
+ //# sourceMappingURL=chunk-GU5LW4B5.js.map