@computesdk/cli 1.0.16 → 2.0.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/index.js CHANGED
@@ -1,599 +1,15 @@
1
1
  #!/usr/bin/env node
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __esm = (fn, res) => function __init() {
5
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
- };
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
10
- };
11
-
12
- // ../../node_modules/.pnpm/tsup@8.5.0_jiti@2.6.1_postcss@8.5.6_tsx@4.20.3_typescript@5.8.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js
13
- import path from "path";
14
- import { fileURLToPath } from "url";
15
- var init_esm_shims = __esm({
16
- "../../node_modules/.pnpm/tsup@8.5.0_jiti@2.6.1_postcss@8.5.6_tsx@4.20.3_typescript@5.8.3_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js"() {
17
- "use strict";
18
- }
19
- });
20
-
21
- // src/repl.ts
22
- import * as readline from "readline";
23
- import * as path2 from "path";
24
- import * as os from "os";
25
- import * as fs from "fs";
26
- import pc4 from "picocolors";
27
- import { compute } from "computesdk";
28
- async function startREPL(sandbox, provider) {
29
- void provider;
30
- const ctx = {
31
- sandboxes: /* @__PURE__ */ new Map([[sandbox.sandboxId, sandbox]]),
32
- activeSandboxId: sandbox.sandboxId
33
- };
34
- await runSession(ctx);
35
- }
36
- function buildPrompt(sandboxId) {
37
- if (!sandboxId) return pc4.gray("[no sandbox] ") + pc4.bold("$ ");
38
- return pc4.cyan(`[${sandboxId}] `) + pc4.bold("$ ");
39
- }
40
- function runSession(ctx) {
41
- const rl = readline.createInterface({
42
- input: process.stdin,
43
- output: process.stdout,
44
- prompt: buildPrompt(ctx.activeSandboxId),
45
- terminal: true,
46
- historySize: 1e3
47
- });
48
- loadHistory(rl);
49
- return new Promise((resolve) => {
50
- let resolved = false;
51
- const done = (result) => {
52
- if (resolved) return;
53
- resolved = true;
54
- rl.close();
55
- resolve(result);
56
- };
57
- rl.on("SIGINT", () => {
58
- console.log();
59
- rl.prompt();
60
- });
61
- rl.on("close", () => {
62
- done("exit");
63
- });
64
- rl.on("line", async (line) => {
65
- const trimmed = line.trim();
66
- if (!trimmed) {
67
- rl.prompt();
68
- return;
69
- }
70
- if (trimmed === ".exit") {
71
- done("exit");
72
- return;
73
- }
74
- saveToHistory(trimmed);
75
- try {
76
- if (trimmed.startsWith("/")) {
77
- const result = await handleSpecialCommand(ctx, trimmed, rl);
78
- if (result === "exit") {
79
- done(result);
80
- return;
81
- }
82
- } else if (ctx.sandboxes.size === 0) {
83
- console.log(pc4.yellow("No active sandbox. Use /new to create one."));
84
- } else {
85
- const sandbox = ctx.sandboxes.get(ctx.activeSandboxId);
86
- await executeCommand(sandbox, trimmed);
87
- }
88
- } catch (error) {
89
- console.log(pc4.red(`\u2717 ${error.message}`));
90
- }
91
- if (!resolved) {
92
- rl.prompt();
93
- }
94
- });
95
- rl.prompt();
96
- });
97
- }
98
- async function executeCommand(sandbox, command) {
99
- const result = await sandbox.runCommand(command);
100
- if (result.stdout) {
101
- process.stdout.write(result.stdout);
102
- if (!result.stdout.endsWith("\n")) {
103
- console.log();
104
- }
105
- }
106
- if (result.stderr) {
107
- process.stderr.write(pc4.red(result.stderr));
108
- if (!result.stderr.endsWith("\n")) {
109
- console.log();
110
- }
111
- }
112
- if (result.exitCode !== 0) {
113
- console.log(pc4.gray(`exit code: ${result.exitCode}`));
114
- }
115
- }
116
- async function handleSpecialCommand(ctx, input, rl) {
117
- const [cmd, ...args] = input.slice(1).split(/\s+/);
118
- switch (cmd.toLowerCase()) {
119
- case "help":
120
- case "h":
121
- case "?":
122
- showHelp();
123
- return null;
124
- case "info":
125
- case "i": {
126
- if (ctx.sandboxes.size === 0) {
127
- console.log(pc4.yellow("No active sandbox. Use /new to create one."));
128
- return null;
129
- }
130
- const activeSandbox = ctx.sandboxes.get(ctx.activeSandboxId);
131
- console.log();
132
- console.log(pc4.bold(" Sandbox Info"));
133
- console.log(` ID: ${pc4.cyan(activeSandbox.sandboxId)}`);
134
- console.log();
135
- return null;
136
- }
137
- case "url":
138
- case "u": {
139
- if (ctx.sandboxes.size === 0) {
140
- console.log(pc4.yellow("No active sandbox. Use /new to create one."));
141
- return null;
142
- }
143
- const activeSandbox = ctx.sandboxes.get(ctx.activeSandboxId);
144
- if (args.length === 0) {
145
- console.log(pc4.gray("Usage: /url <port>"));
146
- } else {
147
- const port = parseInt(args[0], 10);
148
- if (isNaN(port)) {
149
- console.log(pc4.red("Invalid port number"));
150
- } else {
151
- const url = await activeSandbox.getUrl({ port });
152
- console.log(pc4.cyan(url));
153
- }
154
- }
155
- return null;
156
- }
157
- case "new": {
158
- console.log(pc4.gray(" Creating new sandbox..."));
159
- const newSandbox = await compute.sandbox.create();
160
- ctx.sandboxes.set(newSandbox.sandboxId, newSandbox);
161
- ctx.activeSandboxId = newSandbox.sandboxId;
162
- rl.setPrompt(buildPrompt(ctx.activeSandboxId));
163
- console.log(` Sandbox ready: ${pc4.cyan(newSandbox.sandboxId)}`);
164
- console.log(` ${pc4.gray(`Active sandboxes: ${ctx.sandboxes.size}`)}`);
165
- return null;
166
- }
167
- case "destroy": {
168
- if (args.length === 0) {
169
- console.log(pc4.gray("Usage: /destroy <sandbox-id> or /destroy --all"));
170
- console.log(pc4.gray("Use /list to see all sandboxes"));
171
- return null;
172
- }
173
- if (args[0] === "--all") {
174
- if (ctx.sandboxes.size === 0) {
175
- console.log(pc4.yellow(" No sandboxes to destroy."));
176
- return null;
177
- }
178
- const count = ctx.sandboxes.size;
179
- console.log(pc4.gray(` Destroying ${count} sandbox${count > 1 ? "es" : ""}...`));
180
- await Promise.all(
181
- Array.from(ctx.sandboxes.values()).map((s) => s.destroy())
182
- );
183
- ctx.sandboxes.clear();
184
- ctx.activeSandboxId = "";
185
- rl.setPrompt(buildPrompt(ctx.activeSandboxId));
186
- console.log(` Destroyed ${pc4.cyan(String(count))} sandbox${count > 1 ? "es" : ""}`);
187
- console.log(pc4.yellow(" No sandboxes remaining. Use /new to create one or /exit to quit."));
188
- return null;
189
- }
190
- const targetId = args[0];
191
- const target = ctx.sandboxes.get(targetId);
192
- if (!target) {
193
- const matches = Array.from(ctx.sandboxes.keys()).filter((id) => id.startsWith(targetId));
194
- if (matches.length === 1) {
195
- return destroySandbox(ctx, matches[0], rl);
196
- } else if (matches.length > 1) {
197
- console.log(pc4.yellow(" Ambiguous ID. Matches:"));
198
- for (const id of matches) {
199
- console.log(` ${pc4.cyan(id)}`);
200
- }
201
- } else {
202
- console.log(pc4.red(` Sandbox not found: ${targetId}`));
203
- console.log(pc4.gray(" Use /list to see all sandboxes"));
204
- }
205
- return null;
206
- }
207
- return destroySandbox(ctx, targetId, rl);
208
- }
209
- case "list":
210
- case "ls": {
211
- console.log();
212
- console.log(pc4.bold(" Sandboxes"));
213
- for (const id of ctx.sandboxes.keys()) {
214
- const marker = id === ctx.activeSandboxId ? pc4.green(" \u25CF") : " ";
215
- console.log(` ${marker} ${pc4.cyan(id)}`);
216
- }
217
- console.log();
218
- return null;
219
- }
220
- case "switch":
221
- case "sw": {
222
- if (args.length === 0) {
223
- console.log(pc4.gray("Usage: /switch <sandbox-id>"));
224
- console.log(pc4.gray("Use /list to see all sandboxes"));
225
- return null;
226
- }
227
- const switchId = args[0];
228
- if (ctx.sandboxes.has(switchId)) {
229
- ctx.activeSandboxId = switchId;
230
- rl.setPrompt(buildPrompt(ctx.activeSandboxId));
231
- console.log(` Switched to ${pc4.cyan(switchId)}`);
232
- return null;
233
- }
234
- const matches = Array.from(ctx.sandboxes.keys()).filter((id) => id.startsWith(switchId));
235
- if (matches.length === 1) {
236
- ctx.activeSandboxId = matches[0];
237
- rl.setPrompt(buildPrompt(ctx.activeSandboxId));
238
- console.log(` Switched to ${pc4.cyan(matches[0])}`);
239
- } else if (matches.length > 1) {
240
- console.log(pc4.yellow(" Ambiguous ID. Matches:"));
241
- for (const id of matches) {
242
- console.log(` ${pc4.cyan(id)}`);
243
- }
244
- } else {
245
- console.log(pc4.red(` Sandbox not found: ${switchId}`));
246
- console.log(pc4.gray(" Use /list to see all sandboxes"));
247
- }
248
- return null;
249
- }
250
- case "exit":
251
- case "quit":
252
- case "q":
253
- return "exit";
254
- default:
255
- console.log(pc4.yellow(`Unknown command: /${cmd}`));
256
- console.log(pc4.gray("Type /help for available commands"));
257
- return null;
258
- }
259
- }
260
- async function destroySandbox(ctx, sandboxId, rl) {
261
- const target = ctx.sandboxes.get(sandboxId);
262
- console.log(pc4.gray(` Destroying ${sandboxId}...`));
263
- await target.destroy();
264
- ctx.sandboxes.delete(sandboxId);
265
- console.log(` Destroyed ${pc4.cyan(sandboxId)}`);
266
- if (ctx.activeSandboxId === sandboxId && ctx.sandboxes.size > 0) {
267
- ctx.activeSandboxId = ctx.sandboxes.keys().next().value;
268
- rl.setPrompt(buildPrompt(ctx.activeSandboxId));
269
- console.log(` Switched to ${pc4.cyan(ctx.activeSandboxId)}`);
270
- } else if (ctx.sandboxes.size === 0) {
271
- ctx.activeSandboxId = "";
272
- rl.setPrompt(buildPrompt(ctx.activeSandboxId));
273
- console.log(pc4.yellow(" No sandboxes remaining. Use /new to create one or /exit to quit."));
274
- }
275
- return null;
276
- }
277
- function showHelp() {
278
- console.log();
279
- console.log(pc4.bold(" Shell Mode"));
280
- console.log(pc4.gray(" Just type commands - they run in the active sandbox"));
281
- console.log();
282
- console.log(pc4.white(" ls -la"));
283
- console.log(pc4.white(" cat /etc/os-release"));
284
- console.log(pc4.white(" npm install lodash"));
285
- console.log();
286
- console.log(pc4.bold(" Sandbox Commands") + pc4.gray(" (prefix with /)"));
287
- console.log();
288
- console.log(pc4.cyan(" /new") + pc4.gray(" Create a new sandbox and switch to it"));
289
- console.log(pc4.cyan(" /list") + pc4.gray(" List all sandboxes"));
290
- console.log(pc4.cyan(" /switch <id>") + pc4.gray(" Switch active sandbox"));
291
- console.log(pc4.cyan(" /destroy <id>") + pc4.gray(" Destroy a sandbox by ID"));
292
- console.log(pc4.cyan(" /destroy --all") + pc4.gray(" Destroy all sandboxes"));
293
- console.log();
294
- console.log(pc4.bold(" Other Commands"));
295
- console.log();
296
- console.log(pc4.cyan(" /info") + pc4.gray(" Show active sandbox info"));
297
- console.log(pc4.cyan(" /url <port>") + pc4.gray(" Get public URL for port"));
298
- console.log(pc4.cyan(" /help") + pc4.gray(" Show this help"));
299
- console.log(pc4.cyan(" /exit") + pc4.gray(" Exit the REPL"));
300
- console.log();
301
- }
302
- function loadHistory(rl) {
303
- try {
304
- if (fs.existsSync(HISTORY_FILE)) {
305
- const history = fs.readFileSync(HISTORY_FILE, "utf-8").split("\n").filter((line) => line.trim()).reverse();
306
- for (const line of history.slice(0, 1e3)) {
307
- rl.history?.push(line);
308
- }
309
- }
310
- } catch {
311
- }
312
- }
313
- function saveToHistory(line) {
314
- try {
315
- fs.appendFileSync(HISTORY_FILE, line + "\n");
316
- } catch {
317
- }
318
- }
319
- var HISTORY_FILE;
320
- var init_repl = __esm({
321
- "src/repl.ts"() {
322
- "use strict";
323
- init_esm_shims();
324
- HISTORY_FILE = path2.join(os.homedir(), ".create_compute_history");
325
- }
326
- });
327
-
328
- // src/workspace-cli.ts
329
- var workspace_cli_exports = {};
330
- __export(workspace_cli_exports, {
331
- loadState: () => loadState,
332
- loadWorkspaceState: () => loadWorkspaceState,
333
- sandboxList: () => sandboxList,
334
- saveSandbox: () => saveSandbox,
335
- saveState: () => saveState,
336
- workspaceAttach: () => workspaceAttach,
337
- workspaceCreate: () => workspaceCreate,
338
- workspaceDestroy: () => workspaceDestroy,
339
- workspaceList: () => workspaceList
340
- });
341
- import { compute as compute3 } from "computesdk";
342
- import * as fs3 from "fs";
343
- import * as path4 from "path";
344
- import * as os3 from "os";
345
- import * as p3 from "@clack/prompts";
346
- import pc7 from "picocolors";
347
- function shellEscape(arg) {
348
- if (/^[a-zA-Z0-9._\-/:=@]+$/.test(arg)) return arg;
349
- return `'${arg.replace(/'/g, "'\\''")}'`;
350
- }
351
- function loadState() {
352
- try {
353
- if (!fs3.existsSync(WORKSPACE_STATE_FILE)) {
354
- return { workspaces: {} };
355
- }
356
- const raw = fs3.readFileSync(WORKSPACE_STATE_FILE, "utf-8");
357
- return JSON.parse(raw);
358
- } catch {
359
- return { workspaces: {} };
360
- }
361
- }
362
- function saveState(state) {
363
- const dir = path4.dirname(WORKSPACE_STATE_FILE);
364
- if (!fs3.existsSync(dir)) {
365
- fs3.mkdirSync(dir, { recursive: true, mode: 448 });
366
- }
367
- fs3.writeFileSync(WORKSPACE_STATE_FILE, JSON.stringify(state, null, 2) + "\n", { mode: 384 });
368
- }
369
- function getWorkspaceId(repo, branch) {
370
- return `${repo}:${branch}`;
371
- }
372
- async function workspaceCreate(options) {
373
- const { repo, branch, provider } = options;
374
- const id = getWorkspaceId(repo, branch);
375
- const state = loadState();
376
- if (state.workspaces[id]) {
377
- const existing = state.workspaces[id];
378
- try {
379
- const sandbox2 = await compute3.sandbox.getById(existing.sandboxId);
380
- if (!sandbox2) {
381
- throw new Error("Sandbox not found");
382
- }
383
- existing.lastUsed = (/* @__PURE__ */ new Date()).toISOString();
384
- saveState(state);
385
- p3.log.info(`Attaching to existing workspace: ${pc7.cyan(id)}`);
386
- return existing;
387
- } catch {
388
- delete state.workspaces[id];
389
- }
390
- }
391
- p3.log.info("Creating sandbox...");
392
- const spinner4 = p3.spinner();
393
- spinner4.start("Provisioning via ComputeSDK...");
394
- let sandbox;
395
- try {
396
- sandbox = await compute3.sandbox.create();
397
- spinner4.stop(`Sandbox ready: ${pc7.cyan(sandbox.sandboxId)}`);
398
- } catch (error) {
399
- spinner4.stop(pc7.red("Failed to create sandbox"));
400
- throw error;
401
- }
402
- spinner4.start("Bootstrapping workspace (installing gh CLI, cloning repo)...");
403
- const bootstrapScript = fs3.readFileSync(
404
- new URL("./bootstrap.sh", import.meta.url),
405
- "utf-8"
406
- );
407
- try {
408
- await sandbox.filesystem.writeFile("/tmp/bootstrap.sh", bootstrapScript);
409
- await sandbox.runCommand("chmod +x /tmp/bootstrap.sh");
410
- spinner4.stop("Running bootstrap (you may need to authenticate with GitHub)...");
411
- const result = await sandbox.runCommand(
412
- `/tmp/bootstrap.sh ${shellEscape(repo)} ${shellEscape(branch)}`,
413
- { timeout: 3e5 }
414
- // 5 min timeout for auth
415
- );
416
- const output2 = result.stdout || "";
417
- const jsonMatch = output2.match(/\{[\s\S]*\}/);
418
- let worktreePath = `/workspaces/${path4.basename(repo)}-${branch}`;
419
- if (jsonMatch) {
420
- try {
421
- const bootstrapInfo = JSON.parse(jsonMatch[0]);
422
- worktreePath = bootstrapInfo.worktree_path || worktreePath;
423
- } catch {
424
- }
425
- }
426
- const workspace = {
427
- id,
428
- repo,
429
- branch,
430
- sandboxId: sandbox.sandboxId,
431
- provider: provider || "e2b",
432
- // TODO: Get actual provider
433
- worktreePath,
434
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
435
- lastUsed: (/* @__PURE__ */ new Date()).toISOString()
436
- };
437
- state.workspaces[id] = workspace;
438
- saveState(state);
439
- p3.log.success(`Workspace ready: ${pc7.cyan(id)}`);
440
- p3.log.info(`Sandbox: ${pc7.dim(sandbox.sandboxId)}`);
441
- p3.log.info(`Worktree: ${pc7.dim(worktreePath)}`);
442
- return workspace;
443
- } catch (error) {
444
- spinner4.stop(pc7.red("Bootstrap failed"));
445
- try {
446
- await sandbox.destroy();
447
- } catch {
448
- }
449
- throw error;
450
- }
451
- }
452
- async function workspaceAttach(id) {
453
- const state = loadState();
454
- const workspace = state.workspaces[id];
455
- if (!workspace) {
456
- p3.log.error(`Workspace not found: ${id}`);
457
- p3.log.info(`Run: compute workspace create --repo owner/repo --branch ${id.split(":")[1] || "main"}`);
458
- process.exit(1);
459
- }
460
- const sandbox = await compute3.sandbox.getById(workspace.sandboxId);
461
- if (!sandbox) {
462
- p3.log.error(`Sandbox no longer exists: ${workspace.sandboxId}`);
463
- delete state.workspaces[id];
464
- saveState(state);
465
- process.exit(1);
466
- }
467
- workspace.lastUsed = (/* @__PURE__ */ new Date()).toISOString();
468
- saveState(state);
469
- p3.log.info(`Attaching to workspace: ${pc7.cyan(id)}`);
470
- p3.log.info(`Worktree: ${pc7.dim(workspace.worktreePath)}`);
471
- console.log(pc7.gray(`Tip: commands run at sandbox root; use { cwd: "${workspace.worktreePath}" } on runCommand to scope them.`));
472
- try {
473
- await startREPL(sandbox, workspace.provider);
474
- } catch (error) {
475
- p3.log.error(`Failed to attach: ${error.message}`);
476
- process.exit(1);
477
- }
478
- }
479
- async function sandboxList() {
480
- const state = loadState();
481
- const workspaces = Object.values(state.workspaces);
482
- if (workspaces.length === 0) {
483
- p3.log.info("No sandboxes found.");
484
- p3.log.info("Create one with: compute owner/repo --branch main");
485
- return;
486
- }
487
- console.log();
488
- console.log(pc7.bold("Sandboxes"));
489
- console.log();
490
- for (const ws of workspaces) {
491
- const age = Math.round((Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24));
492
- const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
493
- const status = age > 7 ? pc7.red("stale") : age > 1 ? pc7.yellow("idle") : pc7.green("active");
494
- console.log(` ${status} ${pc7.cyan(ws.sandboxId)}`);
495
- console.log(` Workspace: ${pc7.dim(ws.id)}`);
496
- console.log(` Provider: ${ws.provider}`);
497
- console.log(` Last active: ${pc7.gray(ageStr)}`);
498
- console.log();
499
- }
500
- console.log(pc7.gray('Tip: Use "compute workspaces" to see full workspace details'));
501
- }
502
- async function workspaceList() {
503
- const state = loadState();
504
- const workspaces = Object.values(state.workspaces);
505
- if (workspaces.length === 0) {
506
- p3.log.info("No workspaces found.");
507
- p3.log.info("Create one with: compute owner/repo --branch main");
508
- return;
509
- }
510
- console.log();
511
- console.log(pc7.bold("Workspaces"));
512
- console.log();
513
- for (const ws of workspaces) {
514
- const age = Math.round((Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24));
515
- const ageStr = age === 0 ? "today" : `${age}d ago`;
516
- console.log(` ${pc7.cyan(ws.id)}`);
517
- console.log(` Sandbox: ${pc7.dim(ws.sandboxId)} (${ws.provider})`);
518
- console.log(` Path: ${pc7.dim(ws.worktreePath)}`);
519
- console.log(` Last used: ${pc7.gray(ageStr)}`);
520
- console.log();
521
- }
522
- }
523
- async function saveSandbox(sandbox) {
524
- const state = loadState();
525
- state.workspaces[sandbox.id] = {
526
- id: sandbox.id,
527
- repo: "(no repo)",
528
- branch: "(raw sandbox)",
529
- sandboxId: sandbox.sandboxId,
530
- provider: sandbox.provider,
531
- worktreePath: "/",
532
- createdAt: sandbox.createdAt,
533
- lastUsed: sandbox.lastUsed
534
- };
535
- saveState(state);
536
- }
537
- async function workspaceDestroy(id) {
538
- const state = loadState();
539
- if (id) {
540
- const workspace = state.workspaces[id];
541
- if (!workspace) {
542
- p3.log.error(`Workspace not found: ${id}`);
543
- return;
544
- }
545
- p3.log.info(`Destroying workspace: ${pc7.cyan(id)}`);
546
- try {
547
- await compute3.sandbox.destroy(workspace.sandboxId);
548
- delete state.workspaces[id];
549
- saveState(state);
550
- p3.log.success("Workspace destroyed");
551
- } catch (error) {
552
- p3.log.error(`Failed to destroy: ${error.message}`);
553
- }
554
- } else {
555
- const choices = Object.values(state.workspaces).map((ws) => ({
556
- value: ws.id,
557
- label: `${ws.id} (${ws.provider}, last used ${Math.round((Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24))}d ago)`
558
- }));
559
- if (choices.length === 0) {
560
- p3.log.info("No workspaces to destroy");
561
- return;
562
- }
563
- const selected = await p3.select({
564
- message: "Select workspace to destroy",
565
- options: choices
566
- });
567
- if (p3.isCancel(selected)) {
568
- return;
569
- }
570
- await workspaceDestroy(selected);
571
- }
572
- }
573
- var WORKSPACE_STATE_FILE, loadWorkspaceState;
574
- var init_workspace_cli = __esm({
575
- "src/workspace-cli.ts"() {
576
- "use strict";
577
- init_esm_shims();
578
- init_repl();
579
- WORKSPACE_STATE_FILE = path4.join(os3.homedir(), ".computesdk", "workspaces.json");
580
- loadWorkspaceState = loadState;
581
- }
582
- });
583
2
 
