@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.
- package/dist/_cloud-attach-ZXBCNWJX.js +13 -0
- package/dist/{chunk-NW5NYTQM.js → chunk-BXQMIEHC.js} +459 -110
- package/dist/chunk-BXQMIEHC.js.map +1 -0
- package/dist/{chunk-UK72UQ5U.js → chunk-G3H2L3O2.js} +55 -4
- package/dist/chunk-G3H2L3O2.js.map +1 -0
- package/dist/{chunk-7KOEFGN2.js → chunk-GU5LW4B5.js} +385 -31
- package/dist/chunk-GU5LW4B5.js.map +1 -0
- package/dist/chunk-KL36BRN4.js +455 -0
- package/dist/chunk-KL36BRN4.js.map +1 -0
- package/dist/{chunk-V5KZGB5V.js → chunk-LEV3KICD.js} +18 -2
- package/dist/chunk-LEV3KICD.js.map +1 -0
- package/dist/chunk-MTVI44DW.js +662 -0
- package/dist/chunk-MTVI44DW.js.map +1 -0
- package/dist/{chunk-NAVL4R34.js → chunk-NCJP5MTN.js} +1281 -556
- package/dist/chunk-NCJP5MTN.js.map +1 -0
- package/dist/{cloud-poller-ZIWSADJB-JXFRJUEM.js → cloud-poller-SUNA6ZQC-2RG5WPRN.js} +2 -2
- package/dist/{dist-ETCFRVPA.js → dist-32EZBYG4.js} +50 -20
- package/dist/{dist-R67WMLCF.js → dist-CX5CGVEB.js} +120 -10
- package/dist/dist-CX5CGVEB.js.map +1 -0
- package/dist/{dist-QZGJIBT5.js → dist-GDHP34ZK.js} +141 -75
- 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 +3881 -867
- package/dist/index.js.map +1 -1
- package/dist/prepared-state-CL4CWXQA-H5THETIM.js +18 -0
- package/dist/prepared-state-CL4CWXQA-H5THETIM.js.map +1 -0
- package/package.json +7 -5
- package/runtime/daytona/custom-system-CLAUDE.md +39 -0
- package/runtime/docker/Dockerfile.box +22 -0
- package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +1 -1
- package/runtime/docker/packages/ctl/dist/bin.cjs +1214 -98
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-codex-hooks.json +66 -35
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +15 -1
- package/runtime/docker/packages/sandbox-docker/scripts/claude-managed-settings.json +62 -1
- package/runtime/docker/packages/sandbox-docker/scripts/custom-system-CLAUDE.md +15 -4
- package/runtime/docker/packages/sandbox-docker/scripts/gh-shim +263 -0
- package/runtime/docker/packages/sandbox-docker/scripts/git-shim +131 -0
- package/runtime/docker/packages/sandbox-docker/scripts/opencode-agentbox-plugin.js +76 -0
- package/runtime/hetzner/agentbox-codex-hooks.json +66 -35
- package/runtime/hetzner/agentbox-setup-skill.md +1 -1
- package/runtime/hetzner/agentbox-vnc-start +15 -1
- package/runtime/hetzner/claude-managed-settings.json +62 -1
- package/runtime/hetzner/ctl.cjs +1214 -98
- package/runtime/hetzner/custom-system-CLAUDE.md +26 -14
- package/runtime/hetzner/gh-shim +263 -0
- package/runtime/hetzner/git-shim +131 -0
- package/runtime/hetzner/opencode-agentbox-plugin.js +76 -0
- package/runtime/hetzner/scripts/install-box.sh +11 -2
- package/runtime/relay/bin.cjs +1146 -63
- 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/share/agentbox-setup/SKILL.md +1 -1
- package/share/host-skills/agentbox/SKILL.md +29 -0
- package/share/host-skills/agentbox-info/SKILL.md +211 -0
- package/share/host-skills/codex/agentbox.md +35 -0
- package/share/host-skills/opencode/agentbox.md +26 -0
- package/dist/_cloud-attach-DMVH6GWO.js +0 -12
- package/dist/chunk-7KOEFGN2.js.map +0 -1
- package/dist/chunk-NAVL4R34.js.map +0 -1
- package/dist/chunk-NW5NYTQM.js.map +0 -1
- package/dist/chunk-UK72UQ5U.js.map +0 -1
- package/dist/chunk-V5KZGB5V.js.map +0 -1
- package/dist/dist-QZGJIBT5.js.map +0 -1
- package/dist/dist-R67WMLCF.js.map +0 -1
- /package/dist/{_cloud-attach-DMVH6GWO.js.map → _cloud-attach-ZXBCNWJX.js.map} +0 -0
- /package/dist/{cloud-poller-ZIWSADJB-JXFRJUEM.js.map → cloud-poller-SUNA6ZQC-2RG5WPRN.js.map} +0 -0
- /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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
62
|
+
const spawn4 = ptyMod["spawn"] ?? ptyMod["default"]?.["spawn"];
|
|
55
63
|
const Terminal = xtermMod["Terminal"] ?? xtermMod["default"]?.["Terminal"];
|
|
56
|
-
if (typeof
|
|
64
|
+
if (typeof spawn4 !== "function" || typeof Terminal !== "function") {
|
|
57
65
|
return null;
|
|
58
66
|
}
|
|
59
67
|
return {
|
|
60
|
-
ptySpawn:
|
|
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
|
|
127
|
+
let lines;
|
|
120
128
|
let noteKind;
|
|
121
129
|
switch (args.mode) {
|
|
122
130
|
case "split":
|
|
123
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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",
|
|
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
|
-
|
|
360
|
+
let i = 0;
|
|
361
|
+
while (i < buf.length) {
|
|
264
362
|
const byte = buf[i];
|
|
265
|
-
if (byte === void 0)
|
|
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
|
-
|
|
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
|
|
1053
|
-
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
|
+
}
|
|
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, {
|
|
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
|
|
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
|
|
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-
|
|
1516
|
+
//# sourceMappingURL=chunk-GU5LW4B5.js.map
|