@madarco/agentbox 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/_cloud-attach-ZXBCNWJX.js +13 -0
  2. package/dist/{chunk-NW5NYTQM.js → chunk-BXQMIEHC.js} +459 -110
  3. package/dist/chunk-BXQMIEHC.js.map +1 -0
  4. package/dist/{chunk-UK72UQ5U.js → chunk-G3H2L3O2.js} +55 -4
  5. package/dist/chunk-G3H2L3O2.js.map +1 -0
  6. package/dist/{chunk-7KOEFGN2.js → chunk-GU5LW4B5.js} +385 -31
  7. package/dist/chunk-GU5LW4B5.js.map +1 -0
  8. package/dist/chunk-KL36BRN4.js +455 -0
  9. package/dist/chunk-KL36BRN4.js.map +1 -0
  10. package/dist/{chunk-V5KZGB5V.js → chunk-LEV3KICD.js} +18 -2
  11. package/dist/chunk-LEV3KICD.js.map +1 -0
  12. package/dist/chunk-MTVI44DW.js +662 -0
  13. package/dist/chunk-MTVI44DW.js.map +1 -0
  14. package/dist/{chunk-NAVL4R34.js → chunk-NCJP5MTN.js} +1281 -556
  15. package/dist/chunk-NCJP5MTN.js.map +1 -0
  16. package/dist/{cloud-poller-ZIWSADJB-JXFRJUEM.js → cloud-poller-SUNA6ZQC-2RG5WPRN.js} +2 -2
  17. package/dist/{dist-ETCFRVPA.js → dist-32EZBYG4.js} +50 -20
  18. package/dist/{dist-R67WMLCF.js → dist-CX5CGVEB.js} +120 -10
  19. package/dist/dist-CX5CGVEB.js.map +1 -0
  20. package/dist/{dist-QZGJIBT5.js → dist-GDHP34ZK.js} +141 -75
  21. package/dist/dist-GDHP34ZK.js.map +1 -0
  22. package/dist/dist-XML54CNB.js +849 -0
  23. package/dist/dist-XML54CNB.js.map +1 -0
  24. package/dist/index.js +3881 -867
  25. package/dist/index.js.map +1 -1
  26. package/dist/prepared-state-CL4CWXQA-H5THETIM.js +18 -0
  27. package/dist/prepared-state-CL4CWXQA-H5THETIM.js.map +1 -0
  28. package/package.json +7 -5
  29. package/runtime/daytona/custom-system-CLAUDE.md +39 -0
  30. package/runtime/docker/Dockerfile.box +22 -0
  31. package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +1 -1
  32. package/runtime/docker/packages/ctl/dist/bin.cjs +1214 -98
  33. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-codex-hooks.json +66 -35
  34. package/runtime/docker/packages/sandbox-docker/scripts/agentbox-vnc-start +15 -1
  35. package/runtime/docker/packages/sandbox-docker/scripts/claude-managed-settings.json +62 -1
  36. package/runtime/docker/packages/sandbox-docker/scripts/custom-system-CLAUDE.md +15 -4
  37. package/runtime/docker/packages/sandbox-docker/scripts/gh-shim +263 -0
  38. package/runtime/docker/packages/sandbox-docker/scripts/git-shim +131 -0
  39. package/runtime/docker/packages/sandbox-docker/scripts/opencode-agentbox-plugin.js +76 -0
  40. package/runtime/hetzner/agentbox-codex-hooks.json +66 -35
  41. package/runtime/hetzner/agentbox-setup-skill.md +1 -1
  42. package/runtime/hetzner/agentbox-vnc-start +15 -1
  43. package/runtime/hetzner/claude-managed-settings.json +62 -1
  44. package/runtime/hetzner/ctl.cjs +1214 -98
  45. package/runtime/hetzner/custom-system-CLAUDE.md +26 -14
  46. package/runtime/hetzner/gh-shim +263 -0
  47. package/runtime/hetzner/git-shim +131 -0
  48. package/runtime/hetzner/opencode-agentbox-plugin.js +76 -0
  49. package/runtime/hetzner/scripts/install-box.sh +11 -2
  50. package/runtime/relay/bin.cjs +1146 -63
  51. package/runtime/vercel/agentbox-checkpoint-cleanup +52 -0
  52. package/runtime/vercel/agentbox-codex-hooks.json +68 -0
  53. package/runtime/vercel/agentbox-open +28 -0
  54. package/runtime/vercel/agentbox-setup-skill.md +196 -0
  55. package/runtime/vercel/agentbox-vnc-start +91 -0
  56. package/runtime/vercel/claude-managed-settings.json +115 -0
  57. package/runtime/vercel/ctl.cjs +23466 -0
  58. package/runtime/vercel/custom-system-CLAUDE.md +50 -0
  59. package/runtime/vercel/gh-shim +263 -0
  60. package/runtime/vercel/git-shim +131 -0
  61. package/runtime/vercel/scripts/provision.sh +274 -0
  62. package/share/agentbox-setup/SKILL.md +1 -1
  63. package/share/host-skills/agentbox/SKILL.md +29 -0
  64. package/share/host-skills/agentbox-info/SKILL.md +211 -0
  65. package/share/host-skills/codex/agentbox.md +35 -0
  66. package/share/host-skills/opencode/agentbox.md +26 -0
  67. package/dist/_cloud-attach-DMVH6GWO.js +0 -12
  68. package/dist/chunk-7KOEFGN2.js.map +0 -1
  69. package/dist/chunk-NAVL4R34.js.map +0 -1
  70. package/dist/chunk-NW5NYTQM.js.map +0 -1
  71. package/dist/chunk-UK72UQ5U.js.map +0 -1
  72. package/dist/chunk-V5KZGB5V.js.map +0 -1
  73. package/dist/dist-QZGJIBT5.js.map +0 -1
  74. package/dist/dist-R67WMLCF.js.map +0 -1
  75. /package/dist/{_cloud-attach-DMVH6GWO.js.map → _cloud-attach-ZXBCNWJX.js.map} +0 -0
  76. /package/dist/{cloud-poller-ZIWSADJB-JXFRJUEM.js.map → cloud-poller-SUNA6ZQC-2RG5WPRN.js.map} +0 -0
  77. /package/dist/{dist-ETCFRVPA.js.map → dist-32EZBYG4.js.map} +0 -0