584
3
  // src/index.ts
585
- init_esm_shims();
586
4
  import "dotenv/config";
587
5
  import { Command } from "commander";
588
- import * as p4 from "@clack/prompts";
589
- import pc8 from "picocolors";
590
- import { readFileSync as readFileSync5 } from "fs";
591
- import { fileURLToPath as fileURLToPath2 } from "url";
592
- import { dirname as dirname2, join as join5 } from "path";
593
- import { compute as compute4 } from "computesdk";
6
+ import * as p from "@clack/prompts";
7
+ import pc4 from "picocolors";
8
+ import { readFileSync as readFileSync2 } from "fs";
9
+ import { fileURLToPath } from "url";
10
+ import { dirname, join as join2 } from "path";
594
11
 
595
12
  // src/providers.ts
596
- init_esm_shims();
597
13
  var PROVIDER_ENV = {
598
14
  e2b: { authOptions: [["E2B_API_KEY"]], configKeys: { E2B_API_KEY: "apiKey" } },
599
15
  railway: { authOptions: [["RAILWAY_API_TOKEN"]], configKeys: { RAILWAY_API_TOKEN: "token" } },
@@ -636,18 +52,6 @@ function missingEnvVars(provider) {
636
52
  function isProviderAuthComplete(provider) {
637
53
  return missingEnvVars(provider).length === 0;
638
54
  }
639
- function detectAvailableProviders() {
640
- const available = [];
641
- if (process.env.COMPUTESDK_API_KEY) {
642
- available.push("computesdk");
643
- }
644
- for (const provider of PROVIDER_NAMES) {
645
- if (isProviderAuthComplete(provider)) {
646
- available.push(provider);
647
- }
648
- }
649
- return available;
650
- }
651
55
  function getProviderStatus() {
652
56
  const statuses = [];
653
57
  const hasGatewayKey = !!process.env.COMPUTESDK_API_KEY;
@@ -665,40 +69,13 @@ function getProviderStatus() {
665
69
  }
666
70
  return statuses;
667
71
  }
668
- function buildProviderConfig(provider) {
669
- const config = {
670
- provider,
671
- apiKey: process.env.COMPUTESDK_API_KEY
672
- // Gateway API key (top-level)
673
- };
674
- if (provider === "computesdk") {
675
- config.computesdk = {
676
- computesdk_api_key: process.env.COMPUTESDK_API_KEY
677
- };
678
- return config;
679
- }
680
- const spec = PROVIDER_ENV[provider];
681
- if (spec) {
682
- const providerConfig = {};
683
- for (const [envVar, key] of Object.entries(spec.configKeys)) {
684
- const value = process.env[envVar];
685
- if (value) providerConfig[key] = value;
686
- }
687
- if (Object.keys(providerConfig).length > 0) {
688
- config[provider] = providerConfig;
689
- }
690
- }
691
- return config;
692
- }
693
72
 
694
73
  // src/actions.ts
695
- init_esm_shims();
696
74
  import pc from "picocolors";
697
75
  import { mkdirSync, readFileSync, writeFileSync } from "fs";
698
76
  import { basename, join } from "path";
699
77
 
700
78
  // src/actions-client.ts
701
- init_esm_shims();
702
79
  var DEFAULT_BASE_URL = "https://platform.computesdk.com";
703
80
  var ActionsApiError = class extends Error {
704
81
  constructor(status, message, body) {
@@ -747,11 +124,12 @@ function errorDetails(body) {
747
124
  function toErrorEnvelope(error) {
748
125
  if (error instanceof ActionsApiError) {
749
126
  const details = errorDetails(error.body);
127
+ const insufficientScope = error.message.includes("insufficient_scope") || typeof error.body === "string" && error.body.includes("insufficient_scope") || typeof error.body === "object" && error.body !== null && JSON.stringify(error.body).includes("insufficient_scope");
750
128
  return {
751
129
  ok: false,
752
130
  error: {
753
131
  code: codeForStatus(error.status),
754
- message: error.message,
132
+ message: insufficientScope ? `${error.message} \u2014 run \`compute login\` to sign in again.` : error.message,
755
133
  httpStatus: error.status,
756
134
  retryable: error.status === 429 || error.status === 502 || error.status === 503 || error.status === 504,
757
135
  ...details !== void 0 && { details }
@@ -792,8 +170,8 @@ var ActionsClient = class {
792
170
  this.orgPromise ?? (this.orgPromise = this.get("/api/v1/actions/org"));
793
171
  return this.orgPromise;
794
172
  }
795
- async get(path5, params) {
796
- const url = new URL(`${this.auth.baseUrl}${path5}`);
173
+ async get(path, params) {
174
+ const url = new URL(`${this.auth.baseUrl}${path}`);
797
175
  for (const [key, value] of Object.entries(params ?? {})) {
798
176
  if (value === void 0) continue;
799
177
  for (const v of Array.isArray(value) ? value : [value]) url.searchParams.append(key, v);
@@ -801,32 +179,32 @@ var ActionsClient = class {
801
179
  const res = await this.fetchImpl(url, { headers: this.headers() });
802
180
  return this.parse(res);
803
181
  }
804
- async post(path5, body = {}) {
805
- const res = await this.fetchImpl(`${this.auth.baseUrl}${path5}`, {
182
+ async post(path, body = {}) {
183
+ const res = await this.fetchImpl(`${this.auth.baseUrl}${path}`, {
806
184
  method: "POST",
807
185
  headers: { ...this.headers(), "content-type": "application/json" },
808
186
  body: JSON.stringify(body)
809
187
  });
810
188
  return this.parse(res);
811
189
  }
812
- async put(path5, body = {}) {
813
- const res = await this.fetchImpl(`${this.auth.baseUrl}${path5}`, {
190
+ async put(path, body = {}) {
191
+ const res = await this.fetchImpl(`${this.auth.baseUrl}${path}`, {
814
192
  method: "PUT",
815
193
  headers: { ...this.headers(), "content-type": "application/json" },
816
194
  body: JSON.stringify(body)
817
195
  });
818
196
  return this.parse(res);
819
197
  }
820
- async patch(path5, body = {}) {
821
- const res = await this.fetchImpl(`${this.auth.baseUrl}${path5}`, {
198
+ async patch(path, body = {}) {
199
+ const res = await this.fetchImpl(`${this.auth.baseUrl}${path}`, {
822
200
  method: "PATCH",
823
201
  headers: { ...this.headers(), "content-type": "application/json" },
824
202
  body: JSON.stringify(body)
825
203
  });
826
204
  return this.parse(res);
827
205
  }
828
- async del(path5) {
829
- const res = await this.fetchImpl(`${this.auth.baseUrl}${path5}`, {
206
+ async del(path) {
207
+ const res = await this.fetchImpl(`${this.auth.baseUrl}${path}`, {
830
208
  method: "DELETE",
831
209
  headers: this.headers()
832
210
  });
@@ -836,8 +214,8 @@ var ActionsClient = class {
836
214
  * One `data:` JSON payload per SSE event. `next()` resolves to the payload,
837
215
  * or null when the stream ends. Call `close()` to abort.
838
216
  */
839
- async *sse(path5, params) {
840
- const url = new URL(`${this.auth.baseUrl}${path5}`);
217
+ async *sse(path, params) {
218
+ const url = new URL(`${this.auth.baseUrl}${path}`);
841
219
  for (const [key, value] of Object.entries(params ?? {})) {
842
220
  if (value === void 0) continue;
843
221
  for (const v of Array.isArray(value) ? value : [value]) url.searchParams.append(key, v);
@@ -856,8 +234,8 @@ var ActionsClient = class {
856
234
  * events are distinguished by name rather than payload shape (process
857
235
  * follow: stdout/stderr share a `{chunk}` payload).
858
236
  */
859
- async *sseNamed(path5, params) {
860
- const url = new URL(`${this.auth.baseUrl}${path5}`);
237
+ async *sseNamed(path, params) {
238
+ const url = new URL(`${this.auth.baseUrl}${path}`);
861
239
  for (const [key, value] of Object.entries(params ?? {})) {
862
240
  if (value === void 0) continue;
863
241
  for (const v of Array.isArray(value) ? value : [value]) url.searchParams.append(key, v);
@@ -872,8 +250,8 @@ var ActionsClient = class {
872
250
  yield* readSseNamedEvents(res.body);
873
251
  }
874
252
  /** Download bytes following redirects (artifact URLs 302 to storage). */
875
- async download(path5) {
876
- const res = await this.fetchImpl(`${this.auth.baseUrl}${path5}`, {
253
+ async download(path) {
254
+ const res = await this.fetchImpl(`${this.auth.baseUrl}${path}`, {
877
255
  headers: this.headers(),
878
256
  redirect: "follow"
879
257
  });
@@ -954,7 +332,7 @@ function encodeWatchCursor(id, jobId, step, offset) {
954
332
  const stepPart = step === null ? "-" : step === "runner" ? "runner" : String(step);
955
333
  return `${id}:${jobId}:${stepPart}:${offset}`;
956
334
  }
957
- var NO_CREDENTIALS_HINT = "Set COMPUTE_API_KEY, pass --api-key, or run `compute bench auth login`.";
335
+ var NO_CREDENTIALS_HINT = "Set COMPUTE_API_KEY, pass --api-key, or run `compute login`.";
958
336
  async function resolveActionsAuth(opts, resolveStored = resolveStoredPlatformAuth) {
959
337
  const baseUrl = (opts.baseUrl || process.env.COMPUTE_PLATFORM_URL || process.env.BENCHMARKS_PLATFORM_URL || // legacy name
960
338
  DEFAULT_BASE_URL).replace(/\/+$/, "");
@@ -1389,14 +767,14 @@ function formatRunHistory(history) {
1389
767
  }
1390
768
  return lines.join("\n");
1391
769
  }
1392
- function formatProviderRow(p5) {
1393
- const icon = p5.usable ? pc.green("\u25CF") : pc.gray("\u25CB");
1394
- const name = p5.usable ? pc.white(p5.provider) : pc.gray(p5.provider);
770
+ function formatProviderRow(p2) {
771
+ const icon = p2.usable ? pc.green("\u25CF") : pc.gray("\u25CB");
772
+ const name = p2.usable ? pc.white(p2.provider) : pc.gray(p2.provider);
1395
773
  const flags = [
1396
- p5.credential,
1397
- p5.actCapable ? pc.green("act") : pc.gray("no-act"),
1398
- p5.regions.length > 0 ? `regions: ${p5.regions.join(",")}` : "no region choice",
1399
- p5.position === null ? pc.gray("not in provider order") : `order #${p5.position}`
774
+ p2.credential,
775
+ p2.actCapable ? pc.green("act") : pc.gray("no-act"),
776
+ p2.regions.length > 0 ? `regions: ${p2.regions.join(",")}` : "no region choice",
777
+ p2.position === null ? pc.gray("not in provider order") : `order #${p2.position}`
1400
778
  ].join(" ");
1401
779
  return ` ${icon} ${name} ${flags}`;
1402
780
  }
@@ -1471,13 +849,13 @@ function parseVaultKind(kind) {
1471
849
  if (kind === void 0 || kind === "secret" || kind === "variable") return kind ?? "secret";
1472
850
  throw new ActionsCliError("invalid_argument", `--kind must be secret or variable, got "${kind}".`);
1473
851
  }
1474
- function vaultPath(path5, params) {
852
+ function vaultPath(path, params) {
1475
853
  const search = new URLSearchParams();
1476
854
  for (const [key, value] of Object.entries(params)) {
1477
855
  if (value !== void 0) search.set(key, value);
1478
856
  }
1479
857
  const query = search.toString();
1480
- return query ? `${path5}?${query}` : path5;
858
+ return query ? `${path}?${query}` : path;
1481
859
  }
1482
860
  function vaultSetBody(name, value, opts) {
1483
861
  if (value === "") throw new ActionsCliError("invalid_argument", "The value is empty.");
@@ -1672,9 +1050,9 @@ function registerActionsCommands(program2) {
1672
1050
  process.stdout.write(JSON.stringify({ jobId: job.id, name: job.name, slice }) + "\n");
1673
1051
  } else {
1674
1052
  for (const seg of slice.segments) {
1675
- const text2 = seg.text.replace(/\n/g, `
1053
+ const text = seg.text.replace(/\n/g, `
1676
1054
  [${job.name}] `);
1677
- process.stdout.write(`[${job.name}] ${text2}`);
1055
+ process.stdout.write(`[${job.name}] ${text}`);
1678
1056
  }
1679
1057
  }
1680
1058
  }
@@ -1687,7 +1065,7 @@ function registerActionsCommands(program2) {
1687
1065
  try {
1688
1066
  const data = await (await client(opts)).get("/api/v1/actions/providers");
1689
1067
  output(opts, data, (d) => {
1690
- for (const p5 of d.providers) console.log(formatProviderRow(p5));
1068
+ for (const p2 of d.providers) console.log(formatProviderRow(p2));
1691
1069
  });
1692
1070
  } catch (e) {
1693
1071
  fail(e, opts);
@@ -1839,8 +1217,8 @@ function registerActionsCommands(program2) {
1839
1217
  vault.command("ls").description("List names and metadata (never values)")
1840
1218
  ).action(async (opts) => {
1841
1219
  try {
1842
- const path5 = vaultPath("/api/v1/vault", { repo: opts.repo, kind: parseVaultKind(opts.kind) });
1843
- const data = await (await client(opts)).get(path5);
1220
+ const path = vaultPath("/api/v1/vault", { repo: opts.repo, kind: parseVaultKind(opts.kind) });
1221
+ const data = await (await client(opts)).get(path);
1844
1222
  output(opts, data, (d) => {
1845
1223
  for (const item of d.items) console.log(formatVaultRow(item));
1846
1224
  });
@@ -1894,8 +1272,8 @@ function registerActionsCommands(program2) {
1894
1272
  vault.command("rm").description("Delete an item (with --repo, only the repo's override)").argument("<name>", "item name")
1895
1273
  ).action(async (name, opts) => {
1896
1274
  try {
1897
- const path5 = vaultPath("/api/v1/vault", { repo: opts.repo, kind: parseVaultKind(opts.kind), name });
1898
- const result = await (await client(opts)).del(path5);
1275
+ const path = vaultPath("/api/v1/vault", { repo: opts.repo, kind: parseVaultKind(opts.kind), name });
1276
+ const result = await (await client(opts)).del(path);
1899
1277
  output(opts, result, (r) => console.log(`deleted ${pc.cyan(safeTerm(r.name))} ${pc.dim(`(${r.kind})`)}`));
1900
1278
  } catch (e) {
1901
1279
  fail(e, opts);
@@ -1992,7 +1370,6 @@ function registerActionsCommands(program2) {
1992
1370
  }
1993
1371
 
1994
1372
  // src/market.ts
1995
- init_esm_shims();
1996
1373
  import pc2 from "picocolors";
1997
1374
  var USD_RE = /^\d+(\.\d+)?([eE][+-]?\d+)?$/;
1998
1375
  var RATE_UNITS = ["second", "minute", "hour"];
@@ -2145,24 +1522,24 @@ function formatListingRow(ask) {
2145
1522
  pc2.dim(expiry)
2146
1523
  ].join(" ");
2147
1524
  }
2148
- function formatProviderStatus(p5) {
1525
+ function formatProviderStatus(p2) {
2149
1526
  const lines = [];
2150
- lines.push(`${pc2.bold(safeTerm(p5.name))} ${pc2.dim(`selling ${safeTerm(p5.provider)}`)}`);
2151
- lines.push(`id: ${p5.id}`);
1527
+ lines.push(`${pc2.bold(safeTerm(p2.name))} ${pc2.dim(`selling ${safeTerm(p2.provider)}`)}`);
1528
+ lines.push(`id: ${p2.id}`);
2152
1529
  lines.push(
2153
- `credential: ${p5.executorCredentialConnected ? pc2.green("connected") : pc2.yellow("not connected \u2014 your listings cannot fill until `compute market credential connect`")}`
1530
+ `credential: ${p2.executorCredentialConnected ? pc2.green("connected") : pc2.yellow("not connected \u2014 your listings cannot fill until `compute market credential connect`")}`
2154
1531
  );
2155
- if (p5.revokedAt) lines.push(pc2.red(`revoked: ${p5.revokedAt}`));
1532
+ if (p2.revokedAt) lines.push(pc2.red(`revoked: ${p2.revokedAt}`));
2156
1533
  lines.push(
2157
- `sizes: ${p5.sizes.map((s) => `${s.name} (${s.label})`).join(", ") || "-"}`
1534
+ `sizes: ${p2.sizes.map((s) => `${s.name} (${s.label})`).join(", ") || "-"}`
2158
1535
  );
2159
- lines.push(`regions: ${p5.regions.join(", ") || "-"}`);
2160
- if (p5.credentialFields.length > 0) {
1536
+ lines.push(`regions: ${p2.regions.join(", ") || "-"}`);
1537
+ if (p2.credentialFields.length > 0) {
2161
1538
  lines.push(
2162
- `credential fields: ${p5.credentialFields.map((f) => `${f.name}${f.secret ? " (secret)" : ""}`).join(", ")}`
1539
+ `credential fields: ${p2.credentialFields.map((f) => `${f.name}${f.secret ? " (secret)" : ""}`).join(", ")}`
2163
1540
  );
2164
1541
  }
2165
- lines.push(`created: ${p5.createdAt}`);
1542
+ lines.push(`created: ${p2.createdAt}`);
2166
1543
  return lines.join("\n");
2167
1544
  }
2168
1545
  function formatBook(book) {
@@ -2397,7 +1774,7 @@ function registerMarketCommands(program2) {
2397
1774
  ).action(async (opts) => {
2398
1775
  try {
2399
1776
  const { provider } = await (await client(opts)).get("/api/v1/market/provider");
2400
- output(opts, provider, (p5) => console.log(formatProviderStatus(p5)));
1777
+ output(opts, provider, (p2) => console.log(formatProviderStatus(p2)));
2401
1778
  } catch (e) {
2402
1779
  fail(e, opts);
2403
1780
  }
@@ -2465,7 +1842,6 @@ ${hints}`
2465
1842
  }
2466
1843
 
2467
1844
  // src/sandboxes.ts
2468
- init_esm_shims();
2469
1845
  import pc3 from "picocolors";
2470
1846
  function shellQuote(arg) {
2471
1847
  return `'${arg.replace(/'/g, `'\\''`)}'`;
@@ -2484,10 +1860,10 @@ function printSandbox(s) {
2484
1860
  );
2485
1861
  }
2486
1862
  }
2487
- function printProcess(p5) {
2488
- const exit = p5.status === "exited" ? `exit=${p5.exitCode ?? "?"}${p5.signal ? ` (${p5.signal})` : ""}` : "";
2489
- const stdin = p5.stdin && p5.status === "running" ? " [stdin open]" : "";
2490
- console.log(`${pc3.cyan(p5.jobId)} ${p5.status}${exit ? " " + exit : ""}${stdin} ${p5.command}`);
1863
+ function printProcess(p2) {
1864
+ const exit = p2.status === "exited" ? `exit=${p2.exitCode ?? "?"}${p2.signal ? ` (${p2.signal})` : ""}` : "";
1865
+ const stdin = p2.stdin && p2.status === "running" ? " [stdin open]" : "";
1866
+ console.log(`${pc3.cyan(p2.jobId)} ${p2.status}${exit ? " " + exit : ""}${stdin} ${p2.command}`);
2491
1867
  }
2492
1868
  function printSnapshot(s) {
2493
1869
  const state = s.deletedAt !== null ? ` ${pc3.dim(`(deleted ${s.deletedAt})`)}` : "";
@@ -2610,10 +1986,10 @@ function registerSandboxesCommands(program2) {
2610
1986
  const res = await c.get(
2611
1987
  `/api/v1/sandboxes/${sandboxId}/processes/${jobId}`
2612
1988
  );
2613
- output(opts, res.process, (p5) => {
2614
- if (p5.stdout) process.stdout.write(p5.stdout);
2615
- if (p5.stderr) process.stderr.write(p5.stderr);
2616
- if (!p5.stdout && !p5.stderr) printProcess(p5);
1989
+ output(opts, res.process, (p2) => {
1990
+ if (p2.stdout) process.stdout.write(p2.stdout);
1991
+ if (p2.stderr) process.stderr.write(p2.stderr);
1992
+ if (!p2.stdout && !p2.stderr) printProcess(p2);
2617
1993
  });
2618
1994
  } catch (e) {
2619
1995
  fail(e, opts);
@@ -2690,11 +2066,11 @@ function registerSandboxesCommands(program2) {
2690
2066
  fail(e, opts);
2691
2067
  }
2692
2068
  });
2693
- cmd.command("ls <sandboxId> [path]").description("List a directory (or stat a path) inside the sandbox").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, path5, opts) => {
2069
+ cmd.command("ls <sandboxId> [path]").description("List a directory (or stat a path) inside the sandbox").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, path, opts) => {
2694
2070
  try {
2695
2071
  const c = await client(opts);
2696
2072
  const res = await c.get(`/api/v1/sandboxes/${sandboxId}/files`, {
2697
- path: path5 ?? "/"
2073
+ path: path ?? "/"
2698
2074
  });
2699
2075
  output(opts, res, (view) => {
2700
2076
  if (view.type === "directory") {
@@ -2709,10 +2085,10 @@ function registerSandboxesCommands(program2) {
2709
2085
  fail(e, opts);
2710
2086
  }
2711
2087
  });
2712
- cmd.command("cat <sandboxId> <path>").description("Print a file's contents").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, path5, opts) => {
2088
+ cmd.command("cat <sandboxId> <path>").description("Print a file's contents").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, path, opts) => {
2713
2089
  try {
2714
2090
  const c = await client(opts);
2715
- const res = await c.get(`/api/v1/sandboxes/${sandboxId}/files`, { path: path5 });
2091
+ const res = await c.get(`/api/v1/sandboxes/${sandboxId}/files`, { path });
2716
2092
  output(opts, res, (view) => {
2717
2093
  if (view.type !== "file") {
2718
2094
  console.error(pc3.red(`${view.path} is a directory`));
@@ -2725,7 +2101,7 @@ function registerSandboxesCommands(program2) {
2725
2101
  fail(e, opts);
2726
2102
  }
2727
2103
  });
2728
- cmd.command("write <sandboxId> <path>").description("Write a file inside the sandbox (--content or --file <local>, else stdin)").option("--content <text>", "file contents").option("--file <local>", "read contents from a local file").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, path5, opts) => {
2104
+ cmd.command("write <sandboxId> <path>").description("Write a file inside the sandbox (--content or --file <local>, else stdin)").option("--content <text>", "file contents").option("--file <local>", "read contents from a local file").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, path, opts) => {
2729
2105
  try {
2730
2106
  let content = opts.content;
2731
2107
  if (content === void 0 && opts.file) {
@@ -2747,18 +2123,18 @@ function registerSandboxesCommands(program2) {
2747
2123
  const c = await client(opts);
2748
2124
  const res = await c.post(
2749
2125
  `/api/v1/sandboxes/${sandboxId}/files`,
2750
- { path: path5, content }
2126
+ { path, content }
2751
2127
  );
2752
2128
  output(opts, res, (r) => console.log(`wrote ${r.path}`));
2753
2129
  } catch (e) {
2754
2130
  fail(e, opts);
2755
2131
  }
2756
2132
  });
2757
- cmd.command("mkdir <sandboxId> <path>").description("Create a directory (parents included)").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, path5, opts) => {
2133
+ cmd.command("mkdir <sandboxId> <path>").description("Create a directory (parents included)").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, path, opts) => {
2758
2134
  try {
2759
2135
  const c = await client(opts);
2760
2136
  const res = await c.post(`/api/v1/sandboxes/${sandboxId}/files`, {
2761
- path: path5,
2137
+ path,
2762
2138
  mkdir: true
2763
2139
  });
2764
2140
  output(opts, res, (r) => console.log(`created ${r.path}`));
@@ -2766,11 +2142,11 @@ function registerSandboxesCommands(program2) {
2766
2142
  fail(e, opts);
2767
2143
  }
2768
2144
  });
2769
- cmd.command("rm <sandboxId> <path>").description("Remove a file or directory inside the sandbox").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, path5, opts) => {
2145
+ cmd.command("rm <sandboxId> <path>").description("Remove a file or directory inside the sandbox").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, path, opts) => {
2770
2146
  try {
2771
2147
  const c = await client(opts);
2772
2148
  const res = await c.del(
2773
- `/api/v1/sandboxes/${sandboxId}/files?path=${encodeURIComponent(path5)}`
2149
+ `/api/v1/sandboxes/${sandboxId}/files?path=${encodeURIComponent(path)}`
2774
2150
  );
2775
2151
  output(opts, res, (r) => console.log(`removed ${r.path}`));
2776
2152
  } catch (e) {
@@ -2849,825 +2225,74 @@ async function followProcess(c, sandboxId, jobId) {
2849
2225
  }
2850
2226
 
2851
2227
  // src/index.ts
2852
- init_repl();
2853
-
2854
- // src/auth.ts
2855
- init_esm_shims();
2856
- import * as http from "http";
2857
- import * as crypto from "crypto";
2858
- import { exec } from "child_process";
2859
- import * as fs2 from "fs";
2860
- import * as path3 from "path";
2861
- import * as os2 from "os";
2862
- import * as p from "@clack/prompts";
2863
- import pc5 from "picocolors";
2864
- function getCredentialsDir() {
2865
- return path3.join(os2.homedir(), ".computesdk");
2866
- }
2867
- function getCredentialsFile() {
2868
- return path3.join(getCredentialsDir(), "credentials.json");
2869
- }
2870
- var AUTH_TIMEOUT_MS = 5 * 60 * 1e3;
2871
- var CONSOLE_AUTH_URL = "https://console.computesdk.com/cli/auth";
2872
- var SUCCESS_HTML = `<!DOCTYPE html>
2873
- <html>
2874
- <head><title>ComputeSDK CLI</title></head>
2875
- <body style="font-family: system-ui, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0;">
2876
- <div style="text-align: center;">
2877
- <h1>Authenticated</h1>
2878
- <p>You can close this tab and return to the terminal.</p>
2879
- </div>
2880
- </body>
2881
- </html>`;
2882
- function loadStoredCredentials() {
2883
- try {
2884
- if (!fs2.existsSync(getCredentialsFile())) {
2885
- return null;
2886
- }
2887
- const raw = fs2.readFileSync(getCredentialsFile(), "utf-8");
2888
- const data = JSON.parse(raw);
2889
- if (typeof data.apiKey === "string" && data.apiKey.length > 0) {
2890
- return data.apiKey;
2891
- }
2892
- return null;
2893
- } catch {
2894
- return null;
2895
- }
2896
- }
2897
- function storeCredentials(apiKey) {
2898
- if (!fs2.existsSync(getCredentialsDir())) {
2899
- fs2.mkdirSync(getCredentialsDir(), { recursive: true, mode: 448 });
2900
- }
2901
- fs2.writeFileSync(
2902
- getCredentialsFile(),
2903
- JSON.stringify({ apiKey }, null, 2) + "\n",
2904
- { mode: 384 }
2905
- );
2906
- }
2907
- function clearStoredCredentials() {
2908
- try {
2909
- if (fs2.existsSync(getCredentialsFile())) {
2910
- fs2.unlinkSync(getCredentialsFile());
2911
- }
2912
- } catch {
2913
- }
2914
- }
2915
- function openBrowser(url) {
2916
- const command = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
2917
- exec(command, (err) => {
2918
- if (err) {
2919
- p.log.warn("Could not open browser automatically.");
2920
- p.log.info(`Open this URL manually:
2921
- ${pc5.cyan(url)}`);
2922
- }
2923
- });
2924
- }
2925
- function runBrowserAuthFlow(options) {
2926
- const timeoutMs = options?.timeoutMs ?? AUTH_TIMEOUT_MS;
2927
- const state = crypto.randomBytes(32).toString("hex");
2928
- return new Promise((resolve, reject) => {
2929
- const server = http.createServer();
2930
- const timeout = setTimeout(() => {
2931
- server.close();
2932
- reject(
2933
- new Error("Authentication timed out after 5 minutes. Please try again.")
2934
- );
2935
- }, timeoutMs);
2936
- server.on("request", (req, res) => {
2937
- const url = new URL(req.url || "/", "http://localhost");
2938
- if (req.method !== "GET" || url.pathname !== "/callback") {
2939
- res.writeHead(404, { "Content-Type": "text/plain" });
2940
- res.end("Not Found");
2941
- return;
2942
- }
2943
- const receivedState = url.searchParams.get("state");
2944
- const token = url.searchParams.get("token");
2945
- if (receivedState !== state) {
2946
- res.writeHead(403, { "Content-Type": "text/plain" });
2947
- res.end("Error: State mismatch. Please try authenticating again.");
2948
- return;
2949
- }
2950
- if (!token) {
2951
- res.writeHead(400, { "Content-Type": "text/plain" });
2952
- res.end("Error: No token received.");
2953
- return;
2954
- }
2955
- res.writeHead(200, { "Content-Type": "text/html" });
2956
- res.end(SUCCESS_HTML);
2957
- clearTimeout(timeout);
2958
- server.close();
2959
- resolve(token);
2960
- });
2961
- server.listen(0, "127.0.0.1", () => {
2962
- const addr = server.address();
2963
- if (!addr || typeof addr === "string") {
2964
- clearTimeout(timeout);
2965
- server.close();
2966
- reject(new Error("Failed to start auth server"));
2967
- return;
2968
- }
2969
- const port = addr.port;
2970
- const authUrl = `${CONSOLE_AUTH_URL}?port=${port}&state=${state}`;
2971
- if (options?.onServerReady) {
2972
- options.onServerReady(port, state);
2973
- }
2974
- if (!options?.skipBrowserOpen) {
2975
- openBrowser(authUrl);
2976
- }
2977
- });
2978
- server.on("error", (err) => {
2979
- clearTimeout(timeout);
2980
- reject(new Error(`Auth server error: ${err.message}`));
2981
- });
2982
- });
2983
- }
2984
- async function resolveApiKey(options) {
2985
- if (process.env.COMPUTESDK_API_KEY) {
2986
- return process.env.COMPUTESDK_API_KEY;
2987
- }
2988
- if (!options?.forceLogin) {
2989
- const stored = loadStoredCredentials();
2990
- if (stored) {
2991
- p.log.info(
2992
- `Using stored credentials from ${pc5.dim("~/.computesdk/credentials.json")}`
2993
- );
2994
- return stored;
2995
- }
2996
- }
2997
- p.log.info("No API key found. Starting browser authentication...");
2998
- const spinner4 = p.spinner();
2999
- spinner4.start("Waiting for browser authentication...");
3000
- try {
3001
- const apiKey = await runBrowserAuthFlow();
3002
- spinner4.stop("Authentication successful");
3003
- try {
3004
- storeCredentials(apiKey);
3005
- p.log.success(
3006
- `Credentials saved to ${pc5.dim("~/.computesdk/credentials.json")}`
3007
- );
3008
- } catch {
3009
- p.log.warn("Could not save credentials to disk. You may need to authenticate again next time.");
3010
- }
3011
- return apiKey;
3012
- } catch (error) {
3013
- spinner4.stop(pc5.red("Authentication failed"));
3014
- throw error;
3015
- }
3016
- }
3017
-
3018
- // src/setup.ts
3019
- init_esm_shims();
3020
- import * as p2 from "@clack/prompts";
3021
- import pc6 from "picocolors";
3022
- import { compute as compute2 } from "computesdk";
3023
- async function ensureAuth(options) {
3024
- try {
3025
- const apiKey = await resolveApiKey({ forceLogin: options?.forceLogin });
3026
- process.env.COMPUTESDK_API_KEY = apiKey;
3027
- } catch (error) {
3028
- p2.log.error(error.message);
3029
- p2.log.info(`Get your API key at ${pc6.cyan("https://console.computesdk.com")}`);
3030
- p2.outro(pc6.red("Setup incomplete"));
3031
- process.exit(1);
3032
- }
3033
- }
3034
- async function resolveProvider(providerFlag, options) {
3035
- const interactive = options?.interactive ?? false;
3036
- const available = detectAvailableProviders();
3037
- const providerStatus = getProviderStatus();
3038
- if (providerFlag) {
3039
- const match = providerStatus.find(
3040
- (s) => s.name.toLowerCase() === providerFlag.toLowerCase()
3041
- );
3042
- if (!match) {
3043
- p2.log.error(`Unknown provider: ${providerFlag}`);
3044
- p2.log.info(`Available providers: ${providerStatus.map((s) => s.name).join(", ")}`);
3045
- process.exit(1);
3046
- }
3047
- if (!match.ready) {
3048
- p2.log.error(`Provider ${providerFlag} is not configured`);
3049
- if (match.missing.length > 0) {
3050
- p2.log.info(`Missing env vars: ${match.missing.join(", ")}`);
3051
- }
3052
- process.exit(1);
3053
- }
3054
- return match.name;
3055
- }
3056
- if (available.length === 0) {
3057
- p2.log.error("No provider credentials detected");
3058
- p2.log.info("Set one of: E2B_API_KEY, MODAL_TOKEN_ID, RAILWAY_API_KEY, etc.");
3059
- p2.outro(pc6.red("Setup incomplete"));
3060
- process.exit(1);
3061
- }
3062
- if (available.length === 1) {
3063
- return available[0];
3064
- }
3065
- if (!interactive) {
3066
- p2.log.error("Multiple providers configured. Use --provider to specify one.");
3067
- p2.log.info(`Available: ${available.join(", ")}`);
3068
- process.exit(1);
3069
- }
3070
- const providerOptions = providerStatus.filter((status) => status.ready).map((status) => ({
3071
- value: status.name,
3072
- label: status.name
3073
- }));
3074
- const provider = await p2.select({
3075
- message: "Select provider",
3076
- options: providerOptions
3077
- });
3078
- if (p2.isCancel(provider)) {
3079
- p2.cancel("Cancelled");
3080
- process.exit(0);
3081
- }
3082
- return provider;
3083
- }
3084
- function configureCompute(provider) {
3085
- const config = buildProviderConfig(provider);
3086
- compute2.setConfig(config);
3087
- }
3088
-
3089
- // src/index.ts
3090
- init_workspace_cli();
3091
- var __filename2 = fileURLToPath2(import.meta.url);
3092
- var __dirname2 = dirname2(__filename2);
2228
+ var __filename2 = fileURLToPath(import.meta.url);
2229
+ var __dirname2 = dirname(__filename2);
3093
2230
  var packageJson = JSON.parse(
3094
- readFileSync5(join5(__dirname2, "..", "package.json"), "utf-8")
2231
+ readFileSync2(join2(__dirname2, "..", "package.json"), "utf-8")
3095
2232
  );
3096
2233
  var isDevBuild = import.meta.url.includes("worktrees") || import.meta.url.includes("/packages/cli/");
3097
2234
  var VERSION = isDevBuild ? `${packageJson.version}-dev` : packageJson.version;
3098
2235
  var program = new Command();
3099
- program.name("compute").description("Cloud-native workspace management").version(VERSION).enablePositionalOptions().passThroughOptions();
2236
+ program.name("compute").description("ComputeSDK CLI").version(VERSION).enablePositionalOptions().passThroughOptions();
2237
+ async function runLogin(options) {
2238
+ const { oauthLogin } = await import("@benchsdk/cli");
2239
+ await oauthLogin({
2240
+ baseUrl: options?.baseUrl ?? process.env.COMPUTE_PLATFORM_URL ?? process.env.BENCHMARKS_PLATFORM_URL
2241
+ });
2242
+ }
2243
+ async function runLogout() {
2244
+ const { clearCredentials } = await import("@benchsdk/cli");
2245
+ await clearCredentials();
2246
+ }
3100
2247
  program.option("--login", "force re-authentication").option("--logout", "clear stored credentials").action(async (opts) => {
3101
2248
  if (opts.logout) {
3102
2249
  console.log();
3103
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
3104
- clearStoredCredentials();
3105
- p4.log.success("Logged out. Stored credentials removed.");
3106
- p4.outro(pc8.green("Done!"));
2250
+ p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
2251
+ await runLogout();
2252
+ p.log.success("Logged out. Stored credentials removed.");
2253
+ p.outro(pc4.green("Done!"));
3107
2254
  process.exit(0);
3108
2255
  }
3109
2256
  if (opts.login) {
3110
2257
  console.log();
3111
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
3112
- await ensureAuth({ forceLogin: true });
3113
- p4.outro(pc8.green("Authenticated!"));
2258
+ p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
2259
+ await runLogin();
2260
+ p.outro(pc4.green("Authenticated!"));
3114
2261
  process.exit(0);
3115
2262
  }
3116
2263
  program.help();
3117
2264
  });
3118
- program.command("create").description("Create a new workspace (same as create-compute)").argument("[repo]", "Repository in owner/repo format").option("-b, --branch <branch>", "Branch name", "main").option("-p, --provider <provider>", "provider to use").option("--no-attach", "Create workspace but do not attach").action(async (repo, options) => {
3119
- console.log();
3120
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
3121
- await ensureAuth();
3122
- if (!repo) {
3123
- const state = loadWorkspaceState();
3124
- const workspaceIds = Object.keys(state.workspaces);
3125
- if (workspaceIds.length > 0) {
3126
- const choices = workspaceIds.map((id) => {
3127
- const ws = state.workspaces[id];
3128
- const age = Math.round(
3129
- (Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24)
3130
- );
3131
- const ageStr = age === 0 ? "today" : `${age}d ago`;
3132
- return {
3133
- value: id,
3134
- label: `${id} (${ws.provider}, ${ageStr})`
3135
- };
3136
- });
3137
- const selected = await p4.select({
3138
- message: "Select workspace to attach (or create new)",
3139
- options: [
3140
- ...choices,
3141
- { value: "__new__", label: pc8.gray("+ Create new workspace") }
3142
- ]
3143
- });
3144
- if (p4.isCancel(selected)) {
3145
- p4.cancel("Cancelled");
3146
- process.exit(0);
3147
- }
3148
- if (selected !== "__new__") {
3149
- await workspaceAttach(selected);
3150
- return;
3151
- }
3152
- }
3153
- const repoInput = await p4.text({
3154
- message: "Repository to work on?",
3155
- placeholder: "owner/repo",
3156
- validate: (value) => {
3157
- if (!value || !value.includes("/")) {
3158
- return "Please enter a repository in owner/repo format";
3159
- }
3160
- }
3161
- });
3162
- if (p4.isCancel(repoInput)) {
3163
- p4.cancel("Cancelled");
3164
- process.exit(0);
3165
- }
3166
- repo = repoInput;
3167
- }
3168
- if (!repo.includes("/")) {
3169
- p4.log.error("Invalid repository format. Use: owner/repo");
3170
- process.exit(1);
3171
- }
3172
- const provider = await resolveProvider(options.provider, { interactive: true });
3173
- configureCompute(provider);
3174
- const spinner4 = p4.spinner();
3175
- spinner4.start("Creating workspace...");
3176
- try {
3177
- const workspace = await workspaceCreate({
3178
- repo,
3179
- branch: options.branch,
3180
- provider
3181
- });
3182
- spinner4.stop(`Workspace ready: ${pc8.cyan(workspace.id)}`);
3183
- if (options.attach !== false) {
3184
- console.log();
3185
- await workspaceAttach(workspace.id);
3186
- } else {
3187
- p4.outro(pc8.green("Workspace created!"));
3188
- }
3189
- } catch (error) {
3190
- spinner4.stop(pc8.red("Failed to create workspace"));
3191
- p4.log.error(error.message);
3192
- process.exit(1);
3193
- }
3194
- });
3195
- var sandboxCmd = program.command("sandbox").alias("sb").description("Manage raw sandboxes (VMs without git/worktree)");
3196
- sandboxCmd.command("create").description("Create a new sandbox").option("-p, --provider <provider>", "provider to use").option("--timeout <duration>", "sandbox timeout (e.g. 5m, 1h)").option("--name <name>", "sandbox name").option("--connect", "drop into the REPL after creation").option("--silent", "only output the sandbox ID").action(async (opts) => {
3197
- if (!opts.silent) {
3198
- console.log();
3199
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
3200
- }
3201
- await ensureAuth();
3202
- const provider = await resolveProvider(opts.provider);
3203
- configureCompute(provider);
3204
- if (!opts.silent) {
3205
- const spinner4 = p4.spinner();
3206
- spinner4.start("Creating sandbox...");
3207
- try {
3208
- const sandbox = await compute4.sandbox.create(
3209
- buildCreateOptions(opts)
3210
- );
3211
- spinner4.stop(`Sandbox ready: ${pc8.cyan(sandbox.sandboxId)}`);
3212
- const { saveSandbox: saveSandbox2 } = await Promise.resolve().then(() => (init_workspace_cli(), workspace_cli_exports));
3213
- await saveSandbox2({
3214
- id: sandbox.sandboxId,
3215
- sandboxId: sandbox.sandboxId,
3216
- provider,
3217
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3218
- lastUsed: (/* @__PURE__ */ new Date()).toISOString()
3219
- });
3220
- if (opts.connect) {
3221
- await startREPL(sandbox, provider);
3222
- }
3223
- } catch (error) {
3224
- spinner4.stop(pc8.red("Failed to create sandbox"));
3225
- p4.log.error(error.message);
3226
- process.exit(1);
3227
- }
3228
- } else {
3229
- try {
3230
- const sandbox = await compute4.sandbox.create(
3231
- buildCreateOptions(opts)
3232
- );
3233
- process.stdout.write(sandbox.sandboxId);
3234
- const { saveSandbox: saveSandbox2 } = await Promise.resolve().then(() => (init_workspace_cli(), workspace_cli_exports));
3235
- await saveSandbox2({
3236
- id: sandbox.sandboxId,
3237
- sandboxId: sandbox.sandboxId,
3238
- provider,
3239
- createdAt: (/* @__PURE__ */ new Date()).toISOString(),
3240
- lastUsed: (/* @__PURE__ */ new Date()).toISOString()
3241
- });
3242
- if (opts.connect) {
3243
- await startREPL(sandbox, provider);
3244
- }
3245
- } catch (error) {
3246
- process.stderr.write(error.message + "\n");
3247
- process.exit(1);
3248
- }
3249
- }
3250
- });
3251
- sandboxCmd.command("destroy").description("Destroy one or more sandboxes").aliases(["rm", "stop"]).argument("[sandbox_id...]", "sandbox IDs to destroy (optional - will show picker if not provided)").option("-p, --provider <provider>", "provider to use").action(async (sandboxIds, opts) => {
3252
- console.log();
3253
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
3254
- const { loadState: loadState2, saveState: saveState2 } = await Promise.resolve().then(() => (init_workspace_cli(), workspace_cli_exports));
3255
- const state = loadState2();
3256
- if (!sandboxIds || sandboxIds.length === 0) {
3257
- const workspaces = Object.values(state.workspaces);
3258
- if (workspaces.length === 0) {
3259
- p4.log.info("No sandboxes to destroy.");
3260
- p4.outro(pc8.green("Done!"));
3261
- return;
3262
- }
3263
- const choices = workspaces.map((ws) => {
3264
- const age = Math.round(
3265
- (Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24)
3266
- );
3267
- const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
3268
- const status = age > 7 ? pc8.red("stale") : age > 1 ? pc8.yellow("idle") : pc8.green("active");
3269
- return {
3270
- value: ws.sandboxId,
3271
- label: `${status} ${ws.sandboxId} (${ws.provider}, last active ${ageStr})`
3272
- };
3273
- });
3274
- const selected = await p4.multiselect({
3275
- message: "Select sandboxes to destroy (space to select, enter to confirm)",
3276
- options: choices
3277
- });
3278
- if (p4.isCancel(selected) || selected.length === 0) {
3279
- p4.cancel("Cancelled");
3280
- process.exit(0);
3281
- }
3282
- sandboxIds = selected;
3283
- }
3284
- await ensureAuth();
3285
- const provider = await resolveProvider(opts.provider);
3286
- configureCompute(provider);
3287
- const results = await Promise.allSettled(
3288
- sandboxIds.map(async (id) => {
3289
- await compute4.sandbox.destroy(id);
3290
- return id;
3291
- })
3292
- );
3293
- for (const result of results) {
3294
- if (result.status === "fulfilled") {
3295
- const sandboxId = result.value;
3296
- console.log(pc8.green(`Destroyed ${sandboxId}`));
3297
- const workspaceId = Object.keys(state.workspaces).find(
3298
- (key) => state.workspaces[key].sandboxId === sandboxId
3299
- );
3300
- if (workspaceId) {
3301
- delete state.workspaces[workspaceId];
3302
- }
3303
- } else {
3304
- console.error(pc8.red(`Failed to destroy: ${result.reason?.message || result.reason}`));
3305
- }
3306
- }
3307
- saveState2(state);
3308
- });
3309
- sandboxCmd.command("list").alias("ls").description("List all sandboxes").action(async () => {
3310
- console.log();
3311
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
3312
- await sandboxList();
3313
- });
3314
- sandboxCmd.command("connect").description("Connect to a sandbox (opens REPL; same as compute connect)").aliases(["ssh", "shell"]).argument("[sandbox_id]", "sandbox ID (optional - will show picker if not provided)").option("-p, --provider <provider>", "provider to use").action(async (sandboxId, opts) => {
3315
- console.log();
3316
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
3317
- await ensureAuth();
3318
- const provider = await resolveProvider(opts.provider);
3319
- configureCompute(provider);
3320
- if (!sandboxId) {
3321
- const { loadState: loadState2 } = await Promise.resolve().then(() => (init_workspace_cli(), workspace_cli_exports));
3322
- const state = loadState2();
3323
- const workspaces = Object.values(state.workspaces);
3324
- if (workspaces.length === 0) {
3325
- p4.log.info("No sandboxes found.");
3326
- p4.log.info("Create one with: compute sandbox create");
3327
- p4.outro(pc8.green("Done!"));
3328
- return;
3329
- }
3330
- const choices = workspaces.map((ws) => {
3331
- const age = Math.round(
3332
- (Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24)
3333
- );
3334
- const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
3335
- const status = age > 7 ? pc8.red("stale") : age > 1 ? pc8.yellow("idle") : pc8.green("active");
3336
- return {
3337
- value: ws.sandboxId,
3338
- label: `${status} ${ws.sandboxId} (${ws.provider}, last active ${ageStr})`
3339
- };
3340
- });
3341
- const selected = await p4.select({
3342
- message: "Select sandbox to connect",
3343
- options: choices
3344
- });
3345
- if (p4.isCancel(selected)) {
3346
- p4.cancel("Cancelled");
3347
- process.exit(0);
3348
- }
3349
- sandboxId = selected;
3350
- }
3351
- const spinner4 = p4.spinner();
3352
- spinner4.start("Connecting...");
3353
- const sandbox = await compute4.sandbox.getById(sandboxId);
3354
- if (!sandbox) {
3355
- spinner4.stop(pc8.red(`Sandbox not found: ${sandboxId}`));
3356
- process.exit(1);
3357
- }
3358
- spinner4.stop("");
3359
- await startREPL(sandbox, provider);
3360
- });
3361
- sandboxCmd.command("exec").description("Execute a command in a sandbox").argument("<sandbox_id>", "sandbox ID").argument("<command...>", "command to run").option("-p, --provider <provider>", "provider to use").option("-e, --env <key=value...>", "environment variables").option("-w, --workdir <directory>", "working directory").passThroughOptions().action(async (sandboxId, command, opts) => {
3362
- await ensureAuth();
3363
- const provider = await resolveProvider(opts.provider);
3364
- configureCompute(provider);
3365
- const sandbox = await compute4.sandbox.getById(sandboxId);
3366
- if (!sandbox) {
3367
- console.error(pc8.red(`Sandbox not found: ${sandboxId}`));
3368
- process.exit(1);
3369
- }
3370
- const cmd = buildExecCommand(command, opts);
3371
- const result = await sandbox.runCommand(cmd);
3372
- if (result.stdout) {
3373
- process.stdout.write(result.stdout);
3374
- }
3375
- if (result.stderr) {
3376
- process.stderr.write(result.stderr);
3377
- }
3378
- process.exit(result.exitCode);
3379
- });
3380
- program.command("connect").description("Connect to a sandbox (opens REPL)").aliases(["ssh", "shell"]).argument("[sandbox_id]", "sandbox ID (optional - will show picker if not provided)").option("-p, --provider <provider>", "provider to use").action(async (sandboxId, opts) => {
3381
- console.log();
3382
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
3383
- await ensureAuth();
3384
- const provider = await resolveProvider(opts.provider);
3385
- configureCompute(provider);
3386
- if (!sandboxId) {
3387
- const { loadState: loadState2 } = await Promise.resolve().then(() => (init_workspace_cli(), workspace_cli_exports));
3388
- const state = loadState2();
3389
- const workspaces = Object.values(state.workspaces);
3390
- if (workspaces.length === 0) {
3391
- p4.log.info("No sandboxes found.");
3392
- p4.log.info("Create one with: compute sandbox create");
3393
- p4.outro(pc8.green("Done!"));
3394
- return;
3395
- }
3396
- const choices = workspaces.map((ws) => {
3397
- const age = Math.round(
3398
- (Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24)
3399
- );
3400
- const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
3401
- const status = age > 7 ? pc8.red("stale") : age > 1 ? pc8.yellow("idle") : pc8.green("active");
3402
- return {
3403
- value: ws.sandboxId,
3404
- label: `${status} ${ws.sandboxId} (${ws.provider}, last active ${ageStr})`
3405
- };
3406
- });
3407
- const selected = await p4.select({
3408
- message: "Select sandbox to connect",
3409
- options: choices
3410
- });
3411
- if (p4.isCancel(selected)) {
3412
- p4.cancel("Cancelled");
3413
- process.exit(0);
3414
- }
3415
- sandboxId = selected;
3416
- }
3417
- const spinner4 = p4.spinner();
3418
- spinner4.start("Connecting...");
3419
- const sandbox = await compute4.sandbox.getById(sandboxId);
3420
- if (!sandbox) {
3421
- spinner4.stop(pc8.red(`Sandbox not found: ${sandboxId}`));
3422
- process.exit(1);
3423
- }
3424
- spinner4.stop("");
3425
- await startREPL(sandbox, provider);
3426
- });
3427
- var workspaceCmd = program.command("workspace").alias("ws").description("Manage workspaces (sandbox + git + worktree)");
3428
- workspaceCmd.command("create").description("Create a new workspace").argument("[repo]", "Repository in owner/repo format").option("-b, --branch <branch>", "Branch name", "main").option("-p, --provider <provider>", "provider to use").option("--no-attach", "Create workspace but do not attach").action(async (repo, options) => {
3429
- console.log();
3430
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
3431
- await ensureAuth();
3432
- if (!repo) {
3433
- p4.log.info("Usage: compute workspace create owner/repo --branch main");
3434
- p4.outro(pc8.green("Done!"));
3435
- return;
3436
- }
3437
- const provider = await resolveProvider(options.provider, { interactive: true });
3438
- configureCompute(provider);
3439
- const spinner4 = p4.spinner();
3440
- spinner4.start("Creating workspace...");
3441
- try {
3442
- const workspace = await workspaceCreate({
3443
- repo,
3444
- branch: options.branch,
3445
- provider
3446
- });
3447
- spinner4.stop(`Workspace ready: ${pc8.cyan(workspace.id)}`);
3448
- if (options.attach !== false) {
3449
- console.log();
3450
- await workspaceAttach(workspace.id);
3451
- } else {
3452
- p4.outro(pc8.green("Workspace created!"));
3453
- }
3454
- } catch (error) {
3455
- spinner4.stop(pc8.red("Failed to create workspace"));
3456
- p4.log.error(error.message);
3457
- process.exit(1);
3458
- }
3459
- });
3460
- workspaceCmd.command("list").alias("ls").description("List all workspaces").action(async () => {
3461
- console.log();
3462
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
3463
- await workspaceList();
3464
- });
3465
- workspaceCmd.command("destroy").alias("rm").description("Destroy a workspace").argument("[id]", "Workspace ID (repo:branch format, optional - will show picker if not provided)").action(async (id) => {
3466
- console.log();
3467
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
3468
- if (!id) {
3469
- const state = loadWorkspaceState();
3470
- const workspaceIds = Object.keys(state.workspaces);
3471
- if (workspaceIds.length === 0) {
3472
- p4.log.info("No workspaces to destroy.");
3473
- p4.outro(pc8.green("Done!"));
3474
- return;
3475
- }
3476
- const choices = workspaceIds.map((key) => {
3477
- const ws = state.workspaces[key];
3478
- const age = Math.round(
3479
- (Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24)
3480
- );
3481
- const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
3482
- return {
3483
- value: key,
3484
- label: `${pc8.cyan(key)} (${ws.provider}, last used ${ageStr})`
3485
- };
3486
- });
3487
- const selected = await p4.multiselect({
3488
- message: "Select workspaces to destroy (space to select, enter to confirm)",
3489
- options: choices
3490
- });
3491
- if (p4.isCancel(selected) || selected.length === 0) {
3492
- p4.cancel("Cancelled");
3493
- process.exit(0);
3494
- }
3495
- for (const workspaceId of selected) {
3496
- await workspaceDestroy(workspaceId);
3497
- }
3498
- } else {
3499
- await workspaceDestroy(id);
3500
- }
3501
- });
3502
- workspaceCmd.command("connect").alias("attach").description("Connect to an existing workspace (with git context)").argument("[id]", "Workspace ID (optional - will show picker if not provided)").action(async (id) => {
3503
- console.log();
3504
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
3505
- if (!id) {
3506
- const state = loadWorkspaceState();
3507
- const workspaceIds = Object.keys(state.workspaces);
3508
- if (workspaceIds.length === 0) {
3509
- p4.log.info("No workspaces found.");
3510
- p4.log.info("Create one with: compute create owner/repo");
3511
- p4.outro(pc8.green("Done!"));
3512
- return;
3513
- }
3514
- const choices = workspaceIds.map((key) => {
3515
- const ws = state.workspaces[key];
3516
- const age = Math.round(
3517
- (Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24)
3518
- );
3519
- const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
3520
- return {
3521
- value: key,
3522
- label: `${pc8.cyan(key)} (${ws.provider}, last used ${ageStr})`
3523
- };
3524
- });
3525
- const selected = await p4.select({
3526
- message: "Select workspace to connect",
3527
- options: choices
3528
- });
3529
- if (p4.isCancel(selected)) {
3530
- p4.cancel("Cancelled");
3531
- process.exit(0);
3532
- }
3533
- id = selected;
3534
- }
3535
- await workspaceAttach(id);
3536
- });
3537
- program.command("run").description("Create a sandbox, run a command, then optionally destroy it").argument("<command...>", "command to run").option("-p, --provider <provider>", "provider to use").option("-e, --env <key=value...>", "environment variables").option("-w, --workdir <directory>", "working directory").passThroughOptions().option("--rm", "destroy sandbox after command finishes").option("--timeout <duration>", "sandbox timeout (e.g. 5m, 1h)").option("--name <name>", "sandbox name").action(async (command, opts) => {
3538
- await ensureAuth();
3539
- const provider = await resolveProvider(opts.provider);
3540
- configureCompute(provider);
3541
- let sandbox;
3542
- try {
3543
- sandbox = await compute4.sandbox.create(
3544
- buildCreateOptions(opts)
3545
- );
3546
- } catch (error) {
3547
- console.error(pc8.red(`Failed to create sandbox: ${error.message}`));
3548
- process.exit(1);
3549
- }
3550
- try {
3551
- const cmd = buildExecCommand(command, opts);
3552
- const result = await sandbox.runCommand(cmd);
3553
- if (result.stdout) {
3554
- process.stdout.write(result.stdout);
3555
- }
3556
- if (result.stderr) {
3557
- process.stderr.write(result.stderr);
3558
- }
3559
- if (opts.rm) {
3560
- await sandbox.destroy();
3561
- }
3562
- process.exit(result.exitCode);
3563
- } catch (error) {
3564
- if (opts.rm) {
3565
- try {
3566
- await sandbox.destroy();
3567
- } catch {
3568
- }
3569
- }
3570
- console.error(pc8.red(error.message));
3571
- process.exit(1);
3572
- }
3573
- });
3574
2265
  program.command("providers").description("List all providers and their configuration status").action(async () => {
3575
2266
  console.log();
3576
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2267
+ p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
3577
2268
  const statuses = getProviderStatus();
3578
2269
  console.log();
3579
2270
  for (const status of statuses) {
3580
- const icon = status.ready ? pc8.green("\u25CF") : pc8.gray("\u25CB");
3581
- const name = status.ready ? pc8.white(status.name) : pc8.gray(status.name);
3582
- const detail = status.ready ? pc8.green("ready") : pc8.gray(`missing: ${status.missing.join(", ")}`);
2271
+ const icon = status.ready ? pc4.green("\u25CF") : pc4.gray("\u25CB");
2272
+ const name = status.ready ? pc4.white(status.name) : pc4.gray(status.name);
2273
+ const detail = status.ready ? pc4.green("ready") : pc4.gray(`missing: ${status.missing.join(", ")}`);
3583
2274
  console.log(` ${icon} ${name} ${detail}`);
3584
2275
  }
3585
2276
  console.log();
3586
2277
  });
3587
- program.command("login").description("Authenticate with ComputeSDK").action(async () => {
2278
+ program.command("login").description("Authenticate with ComputeSDK (OAuth device flow)").option("--base-url <url>", "platform URL to authenticate against").action(async (opts) => {
3588
2279
  console.log();
3589
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
3590
- await ensureAuth({ forceLogin: true });
3591
- p4.outro(pc8.green("Authenticated!"));
2280
+ p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
2281
+ await runLogin({ baseUrl: opts.baseUrl });
2282
+ p.outro(pc4.green("Authenticated!"));
3592
2283
  });
3593
- program.command("logout").description("Clear stored credentials").action(() => {
2284
+ program.command("logout").description("Clear stored credentials").action(async () => {
3594
2285
  console.log();
3595
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
3596
- clearStoredCredentials();
3597
- p4.log.success("Logged out. Stored credentials removed.");
3598
- p4.outro(pc8.green("Done!"));
2286
+ p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
2287
+ await runLogout();
2288
+ p.log.success("Logged out. Stored credentials removed.");
2289
+ p.outro(pc4.green("Done!"));
3599
2290
  });
3600
2291
  registerActionsCommands(program);
3601
2292
  registerMarketCommands(program);
3602
2293
  registerSandboxesCommands(program);
3603
2294
  program.command("bench").description("Benchmarks platform CLI (run, auth, org, benchmarks, runs, results, artifacts, export, ...)").allowUnknownOption().helpOption(false).action(() => {
3604
2295
  });
3605
- function buildCreateOptions(opts) {
3606
- const options = {};
3607
- if (opts.timeout) {
3608
- options.timeout = parseDuration(opts.timeout);
3609
- }
3610
- if (opts.name) {
3611
- options.name = opts.name;
3612
- }
3613
- return options;
3614
- }
3615
- function shellEscape2(arg) {
3616
- if (/^[a-zA-Z0-9._\-/:=@]+$/.test(arg)) return arg;
3617
- return `'${arg.replace(/'/g, "'\\''")}'`;
3618
- }
3619
- function buildExecCommand(command, opts) {
3620
- let cmd = command.map(shellEscape2).join(" ");
3621
- if (opts.workdir) {
3622
- cmd = `cd ${shellEscape2(opts.workdir)} && ${cmd}`;
3623
- }
3624
- if (opts.env && opts.env.length > 0) {
3625
- const envPrefix = opts.env.map((entry) => {
3626
- const eqIndex = entry.indexOf("=");
3627
- if (eqIndex === -1) {
3628
- throw new Error(
3629
- `Invalid environment entry "${entry}". Expected format KEY=VALUE.`
3630
- );
3631
- }
3632
- const key = entry.slice(0, eqIndex);
3633
- const val = entry.slice(eqIndex + 1);
3634
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(key)) {
3635
- throw new Error(
3636
- `Invalid environment variable name "${key}" in entry "${entry}".`
3637
- );
3638
- }
3639
- return `${key}=${shellEscape2(val)}`;
3640
- }).join(" ");
3641
- cmd = `${envPrefix} ${cmd}`;
3642
- }
3643
- return cmd;
3644
- }
3645
- function parseDuration(input) {
3646
- let total = 0;
3647
- const pattern = /(\d+)\s*(s|m|h)/gi;
3648
- let match;
3649
- while ((match = pattern.exec(input)) !== null) {
3650
- const value = parseInt(match[1], 10);
3651
- const unit = match[2].toLowerCase();
3652
- switch (unit) {
3653
- case "s":
3654
- total += value * 1e3;
3655
- break;
3656
- case "m":
3657
- total += value * 60 * 1e3;
3658
- break;
3659
- case "h":
3660
- total += value * 60 * 60 * 1e3;
3661
- break;
3662
- }
3663
- }
3664
- if (total === 0) {
3665
- const num = parseInt(input, 10);
3666
- if (!isNaN(num)) return num * 1e3;
3667
- throw new Error(`Invalid duration: ${input}. Use formats like 30s, 5m, 1h`);
3668
- }
3669
- return total;
3670
- }
3671
2296
  async function main() {
3672
2297
  if (process.argv[2] === "bench" || process.argv[2] === "benchmark") {
3673
2298
  const { run: benchRun } = await import("@benchsdk/runner");
@@ -3677,11 +2302,8 @@ async function main() {
3677
2302
  await program.parseAsync(process.argv);
3678
2303
  }
3679
2304
  main().catch((error) => {
3680
- console.error(pc8.red(`
2305
+ console.error(pc4.red(`
3681
2306
  Error: ${error.message}`));
3682
2307
  process.exit(1);
3683
2308
  });
3684
- export {
3685
- shellEscape2 as shellEscape
3686
- };
3687
2309
  //# sourceMappingURL=index.js.map