@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.
- package/dist/{_cloud-attach-T727ZPRV.js → _cloud-attach-ZXBCNWJX.js} +4 -4
- package/dist/{chunk-67N47KUS.js → chunk-BXQMIEHC.js} +106 -31
- package/dist/chunk-BXQMIEHC.js.map +1 -0
- package/dist/{chunk-FODMEHD3.js → chunk-GU5LW4B5.js} +341 -25
- package/dist/chunk-GU5LW4B5.js.map +1 -0
- package/dist/{chunk-BGK32PZE.js → chunk-KL36BRN4.js} +2 -2
- package/dist/chunk-KL36BRN4.js.map +1 -0
- package/dist/chunk-MTVI44DW.js +662 -0
- package/dist/chunk-MTVI44DW.js.map +1 -0
- package/dist/{chunk-6OZDFNBF.js → chunk-NCJP5MTN.js} +201 -44
- package/dist/chunk-NCJP5MTN.js.map +1 -0
- package/dist/{dist-LOZBWMBF.js → dist-32EZBYG4.js} +9 -3
- package/dist/{dist-L4LCG5SJ.js → dist-CX5CGVEB.js} +4 -4
- package/dist/{dist-ZODPD2I6.js → dist-GDHP34ZK.js} +8 -10
- package/dist/dist-GDHP34ZK.js.map +1 -0
- package/dist/dist-XML54CNB.js +849 -0
- package/dist/dist-XML54CNB.js.map +1 -0
- package/dist/index.js +636 -340
- package/dist/index.js.map +1 -1
- package/dist/{prepared-state-CL4CWXQA-ME4HSKDE.js → prepared-state-CL4CWXQA-H5THETIM.js} +2 -2
- package/package.json +7 -5
- package/runtime/docker/packages/ctl/dist/bin.cjs +98 -29
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +15 -1
- package/runtime/hetzner/agentbox-vnc-start +15 -1
- package/runtime/hetzner/ctl.cjs +98 -29
- package/runtime/relay/bin.cjs +229 -37
- package/runtime/vercel/agentbox-checkpoint-cleanup +52 -0
- package/runtime/vercel/agentbox-codex-hooks.json +68 -0
- package/runtime/vercel/agentbox-open +28 -0
- package/runtime/vercel/agentbox-setup-skill.md +196 -0
- package/runtime/vercel/agentbox-vnc-start +91 -0
- package/runtime/vercel/claude-managed-settings.json +115 -0
- package/runtime/vercel/ctl.cjs +23466 -0
- package/runtime/vercel/custom-system-CLAUDE.md +50 -0
- package/runtime/vercel/gh-shim +263 -0
- package/runtime/vercel/git-shim +131 -0
- package/runtime/vercel/scripts/provision.sh +274 -0
- package/dist/chunk-67N47KUS.js.map +0 -1
- package/dist/chunk-6OZDFNBF.js.map +0 -1
- package/dist/chunk-BGK32PZE.js.map +0 -1
- package/dist/chunk-FODMEHD3.js.map +0 -1
- package/dist/dist-ZODPD2I6.js.map +0 -1
- /package/dist/{_cloud-attach-T727ZPRV.js.map → _cloud-attach-ZXBCNWJX.js.map} +0 -0
- /package/dist/{dist-LOZBWMBF.js.map → dist-32EZBYG4.js.map} +0 -0
- /package/dist/{dist-L4LCG5SJ.js.map → dist-CX5CGVEB.js.map} +0 -0
- /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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
360
|
+
let i = 0;
|
|
361
|
+
while (i < buf.length) {
|
|
282
362
|
const byte = buf[i];
|
|
283
|
-
if (byte === void 0)
|
|
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
|
-
|
|
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
|
|
1071
|
-
const
|
|
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, {
|
|
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
|
|
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), {
|
|
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-
|
|
1516
|
+
//# sourceMappingURL=chunk-GU5LW4B5.js.map
|