@@ -0,0 +1,662 @@
1
+ #!/usr/bin/env node
2
+
3
+ // ../../packages/sandbox-vercel/dist/chunk-S2P2E54H.js
4
+ import { existsSync, readFileSync } from "fs";
5
+ import { homedir } from "os";
6
+ import { resolve } from "path";
7
+ import { spawnSync } from "child_process";
8
+ import { execa } from "execa";
9
+ import {
10
+ chmodSync,
11
+ existsSync as existsSync3,
12
+ mkdirSync,
13
+ readFileSync as readFileSync3,
14
+ renameSync,
15
+ writeFileSync
16
+ } from "fs";
17
+ import { homedir as homedir3 } from "os";
18
+ import { dirname, resolve as resolve2 } from "path";
19
+ import {
20
+ confirm,
21
+ isCancel,
22
+ intro,
23
+ log,
24
+ note,
25
+ outro,
26
+ password,
27
+ select,
28
+ spinner,
29
+ text
30
+ } from "@clack/prompts";
31
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
32
+ import { homedir as homedir2 } from "os";
33
+ import { join } from "path";
34
+ import { Sandbox, Snapshot } from "@vercel/sandbox";
35
+ var VERCEL_KEYS = [
36
+ "VERCEL_OIDC_TOKEN",
37
+ "VERCEL_TOKEN",
38
+ "VERCEL_TEAM_ID",
39
+ "VERCEL_PROJECT_ID",
40
+ // Marker for CLI-login mode (`agentbox vercel login` → `sandbox login`). The
41
+ // access token is NOT stored here — it's read live from the Vercel CLI store;
42
+ // only this marker + team/project ids are persisted.
43
+ "VERCEL_AUTH_SOURCE"
44
+ ];
45
+ var loaded = false;
46
+ function ensureVercelEnvLoaded() {
47
+ if (loaded) return;
48
+ loaded = true;
49
+ importVercelFromFile(resolve(homedir(), ".agentbox", "secrets.env"), VERCEL_KEYS);
50
+ }
51
+ function reloadVercelEnv() {
52
+ loaded = false;
53
+ ensureVercelEnvLoaded();
54
+ }
55
+ function importVercelFromFile(path, keys) {
56
+ if (!existsSync(path)) return;
57
+ let body;
58
+ try {
59
+ body = readFileSync(path, "utf8");
60
+ } catch {
61
+ return;
62
+ }
63
+ const parsed = parseEnvFile(body);
64
+ for (const key of keys) {
65
+ if (process.env[key] !== void 0) continue;
66
+ const value = parsed[key];
67
+ if (typeof value === "string") {
68
+ process.env[key] = value;
69
+ }
70
+ }
71
+ }
72
+ function parseEnvFile(body) {
73
+ const out = {};
74
+ for (const rawLine of body.split(/\r?\n/)) {
75
+ const line = rawLine.trim();
76
+ if (line.length === 0 || line.startsWith("#")) continue;
77
+ const stripped = line.startsWith("export ") ? line.slice("export ".length) : line;
78
+ const eq = stripped.indexOf("=");
79
+ if (eq <= 0) continue;
80
+ const key = stripped.slice(0, eq).trim();
81
+ let value = stripped.slice(eq + 1).trim();
82
+ if (value.length >= 2 && (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'"))) {
83
+ value = value.slice(1, -1);
84
+ }
85
+ out[key] = value;
86
+ }
87
+ return out;
88
+ }
89
+ var SBX_BINS = ["sbx", "sandbox"];
90
+ var cached = null;
91
+ async function detectSbx() {
92
+ if (cached !== null) return cached;
93
+ for (const bin of SBX_BINS) {
94
+ try {
95
+ const r = await execa(bin, ["--version"], { reject: false });
96
+ if (r.exitCode === 0) {
97
+ cached = { installed: true, bin, version: (r.stdout ?? "").trim() || void 0 };
98
+ return cached;
99
+ }
100
+ } catch {
101
+ }
102
+ }
103
+ cached = { installed: false };
104
+ return cached;
105
+ }
106
+ function resetSbxCache() {
107
+ cached = null;
108
+ }
109
+ function installSbxHint() {
110
+ return "npm install -g sandbox";
111
+ }
112
+ async function installSbx() {
113
+ try {
114
+ const r = await execa("npm", ["install", "-g", "sandbox"], { reject: false });
115
+ return r.exitCode === 0;
116
+ } catch {
117
+ return false;
118
+ }
119
+ }
120
+ function loginSbx(bin) {
121
+ const r = spawnSync(bin, ["login"], { stdio: "inherit" });
122
+ return r.status ?? 1;
123
+ }
124
+ async function refreshSbxToken(bin) {
125
+ try {
126
+ const r = await execa(bin, ["list"], {
127
+ reject: false,
128
+ timeout: 3e4,
129
+ stdin: "ignore"
130
+ });
131
+ return r.exitCode === 0;
132
+ } catch {
133
+ return false;
134
+ }
135
+ }
136
+ function vercelCliDir() {
137
+ const override = process.env.AGENTBOX_VERCEL_CLI_DIR;
138
+ if (override && override.trim().length > 0) return override.trim();
139
+ const name = "com.vercel.cli";
140
+ if (process.platform === "darwin") {
141
+ return join(homedir2(), "Library", "Application Support", name);
142
+ }
143
+ if (process.platform === "win32") {
144
+ const appData = process.env.APPDATA;
145
+ if (appData && appData.trim().length > 0) return join(appData, name);
146
+ return join(homedir2(), "AppData", "Roaming", name);
147
+ }
148
+ const xdg = process.env.XDG_DATA_HOME;
149
+ const base = xdg && xdg.trim().length > 0 ? xdg.trim() : join(homedir2(), ".local", "share");
150
+ return join(base, name);
151
+ }
152
+ function cliStorePaths() {
153
+ const dir = vercelCliDir();
154
+ return { authPath: join(dir, "auth.json"), configPath: join(dir, "config.json") };
155
+ }
156
+ function readJson(path) {
157
+ if (!existsSync2(path)) return null;
158
+ try {
159
+ return JSON.parse(readFileSync2(path, "utf8"));
160
+ } catch {
161
+ return null;
162
+ }
163
+ }
164
+ function readCliAuth() {
165
+ const raw = readJson(cliStorePaths().authPath);
166
+ if (!raw || typeof raw.token !== "string" || raw.token.length === 0) return null;
167
+ return {
168
+ token: raw.token,
169
+ expiresAt: typeof raw.expiresAt === "number" ? raw.expiresAt : void 0,
170
+ refreshToken: typeof raw.refreshToken === "string" ? raw.refreshToken : void 0
171
+ };
172
+ }
173
+ function readCliCurrentTeam() {
174
+ const raw = readJson(cliStorePaths().configPath);
175
+ return raw && typeof raw.currentTeam === "string" && raw.currentTeam.length > 0 ? raw.currentTeam : null;
176
+ }
177
+ function isNearExpiry(auth, skewSec = 120) {
178
+ if (auth.expiresAt === void 0) return true;
179
+ return auth.expiresAt * 1e3 < Date.now() + skewSec * 1e3;
180
+ }
181
+ function resolveCredentials() {
182
+ ensureVercelEnvLoaded();
183
+ const oidc = process.env.VERCEL_OIDC_TOKEN;
184
+ if (oidc) {
185
+ const claims = decodeOidcClaims(oidc);
186
+ if (!claims) {
187
+ throw new Error(
188
+ "VERCEL_OIDC_TOKEN is set but could not be decoded (not a valid Vercel OIDC JWT). Re-run `vercel env pull`, or use the VERCEL_TOKEN + VERCEL_TEAM_ID + VERCEL_PROJECT_ID trio."
189
+ );
190
+ }
191
+ if (claims.exp !== void 0 && claims.exp * 1e3 < Date.now()) {
192
+ throw new Error(
193
+ "VERCEL_OIDC_TOKEN has expired (Vercel dev OIDC tokens last ~12h). Re-run `vercel env pull` to refresh it, then retry."
194
+ );
195
+ }
196
+ return { token: oidc, teamId: claims.teamId, projectId: claims.projectId };
197
+ }
198
+ if (process.env.VERCEL_AUTH_SOURCE === "cli") {
199
+ const auth = readCliAuth();
200
+ if (!auth) {
201
+ throw new Error(
202
+ "Vercel CLI session not found \u2014 run `agentbox vercel login` (or `sbx login`) to sign in again."
203
+ );
204
+ }
205
+ const teamId2 = process.env.VERCEL_TEAM_ID ?? readCliCurrentTeam() ?? void 0;
206
+ const projectId2 = process.env.VERCEL_PROJECT_ID;
207
+ if (!teamId2 || !projectId2) {
208
+ throw new Error(
209
+ "Vercel CLI auth is missing the team/project id \u2014 re-run `agentbox vercel login`."
210
+ );
211
+ }
212
+ return { token: auth.token, teamId: teamId2, projectId: projectId2 };
213
+ }
214
+ const token = process.env.VERCEL_TOKEN;
215
+ const teamId = process.env.VERCEL_TEAM_ID;
216
+ const projectId = process.env.VERCEL_PROJECT_ID;
217
+ if (token && teamId && projectId) return { token, teamId, projectId };
218
+ throw new Error(
219
+ "Vercel credentials not configured.\nEither run `vercel link && vercel env pull` to get a VERCEL_OIDC_TOKEN, or set VERCEL_TOKEN + VERCEL_TEAM_ID + VERCEL_PROJECT_ID (see `agentbox vercel login`)."
220
+ );
221
+ }
222
+ function decodeOidcClaims(token) {
223
+ const parts = token.split(".");
224
+ if (parts.length < 2 || !parts[1]) return null;
225
+ try {
226
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
227
+ if (typeof payload.owner_id !== "string" || typeof payload.project_id !== "string") return null;
228
+ return {
229
+ teamId: payload.owner_id,
230
+ projectId: payload.project_id,
231
+ exp: typeof payload.exp === "number" ? payload.exp : void 0
232
+ };
233
+ } catch {
234
+ return null;
235
+ }
236
+ }
237
+ function hasUsableCredentials() {
238
+ ensureVercelEnvLoaded();
239
+ if (process.env.VERCEL_OIDC_TOKEN) return true;
240
+ if (process.env.VERCEL_AUTH_SOURCE === "cli" && process.env.VERCEL_TEAM_ID && process.env.VERCEL_PROJECT_ID && readCliAuth()) {
241
+ return true;
242
+ }
243
+ return Boolean(
244
+ process.env.VERCEL_TOKEN && process.env.VERCEL_TEAM_ID && process.env.VERCEL_PROJECT_ID
245
+ );
246
+ }
247
+ var inflightRefresh = null;
248
+ function ensureFreshCredentials() {
249
+ ensureVercelEnvLoaded();
250
+ if (process.env.VERCEL_AUTH_SOURCE !== "cli") return Promise.resolve();
251
+ if (inflightRefresh) return inflightRefresh;
252
+ inflightRefresh = refreshCliToken().finally(() => {
253
+ inflightRefresh = null;
254
+ });
255
+ return inflightRefresh;
256
+ }
257
+ async function refreshCliToken() {
258
+ const auth = readCliAuth();
259
+ if (!auth) return;
260
+ if (!isNearExpiry(auth)) return;
261
+ const det = await detectSbx();
262
+ if (!det.installed || !det.bin) {
263
+ throw new Error(
264
+ "Vercel access token is near expiry and the `sandbox` CLI is no longer installed \u2014 reinstall it (`npm install -g sandbox`) or run `agentbox vercel login`."
265
+ );
266
+ }
267
+ const ok = await refreshSbxToken(det.bin);
268
+ if (!ok) {
269
+ throw new Error(
270
+ "Vercel token refresh failed \u2014 run `agentbox vercel login` (the refresh token may have expired)."
271
+ );
272
+ }
273
+ const fresh = readCliAuth();
274
+ if (!fresh || isNearExpiry(fresh, 0)) {
275
+ throw new Error(
276
+ "Vercel token is still stale after a refresh attempt \u2014 run `agentbox vercel login`."
277
+ );
278
+ }
279
+ }
280
+ var API = "https://api.vercel.com";
281
+ var VercelApiError = class extends Error {
282
+ constructor(status, message) {
283
+ super(message);
284
+ this.status = status;
285
+ this.name = "VercelApiError";
286
+ }
287
+ status;
288
+ };
289
+ async function api(token, path, init) {
290
+ const res = await fetch(`${API}${path}`, {
291
+ method: init?.method ?? "GET",
292
+ headers: {
293
+ Authorization: `Bearer ${token}`,
294
+ ...init?.body ? { "Content-Type": "application/json" } : {}
295
+ },
296
+ body: init?.body ? JSON.stringify(init.body) : void 0
297
+ });
298
+ const text2 = await res.text();
299
+ let json = null;
300
+ try {
301
+ json = text2 ? JSON.parse(text2) : null;
302
+ } catch {
303
+ json = null;
304
+ }
305
+ if (!res.ok) {
306
+ const detail = json?.error?.message ?? text2 ?? res.statusText;
307
+ throw new VercelApiError(res.status, `Vercel API ${res.status}: ${detail}`);
308
+ }
309
+ return json;
310
+ }
311
+ async function getUser(token) {
312
+ const json = await api(token, "/v2/user");
313
+ const user = json.user;
314
+ if (!user?.id) throw new Error("Vercel /v2/user returned no user");
315
+ return user;
316
+ }
317
+ async function listProjects(token, teamId, limit = 100) {
318
+ const json = await api(
319
+ token,
320
+ `/v9/projects?teamId=${encodeURIComponent(teamId)}&limit=${limit}`
321
+ );
322
+ return (json.projects ?? []).map((p) => ({ id: p.id, name: p.name }));
323
+ }
324
+ async function createProject(token, teamId, name) {
325
+ try {
326
+ const json = await api(token, `/v9/projects?teamId=${encodeURIComponent(teamId)}`, {
327
+ method: "POST",
328
+ body: { name }
329
+ });
330
+ return { id: json.id, name: json.name };
331
+ } catch (err) {
332
+ if (err instanceof VercelApiError && err.status === 409) {
333
+ const existing = (await listProjects(token, teamId)).find((p) => p.name === name);
334
+ if (existing) return existing;
335
+ }
336
+ throw err;
337
+ }
338
+ }
339
+ var DASHBOARD_TOKENS_URL = "https://vercel.com/account/settings/tokens";
340
+ var MANAGED_KEYS = [
341
+ "VERCEL_OIDC_TOKEN",
342
+ "VERCEL_TOKEN",
343
+ "VERCEL_TEAM_ID",
344
+ "VERCEL_PROJECT_ID",
345
+ "VERCEL_AUTH_SOURCE"
346
+ ];
347
+ async function ensureVercelCredentials(opts = {}) {
348
+ ensureVercelEnvLoaded();
349
+ if (!opts.force && hasUsableCredentials()) return;
350
+ if (!process.stdin.isTTY) return;
351
+ intro("Vercel setup");
352
+ note(
353
+ `AgentBox needs Vercel credentials to provision sandboxes.
354
+ Sign in with Vercel (recommended): drives the Vercel \`sandbox\` CLI through a browser login, then reads the token from the CLI's own store and keeps it fresh \u2014 no token to paste.
355
+ Access token (best for CI / headless): personal access token + team id + project id, saved to \`~/.agentbox/secrets.env\`.
356
+ OIDC (short interactive work): export VERCEL_OIDC_TOKEN in your shell or add it to \`~/.agentbox/secrets.env\` (dev token expires ~12h, no headless refresh).`,
357
+ "Credentials required"
358
+ );
359
+ const mode = await select({
360
+ message: "How do you want to authenticate?",
361
+ options: [
362
+ { value: "cli", label: "Sign in with Vercel (browser) \u2014 recommended for interactive use" },
363
+ { value: "token", label: "Access token (VERCEL_TOKEN + team + project) \u2014 best for CI / headless" },
364
+ { value: "oidc", label: "OIDC token (VERCEL_OIDC_TOKEN in env / secrets.env) \u2014 short interactive work" }
365
+ ],
366
+ initialValue: "cli"
367
+ });
368
+ if (isCancel(mode)) {
369
+ log.warn("Vercel setup cancelled \u2014 re-run `agentbox vercel login` when ready.");
370
+ return;
371
+ }
372
+ if (mode === "cli") {
373
+ await runCliLogin();
374
+ return;
375
+ }
376
+ if (mode === "oidc") {
377
+ note(
378
+ `Get an OIDC token with \`vercel link\` then \`vercel env pull\`, then make it visible to AgentBox by either:
379
+ export VERCEL_OIDC_TOKEN=<token> # in this shell
380
+ echo "VERCEL_OIDC_TOKEN=<token>" >> ~/.agentbox/secrets.env
381
+ Re-do every ~12h; the dev token expires. AgentBox does not harvest .env.local.`,
382
+ "OIDC setup"
383
+ );
384
+ reloadVercelEnv();
385
+ if (process.env.VERCEL_OIDC_TOKEN) {
386
+ log.success("Found VERCEL_OIDC_TOKEN \u2014 Vercel is configured.");
387
+ await ensureSbxInstalled();
388
+ outro("Setup complete.");
389
+ } else {
390
+ log.warn("No VERCEL_OIDC_TOKEN found yet \u2014 set it as above, then re-run `agentbox vercel login`.");
391
+ }
392
+ return;
393
+ }
394
+ const creds = await promptForTokenTrio();
395
+ if (creds === null) return;
396
+ persistCredentials(creds);
397
+ log.success(`Vercel credentials saved to ${secretsPath()}`);
398
+ await ensureSbxInstalled();
399
+ outro("Setup complete.");
400
+ }
401
+ async function promptForTokenTrio() {
402
+ const openIt = await confirm({
403
+ message: `Open ${DASHBOARD_TOKENS_URL} to create a token?`,
404
+ initialValue: true
405
+ });
406
+ if (isCancel(openIt)) return null;
407
+ if (openIt) openDashboard();
408
+ const token = await password({
409
+ message: "Paste your Vercel access token",
410
+ validate: (v) => v && v.trim().length > 0 ? void 0 : "Cannot be empty"
411
+ });
412
+ if (isCancel(token)) {
413
+ log.warn("Vercel setup cancelled.");
414
+ return null;
415
+ }
416
+ const teamId = await text({
417
+ message: "Team ID (team settings \u2192 General)",
418
+ placeholder: "team_...",
419
+ validate: (v) => v && v.trim().length > 0 ? void 0 : "Cannot be empty"
420
+ });
421
+ if (isCancel(teamId)) {
422
+ log.warn("Vercel setup cancelled.");
423
+ return null;
424
+ }
425
+ const projectId = await text({
426
+ message: "Project ID (project settings \u2192 General)",
427
+ placeholder: "prj_...",
428
+ validate: (v) => v && v.trim().length > 0 ? void 0 : "Cannot be empty"
429
+ });
430
+ if (isCancel(projectId)) {
431
+ log.warn("Vercel setup cancelled.");
432
+ return null;
433
+ }
434
+ return { token: token.trim(), teamId: teamId.trim(), projectId: projectId.trim() };
435
+ }
436
+ function persistCredentials(creds) {
437
+ writeManaged({
438
+ VERCEL_TOKEN: creds.token,
439
+ VERCEL_TEAM_ID: creds.teamId,
440
+ VERCEL_PROJECT_ID: creds.projectId
441
+ });
442
+ }
443
+ function persistCliCredentials(ids) {
444
+ writeManaged({
445
+ VERCEL_AUTH_SOURCE: "cli",
446
+ VERCEL_TEAM_ID: ids.teamId,
447
+ VERCEL_PROJECT_ID: ids.projectId
448
+ });
449
+ }
450
+ function writeManaged(record) {
451
+ for (const k of MANAGED_KEYS) delete process.env[k];
452
+ for (const [k, v] of Object.entries(record)) process.env[k] = v;
453
+ const path = secretsPath();
454
+ mkdirSync(dirname(path), { recursive: true });
455
+ let existing = "";
456
+ if (existsSync3(path)) {
457
+ try {
458
+ existing = readFileSync3(path, "utf8");
459
+ } catch {
460
+ existing = "";
461
+ }
462
+ }
463
+ const kept = existing.split(/\r?\n/).filter((line) => {
464
+ const stripped = line.startsWith("export ") ? line.slice("export ".length) : line;
465
+ const eq = stripped.indexOf("=");
466
+ if (eq <= 0) return true;
467
+ const key = stripped.slice(0, eq).trim();
468
+ return !MANAGED_KEYS.includes(key);
469
+ }).join("\n").replace(/\s+$/u, "");
470
+ const lines = Object.entries(record).map(([k, v]) => `${k}=${v}`);
471
+ const body = (kept ? `${kept}
472
+ ` : "") + lines.join("\n") + "\n";
473
+ const tmp = `${path}.tmp`;
474
+ writeFileSync(tmp, body, { mode: 384 });
475
+ try {
476
+ chmodSync(tmp, 384);
477
+ } catch {
478
+ }
479
+ renameSync(tmp, path);
480
+ try {
481
+ chmodSync(path, 384);
482
+ } catch {
483
+ }
484
+ }
485
+ async function ensureSbxInstalled() {
486
+ let det = await detectSbx();
487
+ if (!det.installed) {
488
+ const doInstall = await confirm({
489
+ message: `The Vercel sandbox CLI (needed for interactive attach) isn't installed. Install it now? (${installSbxHint()})`,
490
+ initialValue: true
491
+ });
492
+ if (isCancel(doInstall) || !doInstall) {
493
+ log.warn(
494
+ `Install it with \`${installSbxHint()}\` to use \`agentbox shell|claude|codex|opencode\` on Vercel boxes.`
495
+ );
496
+ return null;
497
+ }
498
+ const sp = spinner();
499
+ sp.start("Installing the Vercel sandbox CLI\u2026");
500
+ const ok = await installSbx();
501
+ resetSbxCache();
502
+ det = await detectSbx();
503
+ if (!ok || !det.installed || !det.bin) {
504
+ sp.stop("Install failed.");
505
+ log.warn(`Could not install the sandbox CLI \u2014 run \`${installSbxHint()}\` manually.`);
506
+ return null;
507
+ }
508
+ sp.stop(`Installed sandbox CLI${det.version ? ` ${det.version}` : ""}.`);
509
+ }
510
+ return det.bin ? { bin: det.bin } : null;
511
+ }
512
+ async function runCliLogin() {
513
+ const det = await ensureSbxInstalled();
514
+ if (!det) {
515
+ log.warn("The Vercel sandbox CLI is required to sign in this way \u2014 install it, then re-run `agentbox vercel login`.");
516
+ return;
517
+ }
518
+ note("A browser window will open to sign in to Vercel.", "Vercel sign-in");
519
+ const status = loginSbx(det.bin);
520
+ if (status !== 0) {
521
+ log.warn("Vercel sign-in did not complete \u2014 re-run `agentbox vercel login` to try again.");
522
+ return;
523
+ }
524
+ const harvested = harvestCliCredentials();
525
+ if (!harvested) {
526
+ log.warn("Sign-in finished but no credentials were found in the Vercel CLI store. Try again.");
527
+ return;
528
+ }
529
+ try {
530
+ await getUser(harvested.token);
531
+ } catch (err) {
532
+ log.warn(
533
+ `The Vercel session looks invalid (${err instanceof Error ? err.message : String(err)}). Re-run \`agentbox vercel login\`.`
534
+ );
535
+ return;
536
+ }
537
+ const projectId = await resolveProjectId(harvested.token, harvested.teamId);
538
+ if (projectId === null) {
539
+ log.warn("No project selected \u2014 re-run `agentbox vercel login` to finish setup.");
540
+ return;
541
+ }
542
+ persistCliCredentials({ teamId: harvested.teamId, projectId });
543
+ reloadVercelEnv();
544
+ log.success(`Signed in with Vercel \u2014 credentials managed by the sandbox CLI (saved scope to ${secretsPath()}).`);
545
+ outro("Setup complete.");
546
+ }
547
+ function harvestCliCredentials() {
548
+ const auth = readCliAuth();
549
+ if (!auth) return null;
550
+ const teamId = process.env.VERCEL_TEAM_ID ?? readCliCurrentTeam();
551
+ if (!teamId) return null;
552
+ return { token: auth.token, teamId };
553
+ }
554
+ async function resolveProjectId(token, teamId) {
555
+ let projects = [];
556
+ const sp = spinner();
557
+ sp.start("Loading your Vercel projects\u2026");
558
+ try {
559
+ projects = await listProjects(token, teamId);
560
+ sp.stop(`Found ${projects.length} project${projects.length === 1 ? "" : "s"}.`);
561
+ } catch (err) {
562
+ sp.stop("Could not list projects.");
563
+ log.warn(`Failed to list Vercel projects: ${err instanceof Error ? err.message : String(err)}`);
564
+ return null;
565
+ }
566
+ const CREATE = "__create__";
567
+ const preferred = projects.find((p) => p.name === "agentbox") ?? projects.find((p) => p.name === "vercel-sandbox-default-project");
568
+ const choice = await select({
569
+ message: "Which Vercel project should sandboxes run under?",
570
+ options: [
571
+ ...projects.map((p) => ({ value: p.id, label: p.name })),
572
+ { value: CREATE, label: "Create a new project\u2026" }
573
+ ],
574
+ initialValue: preferred ? preferred.id : CREATE
575
+ });
576
+ if (isCancel(choice)) return null;
577
+ if (choice !== CREATE) return choice;
578
+ const name = await text({
579
+ message: "New project name",
580
+ placeholder: "agentbox",
581
+ defaultValue: "agentbox",
582
+ validate: (v) => v && v.trim().length > 0 ? void 0 : "Cannot be empty"
583
+ });
584
+ if (isCancel(name)) return null;
585
+ try {
586
+ const created = await createProject(token, teamId, name.trim() || "agentbox");
587
+ return created.id;
588
+ } catch (err) {
589
+ log.warn(`Could not create the project: ${err instanceof Error ? err.message : String(err)}`);
590
+ return null;
591
+ }
592
+ }
593
+ function openDashboard() {
594
+ import("child_process").then(({ spawnSync: spawnSync2 }) => {
595
+ const r = spawnSync2("open", [DASHBOARD_TOKENS_URL], { stdio: "ignore" });
596
+ if (r.status !== 0) {
597
+ log.warn(`Could not auto-open the browser \u2014 visit ${DASHBOARD_TOKENS_URL} manually.`);
598
+ }
599
+ }).catch(() => {
600
+ log.warn(`Could not auto-open the browser \u2014 visit ${DASHBOARD_TOKENS_URL} manually.`);
601
+ });
602
+ }
603
+ function secretsPath() {
604
+ return resolve2(homedir3(), ".agentbox", "secrets.env");
605
+ }
606
+ function readVercelCredStatus() {
607
+ const shellHad = !!process.env.VERCEL_OIDC_TOKEN || !!process.env.VERCEL_TOKEN;
608
+ ensureVercelEnvLoaded();
609
+ const oidc = !!process.env.VERCEL_OIDC_TOKEN;
610
+ const teamId = process.env.VERCEL_TEAM_ID;
611
+ const projectId = process.env.VERCEL_PROJECT_ID;
612
+ if (oidc) {
613
+ return { auth: "oidc", oidc: true, teamId, projectId, source: shellHad ? "env" : "secrets.env" };
614
+ }
615
+ if (process.env.VERCEL_AUTH_SOURCE === "cli") {
616
+ const auth = readCliAuth();
617
+ return {
618
+ auth: "cli",
619
+ oidc: false,
620
+ token: auth?.token,
621
+ teamId,
622
+ projectId,
623
+ source: "cli-store",
624
+ cli: {
625
+ loggedIn: !!auth,
626
+ expiresAt: auth?.expiresAt,
627
+ nearExpiry: auth ? isNearExpiry(auth) : void 0,
628
+ authPath: cliStorePaths().authPath
629
+ }
630
+ };
631
+ }
632
+ const token = process.env.VERCEL_TOKEN;
633
+ if (!token) return { auth: "none", oidc: false, source: "none" };
634
+ return {
635
+ auth: "token",
636
+ oidc: false,
637
+ token,
638
+ teamId,
639
+ projectId,
640
+ source: shellHad ? "env" : "secrets.env"
641
+ };
642
+ }
643
+ function maskKey(value) {
644
+ if (value.length <= 8) return "*".repeat(value.length);
645
+ return `${value.slice(0, 4)}\u2026${"*".repeat(8)}${value.slice(-4)}`;
646
+ }
647
+
648
+ export {
649
+ ensureVercelEnvLoaded,
650
+ reloadVercelEnv,
651
+ detectSbx,
652
+ installSbxHint,
653
+ Sandbox,
654
+ Snapshot,
655
+ resolveCredentials,
656
+ ensureFreshCredentials,
657
+ ensureVercelCredentials,
658
+ secretsPath,
659
+ readVercelCredStatus,
660
+ maskKey
661
+ };
662
+ //# sourceMappingURL=chunk-MTVI44DW.js.map