@madarco/agentbox 0.6.0 → 0.7.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-DMVH6GWO.js +12 -0
- package/dist/chunk-7KOEFGN2.js +1162 -0
- package/dist/chunk-7KOEFGN2.js.map +1 -0
- package/dist/chunk-I24B6AXR.js +600 -0
- package/dist/chunk-I24B6AXR.js.map +1 -0
- package/dist/chunk-NAVL4R34.js +7546 -0
- package/dist/chunk-NAVL4R34.js.map +1 -0
- package/dist/chunk-NW5NYTQM.js +1366 -0
- package/dist/chunk-NW5NYTQM.js.map +1 -0
- package/dist/chunk-UK72UQ5U.js +237 -0
- package/dist/chunk-UK72UQ5U.js.map +1 -0
- package/dist/chunk-V5KZGB5V.js +722 -0
- package/dist/chunk-V5KZGB5V.js.map +1 -0
- package/dist/cloud-poller-ZIWSADJB-JXFRJUEM.js +10 -0
- package/dist/dist-ETCFRVPA.js +423 -0
- package/dist/dist-QZGJIBT5.js +1339 -0
- package/dist/dist-QZGJIBT5.js.map +1 -0
- package/dist/dist-R67WMLCF.js +183 -0
- package/dist/dist-R67WMLCF.js.map +1 -0
- package/dist/index.js +3998 -1569
- package/dist/index.js.map +1 -1
- package/package.json +8 -3
- package/runtime/docker/Dockerfile.box +98 -14
- package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +15 -8
- package/runtime/docker/packages/ctl/dist/bin.cjs +10220 -773
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-codex-hooks.json +37 -0
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-open +9 -9
- package/runtime/hetzner/agentbox-checkpoint-cleanup +52 -0
- package/runtime/hetzner/agentbox-codex-hooks.json +37 -0
- package/runtime/hetzner/agentbox-dockerd-start +132 -0
- package/runtime/hetzner/agentbox-open +28 -0
- package/runtime/hetzner/agentbox-setup-skill.md +196 -0
- package/runtime/hetzner/agentbox-vnc-start +77 -0
- package/runtime/hetzner/claude-managed-settings.json +54 -0
- package/runtime/hetzner/ctl.cjs +22350 -0
- package/runtime/hetzner/custom-system-CLAUDE.md +27 -0
- package/runtime/hetzner/scripts/install-box.sh +365 -0
- package/runtime/relay/bin.cjs +9118 -809
- package/share/agentbox-setup/SKILL.md +15 -8
- package/dist/chunk-BBZMA2K6.js +0 -238
- package/dist/chunk-BBZMA2K6.js.map +0 -1
- package/dist/chunk-HHMWQNLF.js +0 -1709
- package/dist/chunk-HHMWQNLF.js.map +0 -1
- package/dist/chunk-HPZMD5DE.js +0 -106
- package/dist/chunk-HPZMD5DE.js.map +0 -1
- package/dist/chunk-HTTKML3C.js +0 -2655
- package/dist/chunk-HTTKML3C.js.map +0 -1
- package/dist/chunk-KJNZP6I3.js +0 -586
- package/dist/chunk-KJNZP6I3.js.map +0 -1
- package/dist/chunk-M7I247BK.js +0 -525
- package/dist/chunk-M7I247BK.js.map +0 -1
- package/dist/create-6PWXI6HO-OWAMHBAK.js +0 -15
- package/dist/lifecycle-EMXR46DI-DUVBXNTV.js +0 -38
- package/dist/state-KD7M46ZP-KHFTHFUS.js +0 -26
- package/dist/stats-SZXOJE3D-N7OODCHW.js +0 -19
- package/dist/stats-SZXOJE3D-N7OODCHW.js.map +0 -1
- /package/dist/{create-6PWXI6HO-OWAMHBAK.js.map → _cloud-attach-DMVH6GWO.js.map} +0 -0
- /package/dist/{lifecycle-EMXR46DI-DUVBXNTV.js.map → cloud-poller-ZIWSADJB-JXFRJUEM.js.map} +0 -0
- /package/dist/{state-KD7M46ZP-KHFTHFUS.js.map → dist-ETCFRVPA.js.map} +0 -0
|
@@ -0,0 +1,722 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// ../../packages/sandbox-daytona/dist/chunk-355NHRTE.js
|
|
4
|
+
import { existsSync } from "fs";
|
|
5
|
+
import { dirname, resolve } from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
8
|
+
import { homedir } from "os";
|
|
9
|
+
import { resolve as resolve2 } from "path";
|
|
10
|
+
import { Daytona, DaytonaNotFoundError as DaytonaNotFoundError2, Image, SandboxState } from "@daytonaio/sdk";
|
|
11
|
+
import {
|
|
12
|
+
DaytonaAuthenticationError,
|
|
13
|
+
DaytonaAuthorizationError,
|
|
14
|
+
DaytonaConflictError,
|
|
15
|
+
DaytonaConnectionError,
|
|
16
|
+
DaytonaError,
|
|
17
|
+
DaytonaNotFoundError,
|
|
18
|
+
DaytonaRateLimitError,
|
|
19
|
+
DaytonaTimeoutError,
|
|
20
|
+
DaytonaValidationError
|
|
21
|
+
} from "@daytonaio/sdk";
|
|
22
|
+
import { spawnSync } from "child_process";
|
|
23
|
+
import {
|
|
24
|
+
chmodSync,
|
|
25
|
+
existsSync as existsSync3,
|
|
26
|
+
mkdirSync,
|
|
27
|
+
readFileSync as readFileSync2,
|
|
28
|
+
renameSync,
|
|
29
|
+
writeFileSync
|
|
30
|
+
} from "fs";
|
|
31
|
+
import { homedir as homedir2 } from "os";
|
|
32
|
+
import { dirname as dirname2, resolve as resolve3 } from "path";
|
|
33
|
+
import { confirm, isCancel, intro, log, note, outro, password, spinner, text } from "@clack/prompts";
|
|
34
|
+
function resolveDockerfileContext() {
|
|
35
|
+
const override = process.env.AGENTBOX_DOCKER_CONTEXT;
|
|
36
|
+
if (override && existsSync(resolve(override, "Dockerfile.box"))) {
|
|
37
|
+
return { dockerfile: resolve(override, "Dockerfile.box"), context: override };
|
|
38
|
+
}
|
|
39
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
40
|
+
const staged = resolve(here, "..", "runtime", "docker");
|
|
41
|
+
if (existsSync(resolve(staged, "Dockerfile.box"))) {
|
|
42
|
+
return { dockerfile: resolve(staged, "Dockerfile.box"), context: staged };
|
|
43
|
+
}
|
|
44
|
+
const monorepoRoot = resolve(here, "..", "..", "..");
|
|
45
|
+
const dockerfile = resolve(monorepoRoot, "packages", "sandbox-docker", "Dockerfile.box");
|
|
46
|
+
if (existsSync(dockerfile)) {
|
|
47
|
+
return { dockerfile, context: monorepoRoot };
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
var DAYTONA_KEYS = [
|
|
52
|
+
"DAYTONA_API_KEY",
|
|
53
|
+
"DAYTONA_JWT_TOKEN",
|
|
54
|
+
"DAYTONA_ORGANIZATION_ID",
|
|
55
|
+
"DAYTONA_API_URL",
|
|
56
|
+
"DAYTONA_TARGET"
|
|
57
|
+
];
|
|
58
|
+
var loaded = false;
|
|
59
|
+
function ensureDaytonaEnvLoaded() {
|
|
60
|
+
if (loaded) return;
|
|
61
|
+
loaded = true;
|
|
62
|
+
importDaytonaFromFile(resolve2(homedir(), ".agentbox", "secrets.env"));
|
|
63
|
+
}
|
|
64
|
+
function importDaytonaFromFile(path) {
|
|
65
|
+
if (!existsSync2(path)) return;
|
|
66
|
+
let body;
|
|
67
|
+
try {
|
|
68
|
+
body = readFileSync(path, "utf8");
|
|
69
|
+
} catch {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const parsed = parseEnvFile(body);
|
|
73
|
+
for (const key of DAYTONA_KEYS) {
|
|
74
|
+
if (process.env[key] !== void 0) continue;
|
|
75
|
+
const value = parsed[key];
|
|
76
|
+
if (typeof value === "string") {
|
|
77
|
+
process.env[key] = value;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function parseEnvFile(body) {
|
|
82
|
+
const out = {};
|
|
83
|
+
for (const rawLine of body.split(/\r?\n/)) {
|
|
84
|
+
const line = rawLine.trim();
|
|
85
|
+
if (line.length === 0 || line.startsWith("#")) continue;
|
|
86
|
+
const stripped = line.startsWith("export ") ? line.slice("export ".length) : line;
|
|
87
|
+
const eq = stripped.indexOf("=");
|
|
88
|
+
if (eq <= 0) continue;
|
|
89
|
+
const key = stripped.slice(0, eq).trim();
|
|
90
|
+
let value = stripped.slice(eq + 1).trim();
|
|
91
|
+
if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
|
|
92
|
+
value = value.slice(1, -1);
|
|
93
|
+
}
|
|
94
|
+
out[key] = value;
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
var DEFAULT_BACKOFF = [1e3, 2e3, 4e3];
|
|
99
|
+
var DEFAULT_ATTEMPT_TIMEOUT_MS = 3e4;
|
|
100
|
+
var AttemptTimeoutError = class extends Error {
|
|
101
|
+
constructor(method, ms) {
|
|
102
|
+
super(`daytona ${method}: per-attempt timeout after ${String(ms)}ms`);
|
|
103
|
+
this.name = "AttemptTimeoutError";
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
function isRetriable(err, allowAmbiguous) {
|
|
107
|
+
if (err instanceof DaytonaRateLimitError) return true;
|
|
108
|
+
if (err instanceof DaytonaNotFoundError || err instanceof DaytonaAuthenticationError || err instanceof DaytonaAuthorizationError || err instanceof DaytonaValidationError || err instanceof DaytonaConflictError) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
if (err instanceof DaytonaConnectionError || err instanceof DaytonaTimeoutError || err instanceof AttemptTimeoutError) {
|
|
112
|
+
return allowAmbiguous;
|
|
113
|
+
}
|
|
114
|
+
if (err instanceof DaytonaError) {
|
|
115
|
+
const status = err.statusCode;
|
|
116
|
+
if (typeof status === "number" && status >= 500 && status <= 599) {
|
|
117
|
+
return allowAmbiguous;
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
if (err && typeof err === "object") {
|
|
122
|
+
const code = err.code;
|
|
123
|
+
if (code === "ECONNRESET" || code === "ETIMEDOUT" || code === "ECONNABORTED" || code === "EAI_AGAIN" || code === "ECONNREFUSED" || code === "ENOTFOUND") {
|
|
124
|
+
return allowAmbiguous;
|
|
125
|
+
}
|
|
126
|
+
const status = err.response?.status ?? err.status ?? err.statusCode;
|
|
127
|
+
if (typeof status === "number" && status >= 500 && status <= 599) {
|
|
128
|
+
return allowAmbiguous;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
async function withDaytonaRetry(opts, fn) {
|
|
134
|
+
const backoff = opts.backoffMs ?? DEFAULT_BACKOFF;
|
|
135
|
+
const maxAttempts = backoff.length + 1;
|
|
136
|
+
const timeoutMs = opts.attemptTimeoutMs ?? DEFAULT_ATTEMPT_TIMEOUT_MS;
|
|
137
|
+
const log2 = opts.onRetry ?? defaultRetryLog;
|
|
138
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
139
|
+
try {
|
|
140
|
+
return await raceTimeout(fn(), timeoutMs, opts.method);
|
|
141
|
+
} catch (err) {
|
|
142
|
+
const last = attempt === maxAttempts;
|
|
143
|
+
if (last || !isRetriable(err, opts.retryOnAmbiguous)) throw err;
|
|
144
|
+
const delay = backoff[attempt - 1] ?? backoff[backoff.length - 1] ?? 4e3;
|
|
145
|
+
log2(
|
|
146
|
+
`daytona ${opts.method}: attempt ${String(attempt)} failed (${errorSummary(err)}); retrying in ${String(delay)}ms`
|
|
147
|
+
);
|
|
148
|
+
await sleep(delay);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
throw new Error(`withDaytonaRetry: exhausted attempts for ${opts.method}`);
|
|
152
|
+
}
|
|
153
|
+
function defaultRetryLog(line) {
|
|
154
|
+
process.stderr.write(`
|
|
155
|
+
[daytona-retry] ${line}
|
|
156
|
+
`);
|
|
157
|
+
}
|
|
158
|
+
function sleep(ms) {
|
|
159
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
160
|
+
}
|
|
161
|
+
async function raceTimeout(p, ms, method) {
|
|
162
|
+
let timer;
|
|
163
|
+
try {
|
|
164
|
+
return await Promise.race([
|
|
165
|
+
p,
|
|
166
|
+
new Promise((_resolve, reject) => {
|
|
167
|
+
timer = setTimeout(() => reject(new AttemptTimeoutError(method, ms)), ms);
|
|
168
|
+
})
|
|
169
|
+
]);
|
|
170
|
+
} finally {
|
|
171
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function errorSummary(err) {
|
|
175
|
+
if (err instanceof DaytonaError) {
|
|
176
|
+
const status = err.statusCode;
|
|
177
|
+
const cls = err.constructor.name;
|
|
178
|
+
return `${cls}${typeof status === "number" ? ` ${String(status)}` : ""}: ${truncate(err.message)}`;
|
|
179
|
+
}
|
|
180
|
+
if (err instanceof Error) {
|
|
181
|
+
const code = err.code;
|
|
182
|
+
return code !== void 0 ? `${err.name}(${String(code)}): ${truncate(err.message)}` : `${err.name}: ${truncate(err.message)}`;
|
|
183
|
+
}
|
|
184
|
+
return truncate(String(err));
|
|
185
|
+
}
|
|
186
|
+
function truncate(s, max = 160) {
|
|
187
|
+
return s.length > max ? `${s.slice(0, max)}\u2026` : s;
|
|
188
|
+
}
|
|
189
|
+
function retry(method, fn, opts = {}) {
|
|
190
|
+
return withDaytonaRetry(
|
|
191
|
+
{
|
|
192
|
+
method,
|
|
193
|
+
retryOnAmbiguous: opts.retryOnAmbiguous ?? true,
|
|
194
|
+
attemptTimeoutMs: opts.attemptTimeoutMs,
|
|
195
|
+
backoffMs: opts.noRetry === true ? [] : void 0
|
|
196
|
+
},
|
|
197
|
+
fn
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
var DEFAULT_BOX_IMAGE_REF = "agentbox/box:dev";
|
|
201
|
+
var client = null;
|
|
202
|
+
function getClient() {
|
|
203
|
+
if (!client) {
|
|
204
|
+
ensureDaytonaEnvLoaded();
|
|
205
|
+
try {
|
|
206
|
+
client = new Daytona();
|
|
207
|
+
} catch (err) {
|
|
208
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
209
|
+
throw new Error(
|
|
210
|
+
`Daytona credentials not configured: ${msg}
|
|
211
|
+
Run \`agentbox daytona login\` interactively, or set DAYTONA_API_KEY in the environment.`
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return client;
|
|
216
|
+
}
|
|
217
|
+
async function getSandbox(id) {
|
|
218
|
+
return getClient().get(id);
|
|
219
|
+
}
|
|
220
|
+
async function maybeGetSandbox(id) {
|
|
221
|
+
try {
|
|
222
|
+
return await getClient().get(id);
|
|
223
|
+
} catch {
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
function mapState(s) {
|
|
228
|
+
switch (s) {
|
|
229
|
+
case SandboxState.STARTED:
|
|
230
|
+
return "running";
|
|
231
|
+
case SandboxState.STARTING:
|
|
232
|
+
case SandboxState.CREATING:
|
|
233
|
+
case SandboxState.RESTORING:
|
|
234
|
+
case SandboxState.BUILDING_SNAPSHOT:
|
|
235
|
+
case SandboxState.PULLING_SNAPSHOT:
|
|
236
|
+
case SandboxState.PENDING_BUILD:
|
|
237
|
+
case SandboxState.STOPPING:
|
|
238
|
+
return "running";
|
|
239
|
+
case SandboxState.STOPPED:
|
|
240
|
+
return "stopped";
|
|
241
|
+
case SandboxState.ARCHIVED:
|
|
242
|
+
case SandboxState.ARCHIVING:
|
|
243
|
+
return "paused";
|
|
244
|
+
case SandboxState.DESTROYED:
|
|
245
|
+
case SandboxState.DESTROYING:
|
|
246
|
+
case SandboxState.ERROR:
|
|
247
|
+
case SandboxState.BUILD_FAILED:
|
|
248
|
+
case SandboxState.UNKNOWN:
|
|
249
|
+
default:
|
|
250
|
+
return "missing";
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function toDaytonaVolumeMount(v) {
|
|
254
|
+
return {
|
|
255
|
+
volumeId: v.volumeId,
|
|
256
|
+
mountPath: v.mountPath,
|
|
257
|
+
...v.subpath ? { subpath: v.subpath } : {}
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function resolveImage(ref) {
|
|
261
|
+
if (ref !== DEFAULT_BOX_IMAGE_REF) return ref;
|
|
262
|
+
const ctx = resolveDockerfileContext();
|
|
263
|
+
if (!ctx) {
|
|
264
|
+
throw new Error(
|
|
265
|
+
"could not locate the AgentBox Dockerfile.box build context for the Daytona snapshot. Set AGENTBOX_DOCKER_CONTEXT to a directory containing Dockerfile.box, or pass --image <ref> with a Daytona-compatible image."
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
return Image.fromDockerfile(ctx.dockerfile);
|
|
269
|
+
}
|
|
270
|
+
var daytonaBackend = {
|
|
271
|
+
name: "daytona",
|
|
272
|
+
async provision(req) {
|
|
273
|
+
return retry(
|
|
274
|
+
"provision",
|
|
275
|
+
async () => {
|
|
276
|
+
const baseParams = {
|
|
277
|
+
...req.resources ? { resources: req.resources } : {},
|
|
278
|
+
envVars: req.env,
|
|
279
|
+
...req.volumes && req.volumes.length > 0 ? { volumes: req.volumes.map(toDaytonaVolumeMount) } : {},
|
|
280
|
+
labels: { "agentbox.name": req.name }
|
|
281
|
+
};
|
|
282
|
+
const client2 = getClient();
|
|
283
|
+
let snapshotName = req.snapshot;
|
|
284
|
+
if (!snapshotName && req.image && req.image !== DEFAULT_BOX_IMAGE_REF) {
|
|
285
|
+
try {
|
|
286
|
+
const snap = await client2.snapshot.get(req.image);
|
|
287
|
+
if (snap && snap.name) snapshotName = snap.name;
|
|
288
|
+
} catch {
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
const snapshotParams = { ...baseParams };
|
|
292
|
+
delete snapshotParams.resources;
|
|
293
|
+
const sandbox = snapshotName ? await client2.create({ snapshot: snapshotName, ...snapshotParams }, { timeout: 900 }) : await client2.create(
|
|
294
|
+
{ image: resolveImage(req.image), ...baseParams },
|
|
295
|
+
{
|
|
296
|
+
timeout: 900,
|
|
297
|
+
...req.onLog ? { onSnapshotCreateLogs: req.onLog } : {}
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
return { sandboxId: sandbox.id };
|
|
301
|
+
},
|
|
302
|
+
{ retryOnAmbiguous: false, attemptTimeoutMs: 9e5 }
|
|
303
|
+
);
|
|
304
|
+
},
|
|
305
|
+
async ensureVolume(name) {
|
|
306
|
+
const client2 = getClient();
|
|
307
|
+
let vol = await retry("volume.get(create)", () => client2.volume.get(name, true));
|
|
308
|
+
const deadline = Date.now() + 6e4;
|
|
309
|
+
while (vol.state !== "ready") {
|
|
310
|
+
if (vol.state === "error" || vol.state === "deleted" || vol.state === "deleting") {
|
|
311
|
+
throw new Error(
|
|
312
|
+
`Daytona volume '${name}' is in unrecoverable state '${vol.state}'. Delete it from the Daytona dashboard and retry.`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
if (Date.now() >= deadline) {
|
|
316
|
+
throw new Error(
|
|
317
|
+
`Daytona volume '${name}' did not become ready within 60s (state: ${vol.state}). Try again \u2014 the Daytona control plane may be slow.`
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
await new Promise((r) => setTimeout(r, 1e3));
|
|
321
|
+
vol = await retry("volume.get(poll)", () => client2.volume.get(name));
|
|
322
|
+
}
|
|
323
|
+
return { volumeId: vol.id };
|
|
324
|
+
},
|
|
325
|
+
async get(sandboxId) {
|
|
326
|
+
return retry("get", async () => {
|
|
327
|
+
const sb = await maybeGetSandbox(sandboxId);
|
|
328
|
+
return sb ? { sandboxId: sb.id } : null;
|
|
329
|
+
});
|
|
330
|
+
},
|
|
331
|
+
async list() {
|
|
332
|
+
return retry("list", async () => {
|
|
333
|
+
const client2 = getClient();
|
|
334
|
+
const page = await client2.list();
|
|
335
|
+
const items = Array.isArray(page) ? page : page.items ?? [];
|
|
336
|
+
return items.map((sb) => {
|
|
337
|
+
const summary = { sandboxId: sb.id };
|
|
338
|
+
const raw = sb;
|
|
339
|
+
const friendly = raw.labels?.["agentbox.name"] ?? raw.name;
|
|
340
|
+
if (friendly) summary.name = friendly;
|
|
341
|
+
if (raw.createdAt) summary.createdAt = raw.createdAt;
|
|
342
|
+
if (typeof raw.state === "string") summary.state = mapState(raw.state);
|
|
343
|
+
return summary;
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
},
|
|
347
|
+
async start(h) {
|
|
348
|
+
return retry(
|
|
349
|
+
"start",
|
|
350
|
+
async () => {
|
|
351
|
+
const sb = await getSandbox(h.sandboxId);
|
|
352
|
+
await sb.start();
|
|
353
|
+
},
|
|
354
|
+
{ attemptTimeoutMs: 6e4 }
|
|
355
|
+
);
|
|
356
|
+
},
|
|
357
|
+
async stop(h) {
|
|
358
|
+
return retry(
|
|
359
|
+
"stop",
|
|
360
|
+
async () => {
|
|
361
|
+
const sb = await getSandbox(h.sandboxId);
|
|
362
|
+
await sb.stop();
|
|
363
|
+
},
|
|
364
|
+
{ attemptTimeoutMs: 6e4 }
|
|
365
|
+
);
|
|
366
|
+
},
|
|
367
|
+
async pause(h) {
|
|
368
|
+
return retry(
|
|
369
|
+
"pause",
|
|
370
|
+
async () => {
|
|
371
|
+
const sb = await getSandbox(h.sandboxId);
|
|
372
|
+
await sb.archive();
|
|
373
|
+
},
|
|
374
|
+
{ attemptTimeoutMs: 6e4 }
|
|
375
|
+
);
|
|
376
|
+
},
|
|
377
|
+
async resume(h) {
|
|
378
|
+
return retry(
|
|
379
|
+
"resume",
|
|
380
|
+
async () => {
|
|
381
|
+
const sb = await getSandbox(h.sandboxId);
|
|
382
|
+
await sb.start();
|
|
383
|
+
},
|
|
384
|
+
{ attemptTimeoutMs: 6e4 }
|
|
385
|
+
);
|
|
386
|
+
},
|
|
387
|
+
async destroy(h) {
|
|
388
|
+
return retry(
|
|
389
|
+
"destroy",
|
|
390
|
+
async () => {
|
|
391
|
+
const sb = await maybeGetSandbox(h.sandboxId);
|
|
392
|
+
if (!sb) return;
|
|
393
|
+
try {
|
|
394
|
+
await sb.stop(60);
|
|
395
|
+
} catch {
|
|
396
|
+
}
|
|
397
|
+
try {
|
|
398
|
+
await sb.delete(60);
|
|
399
|
+
} catch (err) {
|
|
400
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
401
|
+
if (!/not found/i.test(msg)) throw err;
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
{ attemptTimeoutMs: 12e4 }
|
|
405
|
+
);
|
|
406
|
+
},
|
|
407
|
+
async state(h) {
|
|
408
|
+
return retry("state", async () => {
|
|
409
|
+
const sb = await maybeGetSandbox(h.sandboxId);
|
|
410
|
+
if (!sb) return "missing";
|
|
411
|
+
return mapState(sb.state);
|
|
412
|
+
});
|
|
413
|
+
},
|
|
414
|
+
async exec(h, cmd, opts) {
|
|
415
|
+
return retry(
|
|
416
|
+
"exec",
|
|
417
|
+
async () => {
|
|
418
|
+
const sb = await getSandbox(h.sandboxId);
|
|
419
|
+
const r = await sb.process.executeCommand(cmd, opts?.cwd, opts?.env);
|
|
420
|
+
return { exitCode: r.exitCode, stdout: r.result, stderr: "" };
|
|
421
|
+
},
|
|
422
|
+
{ attemptTimeoutMs: opts?.attemptTimeoutMs ?? 12e4, noRetry: opts?.noRetry }
|
|
423
|
+
);
|
|
424
|
+
},
|
|
425
|
+
async uploadFile(h, localPath, remotePath) {
|
|
426
|
+
return retry(
|
|
427
|
+
"uploadFile",
|
|
428
|
+
async () => {
|
|
429
|
+
const sb = await getSandbox(h.sandboxId);
|
|
430
|
+
await sb.fs.uploadFile(localPath, remotePath);
|
|
431
|
+
},
|
|
432
|
+
{ attemptTimeoutMs: 3e5 }
|
|
433
|
+
);
|
|
434
|
+
},
|
|
435
|
+
async downloadFile(h, remotePath, localPath) {
|
|
436
|
+
return retry(
|
|
437
|
+
"downloadFile",
|
|
438
|
+
async () => {
|
|
439
|
+
const sb = await getSandbox(h.sandboxId);
|
|
440
|
+
await sb.fs.downloadFile(remotePath, localPath);
|
|
441
|
+
},
|
|
442
|
+
{ attemptTimeoutMs: 3e5 }
|
|
443
|
+
);
|
|
444
|
+
},
|
|
445
|
+
async listFiles(h, remoteDir) {
|
|
446
|
+
return retry("listFiles", async () => {
|
|
447
|
+
const sb = await getSandbox(h.sandboxId);
|
|
448
|
+
const files = await sb.fs.listFiles(remoteDir);
|
|
449
|
+
return files.map((f) => ({
|
|
450
|
+
name: f.name,
|
|
451
|
+
isDir: Boolean(f.isDir)
|
|
452
|
+
}));
|
|
453
|
+
});
|
|
454
|
+
},
|
|
455
|
+
async previewUrl(h, port) {
|
|
456
|
+
return retry("previewUrl", async () => {
|
|
457
|
+
const sb = await getSandbox(h.sandboxId);
|
|
458
|
+
const p = await sb.getPreviewLink(port);
|
|
459
|
+
return { url: p.url, token: p.token };
|
|
460
|
+
});
|
|
461
|
+
},
|
|
462
|
+
async signedPreviewUrl(h, port, expiresInSeconds) {
|
|
463
|
+
return retry("signedPreviewUrl", async () => {
|
|
464
|
+
const sb = await getSandbox(h.sandboxId);
|
|
465
|
+
const s = await sb.getSignedPreviewUrl(port, expiresInSeconds);
|
|
466
|
+
return { url: s.url, token: s.token };
|
|
467
|
+
});
|
|
468
|
+
},
|
|
469
|
+
async attachArgv(h) {
|
|
470
|
+
return retry("attachArgv", async () => {
|
|
471
|
+
const sb = await getSandbox(h.sandboxId);
|
|
472
|
+
const ssh = await sb.createSshAccess(60);
|
|
473
|
+
return [
|
|
474
|
+
"ssh",
|
|
475
|
+
// First-connect to a never-seen host fingerprint should be silent in a
|
|
476
|
+
// PTY — the user already authenticated via Daytona's API.
|
|
477
|
+
"-o",
|
|
478
|
+
"StrictHostKeyChecking=accept-new",
|
|
479
|
+
// Daytona's SSH gateway terminates per-token; no key file, no port.
|
|
480
|
+
`${ssh.token}@ssh.app.daytona.io`
|
|
481
|
+
];
|
|
482
|
+
});
|
|
483
|
+
},
|
|
484
|
+
async revokeAttachToken(h, argv) {
|
|
485
|
+
const userhost = argv[argv.length - 1] ?? "";
|
|
486
|
+
const atIdx = userhost.indexOf("@");
|
|
487
|
+
if (atIdx <= 0) return;
|
|
488
|
+
const token = userhost.slice(0, atIdx);
|
|
489
|
+
if (token.length === 0) return;
|
|
490
|
+
try {
|
|
491
|
+
await retry("revokeAttachToken", async () => {
|
|
492
|
+
const sb = await getSandbox(h.sandboxId);
|
|
493
|
+
await sb.revokeSshAccess(token);
|
|
494
|
+
});
|
|
495
|
+
} catch {
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
async createSnapshot(h, snapshotName) {
|
|
499
|
+
return retry(
|
|
500
|
+
"createSnapshot",
|
|
501
|
+
async () => {
|
|
502
|
+
const sb = await getSandbox(h.sandboxId);
|
|
503
|
+
await sb._experimental_createSnapshot(snapshotName);
|
|
504
|
+
},
|
|
505
|
+
{ attemptTimeoutMs: 9e5, retryOnAmbiguous: false }
|
|
506
|
+
);
|
|
507
|
+
},
|
|
508
|
+
async deleteSnapshot(snapshotName) {
|
|
509
|
+
return retry("deleteSnapshot", async () => {
|
|
510
|
+
try {
|
|
511
|
+
const client2 = getClient();
|
|
512
|
+
const snapshot = await client2.snapshot.get(snapshotName);
|
|
513
|
+
await client2.snapshot.delete(snapshot);
|
|
514
|
+
} catch (err) {
|
|
515
|
+
if (err instanceof DaytonaNotFoundError2) return;
|
|
516
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
517
|
+
if (/not found/i.test(msg)) return;
|
|
518
|
+
throw err;
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
var DASHBOARD_KEYS_URL = "https://app.daytona.io/dashboard/keys";
|
|
524
|
+
var MANAGED_KEYS = ["DAYTONA_API_KEY", "DAYTONA_JWT_TOKEN", "DAYTONA_ORGANIZATION_ID"];
|
|
525
|
+
async function ensureDaytonaCredentials(opts = {}) {
|
|
526
|
+
ensureDaytonaEnvLoaded();
|
|
527
|
+
if (!opts.force && hasUsableCredentials()) return;
|
|
528
|
+
if (!process.stdin.isTTY) return;
|
|
529
|
+
intro("Daytona setup");
|
|
530
|
+
note(
|
|
531
|
+
`AgentBox needs a Daytona API key to provision cloud boxes.
|
|
532
|
+
Generate one at ${DASHBOARD_KEYS_URL}`,
|
|
533
|
+
"API key required"
|
|
534
|
+
);
|
|
535
|
+
const open = await confirm({
|
|
536
|
+
message: `Open ${DASHBOARD_KEYS_URL} in your browser?`,
|
|
537
|
+
initialValue: true
|
|
538
|
+
});
|
|
539
|
+
if (isCancel(open)) {
|
|
540
|
+
log.warn("Daytona setup cancelled \u2014 re-run `agentbox daytona login` when ready.");
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
if (open) openDashboard();
|
|
544
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
545
|
+
const creds = await promptForCredentials();
|
|
546
|
+
if (creds === null) return;
|
|
547
|
+
const result = await validateCredentials(creds);
|
|
548
|
+
if (result.ok) {
|
|
549
|
+
persistCredentials(creds);
|
|
550
|
+
log.success(`Daytona credentials saved to ${secretsPath()}`);
|
|
551
|
+
outro("Setup complete.");
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
if (result.kind === "auth" && attempt === 0) {
|
|
555
|
+
log.error(`That key was rejected by Daytona: ${result.message}`);
|
|
556
|
+
log.info("Try again, or press Ctrl-C to cancel.");
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
if (result.kind === "network") {
|
|
560
|
+
log.warn(`Could not reach Daytona to validate (${result.message}) \u2014 saving anyway.`);
|
|
561
|
+
persistCredentials(creds);
|
|
562
|
+
log.success(`Daytona credentials saved to ${secretsPath()}`);
|
|
563
|
+
outro("Setup complete (unvalidated).");
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
throw new Error(`Daytona credentials rejected: ${result.message}`);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
function hasUsableCredentials() {
|
|
570
|
+
if (process.env.DAYTONA_API_KEY) return true;
|
|
571
|
+
if (process.env.DAYTONA_JWT_TOKEN && process.env.DAYTONA_ORGANIZATION_ID) return true;
|
|
572
|
+
return false;
|
|
573
|
+
}
|
|
574
|
+
async function promptForCredentials() {
|
|
575
|
+
const key = await password({
|
|
576
|
+
message: "Paste your Daytona API key (or JWT token)",
|
|
577
|
+
validate(v) {
|
|
578
|
+
if (!v || v.trim().length === 0) return "Cannot be empty";
|
|
579
|
+
return void 0;
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
if (isCancel(key)) {
|
|
583
|
+
log.warn("Daytona setup cancelled.");
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
const trimmed = key.trim();
|
|
587
|
+
if (trimmed.startsWith("eyJ")) {
|
|
588
|
+
const org = await text({
|
|
589
|
+
message: "Paste your Daytona organization ID",
|
|
590
|
+
placeholder: "org_...",
|
|
591
|
+
validate(v) {
|
|
592
|
+
if (!v || v.trim().length === 0) return "Cannot be empty";
|
|
593
|
+
return void 0;
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
if (isCancel(org)) {
|
|
597
|
+
log.warn("Daytona setup cancelled.");
|
|
598
|
+
return null;
|
|
599
|
+
}
|
|
600
|
+
return { jwtToken: trimmed, organizationId: org.trim() };
|
|
601
|
+
}
|
|
602
|
+
return { apiKey: trimmed };
|
|
603
|
+
}
|
|
604
|
+
async function validateCredentials(creds) {
|
|
605
|
+
const s = spinner();
|
|
606
|
+
s.start("Validating credentials with Daytona");
|
|
607
|
+
const snapshot = snapshotManagedEnv();
|
|
608
|
+
applyToEnv(creds);
|
|
609
|
+
try {
|
|
610
|
+
const { Daytona: Daytona2 } = await import("@daytonaio/sdk");
|
|
611
|
+
const client2 = new Daytona2();
|
|
612
|
+
await client2.list();
|
|
613
|
+
s.stop("Daytona credentials accepted");
|
|
614
|
+
return { ok: true };
|
|
615
|
+
} catch (err) {
|
|
616
|
+
restoreManagedEnv(snapshot);
|
|
617
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
618
|
+
s.stop("Daytona credentials check failed");
|
|
619
|
+
if (/401|403|unauthor|forbidden|invalid/i.test(message)) {
|
|
620
|
+
return { ok: false, kind: "auth", message };
|
|
621
|
+
}
|
|
622
|
+
return { ok: false, kind: "network", message };
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
function snapshotManagedEnv() {
|
|
626
|
+
const out = {};
|
|
627
|
+
for (const k of MANAGED_KEYS) out[k] = process.env[k];
|
|
628
|
+
return out;
|
|
629
|
+
}
|
|
630
|
+
function restoreManagedEnv(snap) {
|
|
631
|
+
for (const k of MANAGED_KEYS) {
|
|
632
|
+
if (snap[k] === void 0) delete process.env[k];
|
|
633
|
+
else process.env[k] = snap[k];
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
function applyToEnv(creds) {
|
|
637
|
+
for (const k of MANAGED_KEYS) delete process.env[k];
|
|
638
|
+
if (creds.apiKey) process.env.DAYTONA_API_KEY = creds.apiKey;
|
|
639
|
+
if (creds.jwtToken) process.env.DAYTONA_JWT_TOKEN = creds.jwtToken;
|
|
640
|
+
if (creds.organizationId) process.env.DAYTONA_ORGANIZATION_ID = creds.organizationId;
|
|
641
|
+
}
|
|
642
|
+
function persistCredentials(creds) {
|
|
643
|
+
applyToEnv(creds);
|
|
644
|
+
const path = secretsPath();
|
|
645
|
+
mkdirSync(dirname2(path), { recursive: true });
|
|
646
|
+
let existing = "";
|
|
647
|
+
if (existsSync3(path)) {
|
|
648
|
+
try {
|
|
649
|
+
existing = readFileSync2(path, "utf8");
|
|
650
|
+
} catch {
|
|
651
|
+
existing = "";
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
const kept = existing.split(/\r?\n/).filter((line) => {
|
|
655
|
+
const stripped = line.startsWith("export ") ? line.slice("export ".length) : line;
|
|
656
|
+
const eq = stripped.indexOf("=");
|
|
657
|
+
if (eq <= 0) return true;
|
|
658
|
+
const key = stripped.slice(0, eq).trim();
|
|
659
|
+
return !MANAGED_KEYS.includes(key);
|
|
660
|
+
}).join("\n").replace(/\s+$/u, "");
|
|
661
|
+
const lines = [];
|
|
662
|
+
if (creds.apiKey) lines.push(`DAYTONA_API_KEY=${creds.apiKey}`);
|
|
663
|
+
if (creds.jwtToken) lines.push(`DAYTONA_JWT_TOKEN=${creds.jwtToken}`);
|
|
664
|
+
if (creds.organizationId) lines.push(`DAYTONA_ORGANIZATION_ID=${creds.organizationId}`);
|
|
665
|
+
const body = (kept ? `${kept}
|
|
666
|
+
` : "") + lines.join("\n") + "\n";
|
|
667
|
+
const tmp = `${path}.tmp`;
|
|
668
|
+
writeFileSync(tmp, body, { mode: 384 });
|
|
669
|
+
try {
|
|
670
|
+
chmodSync(tmp, 384);
|
|
671
|
+
} catch {
|
|
672
|
+
}
|
|
673
|
+
renameSync(tmp, path);
|
|
674
|
+
try {
|
|
675
|
+
chmodSync(path, 384);
|
|
676
|
+
} catch {
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
function openDashboard() {
|
|
680
|
+
try {
|
|
681
|
+
const r = spawnSync("open", [DASHBOARD_KEYS_URL], { stdio: "ignore" });
|
|
682
|
+
if (r.status !== 0) {
|
|
683
|
+
log.warn(`Could not auto-open the browser \u2014 visit ${DASHBOARD_KEYS_URL} manually.`);
|
|
684
|
+
}
|
|
685
|
+
} catch {
|
|
686
|
+
log.warn(`Could not auto-open the browser \u2014 visit ${DASHBOARD_KEYS_URL} manually.`);
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
function secretsPath() {
|
|
690
|
+
return resolve3(homedir2(), ".agentbox", "secrets.env");
|
|
691
|
+
}
|
|
692
|
+
function readDaytonaCredStatus() {
|
|
693
|
+
const shellHadKey = !!process.env.DAYTONA_API_KEY || !!process.env.DAYTONA_JWT_TOKEN;
|
|
694
|
+
ensureDaytonaEnvLoaded();
|
|
695
|
+
const apiKey = process.env.DAYTONA_API_KEY;
|
|
696
|
+
const jwtToken = process.env.DAYTONA_JWT_TOKEN;
|
|
697
|
+
const organizationId = process.env.DAYTONA_ORGANIZATION_ID;
|
|
698
|
+
if (!apiKey && !jwtToken) return { source: "none" };
|
|
699
|
+
return {
|
|
700
|
+
apiKey,
|
|
701
|
+
jwtToken,
|
|
702
|
+
organizationId,
|
|
703
|
+
source: shellHadKey ? "env" : "secrets.env"
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
function maskKey(value) {
|
|
707
|
+
if (value.length <= 8) return "*".repeat(value.length);
|
|
708
|
+
return `${value.slice(0, 4)}\u2026${"*".repeat(8)}${value.slice(-4)}`;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
export {
|
|
712
|
+
resolveDockerfileContext,
|
|
713
|
+
ensureDaytonaEnvLoaded,
|
|
714
|
+
DEFAULT_BOX_IMAGE_REF,
|
|
715
|
+
getClient,
|
|
716
|
+
daytonaBackend,
|
|
717
|
+
ensureDaytonaCredentials,
|
|
718
|
+
secretsPath,
|
|
719
|
+
readDaytonaCredStatus,
|
|
720
|
+
maskKey
|
|
721
|
+
};
|
|
722
|
+
//# sourceMappingURL=chunk-V5KZGB5V.js.map
|