@computesdk/cli 1.0.16 → 2.1.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,26 +52,8 @@ 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
- const hasGatewayKey = !!process.env.COMPUTESDK_API_KEY;
654
- statuses.push({
655
- name: "computesdk",
656
- ready: hasGatewayKey,
657
- missing: hasGatewayKey ? [] : ["COMPUTESDK_API_KEY"]
658
- });
659
57
  for (const provider of PROVIDER_NAMES) {
660
58
  statuses.push({
661
59
  name: provider,
@@ -665,40 +63,13 @@ function getProviderStatus() {
665
63
  }
666
64
  return statuses;
667
65
  }
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
66
 
694
67
  // src/actions.ts
695
- init_esm_shims();
696
68
  import pc from "picocolors";
697
69
  import { mkdirSync, readFileSync, writeFileSync } from "fs";
698
70
  import { basename, join } from "path";
699
71
 
700
72
  // src/actions-client.ts
701
- init_esm_shims();
702
73
  var DEFAULT_BASE_URL = "https://platform.computesdk.com";
703
74
  var ActionsApiError = class extends Error {
704
75
  constructor(status, message, body) {
@@ -747,11 +118,12 @@ function errorDetails(body) {
747
118
  function toErrorEnvelope(error) {
748
119
  if (error instanceof ActionsApiError) {
749
120
  const details = errorDetails(error.body);
121
+ 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
122
  return {
751
123
  ok: false,
752
124
  error: {
753
125
  code: codeForStatus(error.status),
754
- message: error.message,
126
+ message: insufficientScope ? `${error.message} \u2014 run \`compute login\` to sign in again.` : error.message,
755
127
  httpStatus: error.status,
756
128
  retryable: error.status === 429 || error.status === 502 || error.status === 503 || error.status === 504,
757
129
  ...details !== void 0 && { details }
@@ -792,8 +164,8 @@ var ActionsClient = class {
792
164
  this.orgPromise ?? (this.orgPromise = this.get("/api/v1/actions/org"));
793
165
  return this.orgPromise;
794
166
  }
795
- async get(path5, params) {
796
- const url = new URL(`${this.auth.baseUrl}${path5}`);
167
+ async get(path, params) {
168
+ const url = new URL(`${this.auth.baseUrl}${path}`);
797
169
  for (const [key, value] of Object.entries(params ?? {})) {
798
170
  if (value === void 0) continue;
799
171
  for (const v of Array.isArray(value) ? value : [value]) url.searchParams.append(key, v);
@@ -801,32 +173,32 @@ var ActionsClient = class {
801
173
  const res = await this.fetchImpl(url, { headers: this.headers() });
802
174
  return this.parse(res);
803
175
  }
804
- async post(path5, body = {}) {
805
- const res = await this.fetchImpl(`${this.auth.baseUrl}${path5}`, {
176
+ async post(path, body = {}) {
177
+ const res = await this.fetchImpl(`${this.auth.baseUrl}${path}`, {
806
178
  method: "POST",
807
179
  headers: { ...this.headers(), "content-type": "application/json" },
808
180
  body: JSON.stringify(body)
809
181
  });
810
182
  return this.parse(res);
811
183
  }
812
- async put(path5, body = {}) {
813
- const res = await this.fetchImpl(`${this.auth.baseUrl}${path5}`, {
184
+ async put(path, body = {}) {
185
+ const res = await this.fetchImpl(`${this.auth.baseUrl}${path}`, {
814
186
  method: "PUT",
815
187
  headers: { ...this.headers(), "content-type": "application/json" },
816
188
  body: JSON.stringify(body)
817
189
  });
818
190
  return this.parse(res);
819
191
  }
820
- async patch(path5, body = {}) {
821
- const res = await this.fetchImpl(`${this.auth.baseUrl}${path5}`, {
192
+ async patch(path, body = {}) {
193
+ const res = await this.fetchImpl(`${this.auth.baseUrl}${path}`, {
822
194
  method: "PATCH",
823
195
  headers: { ...this.headers(), "content-type": "application/json" },
824
196
  body: JSON.stringify(body)
825
197
  });
826
198
  return this.parse(res);
827
199
  }
828
- async del(path5) {
829
- const res = await this.fetchImpl(`${this.auth.baseUrl}${path5}`, {
200
+ async del(path) {
201
+ const res = await this.fetchImpl(`${this.auth.baseUrl}${path}`, {
830
202
  method: "DELETE",
831
203
  headers: this.headers()
832
204
  });
@@ -836,8 +208,8 @@ var ActionsClient = class {
836
208
  * One `data:` JSON payload per SSE event. `next()` resolves to the payload,
837
209
  * or null when the stream ends. Call `close()` to abort.
838
210
  */
839
- async *sse(path5, params) {
840
- const url = new URL(`${this.auth.baseUrl}${path5}`);
211
+ async *sse(path, params) {
212
+ const url = new URL(`${this.auth.baseUrl}${path}`);
841
213
  for (const [key, value] of Object.entries(params ?? {})) {
842
214
  if (value === void 0) continue;
843
215
  for (const v of Array.isArray(value) ? value : [value]) url.searchParams.append(key, v);
@@ -856,8 +228,8 @@ var ActionsClient = class {
856
228
  * events are distinguished by name rather than payload shape (process
857
229
  * follow: stdout/stderr share a `{chunk}` payload).
858
230
  */
859
- async *sseNamed(path5, params) {
860
- const url = new URL(`${this.auth.baseUrl}${path5}`);
231
+ async *sseNamed(path, params) {
232
+ const url = new URL(`${this.auth.baseUrl}${path}`);
861
233
  for (const [key, value] of Object.entries(params ?? {})) {
862
234
  if (value === void 0) continue;
863
235
  for (const v of Array.isArray(value) ? value : [value]) url.searchParams.append(key, v);
@@ -872,8 +244,8 @@ var ActionsClient = class {
872
244
  yield* readSseNamedEvents(res.body);
873
245
  }
874
246
  /** Download bytes following redirects (artifact URLs 302 to storage). */
875
- async download(path5) {
876
- const res = await this.fetchImpl(`${this.auth.baseUrl}${path5}`, {
247
+ async download(path) {
248
+ const res = await this.fetchImpl(`${this.auth.baseUrl}${path}`, {
877
249
  headers: this.headers(),
878
250
  redirect: "follow"
879
251
  });
@@ -884,7 +256,12 @@ var ActionsClient = class {
884
256
  return new Uint8Array(await res.arrayBuffer());
885
257
  }
886
258
  headers() {
887
- return { authorization: `Bearer ${this.auth.apiKey}`, accept: "application/json" };
259
+ const headers = {
260
+ authorization: `Bearer ${this.auth.apiKey}`,
261
+ accept: "application/json"
262
+ };
263
+ if (this.auth.orgSlug) headers["X-Org-Slug"] = this.auth.orgSlug;
264
+ return headers;
888
265
  }
889
266
  async parse(res) {
890
267
  const body = await res.json().catch(() => void 0);
@@ -954,10 +331,11 @@ function encodeWatchCursor(id, jobId, step, offset) {
954
331
  const stepPart = step === null ? "-" : step === "runner" ? "runner" : String(step);
955
332
  return `${id}:${jobId}:${stepPart}:${offset}`;
956
333
  }
957
- var NO_CREDENTIALS_HINT = "Set COMPUTE_API_KEY, pass --api-key, or run `compute bench auth login`.";
334
+ var NO_CREDENTIALS_HINT = "Set COMPUTE_API_KEY, pass --api-key, or run `compute login`.";
958
335
  async function resolveActionsAuth(opts, resolveStored = resolveStoredPlatformAuth) {
959
336
  const baseUrl = (opts.baseUrl || process.env.COMPUTE_PLATFORM_URL || process.env.BENCHMARKS_PLATFORM_URL || // legacy name
960
337
  DEFAULT_BASE_URL).replace(/\/+$/, "");
338
+ let orgSlug = opts.org || process.env.COMPUTE_ORG || void 0;
961
339
  if (!opts.allowUntrustedHost && !isTrustedActionsHost(baseUrl)) {
962
340
  throw new ActionsCliError(
963
341
  "untrusted_host",
@@ -992,12 +370,12 @@ async function resolveActionsAuth(opts, resolveStored = resolveStoredPlatformAut
992
370
  if (!apiKey) {
993
371
  throw new ActionsCliError("no_credentials", `No API key. ${NO_CREDENTIALS_HINT}`);
994
372
  }
995
- return { apiKey, baseUrl };
373
+ return { apiKey, baseUrl, ...orgSlug && { orgSlug } };
996
374
  }
997
375
  async function resolveStoredPlatformAuth(opts) {
998
376
  const { resolveAuth } = await import("@benchsdk/cli");
999
377
  const auth = await resolveAuth({ baseUrl: opts.baseUrl });
1000
- return { apiKey: auth.apiKey, token: auth.token };
378
+ return { apiKey: auth.apiKey, token: auth.token, orgSlug: auth.orgSlug, orgId: auth.orgId };
1001
379
  }
1002
380
  function isLoopbackHost(host) {
1003
381
  return host === "localhost" || host === "127.0.0.1" || host === "[::1]" || host === "::1";
@@ -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.");
@@ -1505,7 +883,7 @@ function formatVaultRow(item) {
1505
883
  }
1506
884
  function registerActionsCommands(program2) {
1507
885
  const actions = program2.command("actions").alias("ci").description("Drive the benchmarks-platform Actions API").configureOutput({ outputError: usageErrorOutput });
1508
- const common = (cmd) => cmd.option("--api-key <key>", "API key (default: $COMPUTE_API_KEY)").option("--base-url <url>", "API base URL (default: https://platform.computesdk.com)").option("--allow-untrusted-host", "send an explicit --api-key/env key to a non-computesdk, non-localhost --base-url (stored login credentials are never sent)").option("--json", "print machine-readable JSON");
886
+ const common = (cmd) => cmd.option("--api-key <key>", "API key (default: $COMPUTE_API_KEY)").option("--base-url <url>", "API base URL (default: https://platform.computesdk.com)").option("--org <slug>", "organization slug for this command (or $COMPUTE_ORG)").option("--allow-untrusted-host", "send an explicit --api-key/env key to a non-computesdk, non-localhost --base-url (stored login credentials are never sent)").option("--json", "print machine-readable JSON");
1509
887
  common(
1510
888
  actions.command("dispatch").description("Dispatch a workflow_dispatch run (or any workflow with --manual)").argument("<repo>", "repository in owner/repo format").requiredOption("--workflow <path|name>", "workflow path or name").option("--ref <ref>", "git ref to run (default: the workflow's first watched ref)").option("--inputs <pairs...>", "workflow inputs as key=value (workflow_dispatch inputs only)").option("--manual", "run a workflow even if it does not declare workflow_dispatch (such runs take no --inputs)").option("--provider <id>", "place the run on one provider (e.g. namespace, vercel:sfo1) instead of the org provider order").option("--provider-region <region>", "region for --provider (same as --provider <id>:<region>)").option("--max-bid <usd>", "max price per vCPU for a market fill (e.g. 0.12); unfilled jobs fall through to the next provider").option("--max-bid-per <unit>", "time unit --max-bid is priced in: second, minute, or hour (default: second)")
1511
889
  ).action(async (repo, opts) => {
@@ -1528,8 +906,8 @@ function registerActionsCommands(program2) {
1528
906
  "/api/v1/actions/dispatch",
1529
907
  body
1530
908
  );
1531
- const org = await c.org();
1532
- const url = `${c.baseUrl}/${org.slug}/actions/runs/${result.runId}`;
909
+ const org2 = await c.org();
910
+ const url = `${c.baseUrl}/${org2.slug}/actions/runs/${result.runId}`;
1533
911
  output(opts, { ...result, url }, (r) => {
1534
912
  console.log(`${r.created ? "dispatched" : "already running"} ${pc.cyan(r.runId)}`);
1535
913
  console.log(`sha: ${r.headSha}`);
@@ -1587,8 +965,8 @@ function registerActionsCommands(program2) {
1587
965
  try {
1588
966
  const c = await client(opts);
1589
967
  const run = await c.get(`/api/v1/actions/runs/${runId}`);
1590
- const org = await c.org();
1591
- const url = `${c.baseUrl}/${org.slug}/actions/runs/${run.id}`;
968
+ const org2 = await c.org();
969
+ const url = `${c.baseUrl}/${org2.slug}/actions/runs/${run.id}`;
1592
970
  const summary = run.conclusion === "failed" ? await fetchRunSummary(c, runId) : null;
1593
971
  output(opts, { ...run, url, summary }, (r) => {
1594
972
  console.log(formatRunDetail(r, r.url));
@@ -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);
@@ -1923,8 +1301,8 @@ function registerActionsCommands(program2) {
1923
1301
  const result = await c.post(
1924
1302
  `/api/v1/actions/runs/${runId}/rerun`
1925
1303
  );
1926
- const org = await c.org();
1927
- const url = `${c.baseUrl}/${org.slug}/actions/runs/${result.runId}`;
1304
+ const org2 = await c.org();
1305
+ const url = `${c.baseUrl}/${org2.slug}/actions/runs/${result.runId}`;
1928
1306
  output(opts, { ...result, url }, (r) => {
1929
1307
  console.log(`${r.created ? "rerun dispatched" : "rerun already running"} ${pc.cyan(r.runId)}`);
1930
1308
  console.log(`url: ${r.url}`);
@@ -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) {
@@ -2232,7 +1609,7 @@ function formatSettlementRow(s) {
2232
1609
  }
2233
1610
  function registerMarketCommands(program2) {
2234
1611
  const market = program2.command("market").description("Sell compute capacity on the compute market").configureOutput({ outputError: usageErrorOutput });
2235
- const common = (cmd) => cmd.option("--api-key <key>", "API key (default: $COMPUTE_API_KEY)").option("--base-url <url>", "API base URL (default: https://platform.computesdk.com)").option("--allow-untrusted-host", "send an explicit --api-key/env key to a non-computesdk, non-localhost --base-url (stored login credentials are never sent)").option("--json", "print machine-readable JSON");
1612
+ const common = (cmd) => cmd.option("--api-key <key>", "API key (default: $COMPUTE_API_KEY)").option("--base-url <url>", "API base URL (default: https://platform.computesdk.com)").option("--org <slug>", "organization slug for this command (or $COMPUTE_ORG)").option("--allow-untrusted-host", "send an explicit --api-key/env key to a non-computesdk, non-localhost --base-url (stored login credentials are never sent)").option("--json", "print machine-readable JSON");
2236
1613
  common(
2237
1614
  market.command("listings").alias("list").description("Your listings \u2014 live, paused, expired, and withdrawn")
2238
1615
  ).action(async (opts) => {
@@ -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) {
@@ -2823,6 +2199,9 @@ function registerSandboxesCommands(program2) {
2823
2199
  fail(e, opts);
2824
2200
  }
2825
2201
  });
2202
+ for (const sub of cmd.commands) {
2203
+ sub.option("--org <slug>", "organization slug for this command (or $COMPUTE_ORG)");
2204
+ }
2826
2205
  }
2827
2206
  async function followProcess(c, sandboxId, jobId) {
2828
2207
  let exitCode = 0;
@@ -2849,825 +2228,155 @@ async function followProcess(c, sandboxId, jobId) {
2849
2228
  }
2850
2229
 
2851
2230
  // 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
- });
2231
+ var __filename2 = fileURLToPath(import.meta.url);
2232
+ var __dirname2 = dirname(__filename2);
2233
+ var packageJson = JSON.parse(
2234
+ readFileSync2(join2(__dirname2, "..", "package.json"), "utf-8")
2235
+ );
2236
+ var isDevBuild = import.meta.url.includes("worktrees") || import.meta.url.includes("/packages/cli/");
2237
+ var VERSION = isDevBuild ? `${packageJson.version}-dev` : packageJson.version;
2238
+ var program = new Command();
2239
+ program.name("compute").description("ComputeSDK CLI").version(VERSION).enablePositionalOptions().passThroughOptions();
2240
+ function envBaseUrl() {
2241
+ return process.env.COMPUTE_PLATFORM_URL ?? process.env.BENCHMARKS_PLATFORM_URL;
2924
2242
  }
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
- });
2243
+ async function benchAuth(opts = {}) {
2244
+ const { resolveAuth } = await import("@benchsdk/cli");
2245
+ return resolveAuth({ baseUrl: opts.baseUrl ?? envBaseUrl() });
2983
2246
  }
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...");
2247
+ async function printSessionLine(options) {
3000
2248
  try {
3001
- const apiKey = await runBrowserAuthFlow();
3002
- spinner4.stop("Authentication successful");
3003
- try {
3004
- storeCredentials(apiKey);
2249
+ const { getMe } = await import("@benchsdk/cli");
2250
+ const me = await getMe(await benchAuth({ baseUrl: options?.baseUrl }));
2251
+ const active = me.organizations.find((o) => o.id === me.activeOrganizationId);
2252
+ const who = me.user.email ?? me.user.name ?? me.user.id;
2253
+ if (active) {
3005
2254
  p.log.success(
3006
- `Credentials saved to ${pc5.dim("~/.computesdk/credentials.json")}`
2255
+ `Logged in as ${who} \u2014 active org: ${active.slug}. Change with \`compute org use <slug>\`.`
3007
2256
  );
3008
- } catch {
3009
- p.log.warn("Could not save credentials to disk. You may need to authenticate again next time.");
2257
+ } else {
2258
+ p.log.success(`Logged in as ${who} \u2014 no active org`);
3010
2259
  }
3011
- return apiKey;
3012
- } catch (error) {
3013
- spinner4.stop(pc5.red("Authentication failed"));
3014
- throw error;
2260
+ } catch {
2261
+ p.log.warn("Logged in, but could not fetch session info.");
3015
2262
  }
3016
2263
  }
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
2264
+ async function runLogin(options) {
2265
+ const { oauthLogin } = await import("@benchsdk/cli");
2266
+ await oauthLogin({
2267
+ baseUrl: options?.baseUrl ?? envBaseUrl()
3077
2268
  });
3078
- if (p2.isCancel(provider)) {
3079
- p2.cancel("Cancelled");
3080
- process.exit(0);
3081
- }
3082
- return provider;
3083
2269
  }
3084
- function configureCompute(provider) {
3085
- const config = buildProviderConfig(provider);
3086
- compute2.setConfig(config);
2270
+ async function runLogout() {
2271
+ const { clearCredentials } = await import("@benchsdk/cli");
2272
+ await clearCredentials();
3087
2273
  }
3088
-
3089
- // src/index.ts
3090
- init_workspace_cli();
3091
- var __filename2 = fileURLToPath2(import.meta.url);
3092
- var __dirname2 = dirname2(__filename2);
3093
- var packageJson = JSON.parse(
3094
- readFileSync5(join5(__dirname2, "..", "package.json"), "utf-8")
3095
- );
3096
- var isDevBuild = import.meta.url.includes("worktrees") || import.meta.url.includes("/packages/cli/");
3097
- var VERSION = isDevBuild ? `${packageJson.version}-dev` : packageJson.version;
3098
- var program = new Command();
3099
- program.name("compute").description("Cloud-native workspace management").version(VERSION).enablePositionalOptions().passThroughOptions();
3100
- program.option("--login", "force re-authentication").option("--logout", "clear stored credentials").action(async (opts) => {
2274
+ program.option("--login", "force re-authentication").option("--logout", "clear stored credentials").option("--org <slug>", "organization slug override for this invocation (or $COMPUTE_ORG)").action(async (opts) => {
3101
2275
  if (opts.logout) {
3102
2276
  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!"));
2277
+ p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
2278
+ await runLogout();
2279
+ p.log.success("Logged out. Stored credentials removed.");
2280
+ p.outro(pc4.green("Done!"));
3107
2281
  process.exit(0);
3108
2282
  }
3109
2283
  if (opts.login) {
3110
2284
  console.log();
3111
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
3112
- await ensureAuth({ forceLogin: true });
3113
- p4.outro(pc8.green("Authenticated!"));
2285
+ p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
2286
+ await runLogin();
2287
+ await printSessionLine();
2288
+ p.outro(pc4.green("Authenticated!"));
3114
2289
  process.exit(0);
3115
2290
  }
3116
2291
  program.help();
3117
2292
  });
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
- }
2293
+ program.hook("preAction", (thisCommand) => {
2294
+ const org2 = thisCommand.opts().org;
2295
+ if (org2) process.env.COMPUTE_ORG = org2;
3573
2296
  });
3574
2297
  program.command("providers").description("List all providers and their configuration status").action(async () => {
3575
2298
  console.log();
3576
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2299
+ p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
3577
2300
  const statuses = getProviderStatus();
3578
2301
  console.log();
3579
2302
  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(", ")}`);
2303
+ const icon = status.ready ? pc4.green("\u25CF") : pc4.gray("\u25CB");
2304
+ const name = status.ready ? pc4.white(status.name) : pc4.gray(status.name);
2305
+ const detail = status.ready ? pc4.green("ready") : pc4.gray(`missing: ${status.missing.join(", ")}`);
3583
2306
  console.log(` ${icon} ${name} ${detail}`);
3584
2307
  }
3585
2308
  console.log();
3586
2309
  });
3587
- program.command("login").description("Authenticate with ComputeSDK").action(async () => {
2310
+ program.command("login").description("Authenticate with ComputeSDK (OAuth device flow)").option("--base-url <url>", "platform URL to authenticate against").action(async (opts) => {
3588
2311
  console.log();
3589
- p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
3590
- await ensureAuth({ forceLogin: true });
3591
- p4.outro(pc8.green("Authenticated!"));
2312
+ p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
2313
+ await runLogin({ baseUrl: opts.baseUrl });
2314
+ await printSessionLine({ baseUrl: opts.baseUrl });
2315
+ p.outro(pc4.green("Authenticated!"));
3592
2316
  });
3593
- program.command("logout").description("Clear stored credentials").action(() => {
2317
+ program.command("logout").description("Clear stored credentials").action(async () => {
3594
2318
  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!"));
2319
+ p.intro(pc4.cyan(`@computesdk/cli v${VERSION}`));
2320
+ await runLogout();
2321
+ p.log.success("Logged out. Stored credentials removed.");
2322
+ p.outro(pc4.green("Done!"));
3599
2323
  });
2324
+ async function printWhoami() {
2325
+ const { getMe } = await import("@benchsdk/cli");
2326
+ const me = await getMe(await benchAuth());
2327
+ const active = me.organizations.find((o) => o.id === me.activeOrganizationId);
2328
+ console.log(me.user.email ?? me.user.name ?? me.user.id);
2329
+ console.log(
2330
+ `active org: ${active ? `${active.slug} (${active.id})` : "\u2014"}${me.organizations.length > 1 ? " \u2014 change with `compute org use <slug>`" : ""}`
2331
+ );
2332
+ }
2333
+ var org = program.command("org").description("Manage the active organization");
2334
+ org.command("list").description("List your organizations").option("--json", "print machine-readable JSON").action(async (opts) => {
2335
+ const { listOrganizations, getMe } = await import("@benchsdk/cli");
2336
+ const auth = await benchAuth();
2337
+ const [organizations, me] = await Promise.all([listOrganizations(auth), getMe(auth)]);
2338
+ if (opts.json) {
2339
+ console.log(JSON.stringify(organizations, null, 2));
2340
+ return;
2341
+ }
2342
+ for (const o of organizations) {
2343
+ const marker = o.id === me.activeOrganizationId ? pc4.green("*") : " ";
2344
+ console.log(`${marker} ${pc4.cyan(o.slug)} ${o.name ?? ""} ${pc4.dim(o.id)}`);
2345
+ }
2346
+ });
2347
+ org.command("use <slug>").description("Set the active organization (persisted to the stored login)").action(async (slug) => {
2348
+ const { loadCredentials, saveCredentials, createApiClient, setActiveOrganization } = await import("@benchsdk/cli");
2349
+ const credentials = await loadCredentials() ?? {};
2350
+ const { auth } = await createApiClient({ baseUrl: envBaseUrl() });
2351
+ const result = await setActiveOrganization(auth, slug);
2352
+ if (!result.organization) {
2353
+ throw new Error(`Could not set active organization to ${slug}`);
2354
+ }
2355
+ await saveCredentials({
2356
+ ...credentials,
2357
+ baseUrl: auth.baseUrl,
2358
+ // An API-key login must not be rewritten as an OAuth one — keep its
2359
+ // key and kind, and only stamp token fields when this auth actually
2360
+ // is a token session.
2361
+ ...auth.token ? {
2362
+ token: auth.token,
2363
+ refreshToken: auth.refreshToken,
2364
+ tokenExpiresAt: auth.tokenExpiresAt,
2365
+ refreshExpiresAt: auth.refreshExpiresAt,
2366
+ kind: "oauth"
2367
+ } : {},
2368
+ orgSlug: result.organization.slug,
2369
+ orgId: result.organization.id
2370
+ });
2371
+ console.log(`Active organization set to ${result.organization.slug} (${result.organization.id})`);
2372
+ });
2373
+ org.command("current").description("Show the current user and active organization").action(printWhoami);
2374
+ program.command("whoami").description("Show the current user and active organization").action(printWhoami);
3600
2375
  registerActionsCommands(program);
3601
2376
  registerMarketCommands(program);
3602
2377
  registerSandboxesCommands(program);
3603
2378
  program.command("bench").description("Benchmarks platform CLI (run, auth, org, benchmarks, runs, results, artifacts, export, ...)").allowUnknownOption().helpOption(false).action(() => {
3604
2379
  });
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
2380
  async function main() {
3672
2381
  if (process.argv[2] === "bench" || process.argv[2] === "benchmark") {
3673
2382
  const { run: benchRun } = await import("@benchsdk/runner");
@@ -3677,11 +2386,8 @@ async function main() {
3677
2386
  await program.parseAsync(process.argv);
3678
2387
  }
3679
2388
  main().catch((error) => {
3680
- console.error(pc8.red(`
2389
+ console.error(pc4.red(`
3681
2390
  Error: ${error.message}`));
3682
2391
  process.exit(1);
3683
2392
  });
3684
- export {
3685
- shellEscape2 as shellEscape
3686
- };
3687
2393
  //# sourceMappingURL=index.js.map