@computesdk/cli 1.0.12 → 1.0.14

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
@@ -23,7 +23,7 @@ import * as readline from "readline";
23
23
  import * as path2 from "path";
24
24
  import * as os from "os";
25
25
  import * as fs from "fs";
26
- import pc2 from "picocolors";
26
+ import pc4 from "picocolors";
27
27
  import { compute } from "computesdk";
28
28
  async function startREPL(sandbox, provider) {
29
29
  void provider;
@@ -34,8 +34,8 @@ async function startREPL(sandbox, provider) {
34
34
  await runSession(ctx);
35
35
  }
36
36
  function buildPrompt(sandboxId) {
37
- if (!sandboxId) return pc2.gray("[no sandbox] ") + pc2.bold("$ ");
38
- return pc2.cyan(`[${sandboxId}] `) + pc2.bold("$ ");
37
+ if (!sandboxId) return pc4.gray("[no sandbox] ") + pc4.bold("$ ");
38
+ return pc4.cyan(`[${sandboxId}] `) + pc4.bold("$ ");
39
39
  }
40
40
  function runSession(ctx) {
41
41
  const rl = readline.createInterface({
@@ -80,13 +80,13 @@ function runSession(ctx) {
80
80
  return;
81
81
  }
82
82
  } else if (ctx.sandboxes.size === 0) {
83
- console.log(pc2.yellow("No active sandbox. Use /new to create one."));
83
+ console.log(pc4.yellow("No active sandbox. Use /new to create one."));
84
84
  } else {
85
85
  const sandbox = ctx.sandboxes.get(ctx.activeSandboxId);
86
86
  await executeCommand(sandbox, trimmed);
87
87
  }
88
88
  } catch (error) {
89
- console.log(pc2.red(`\u2717 ${error.message}`));
89
+ console.log(pc4.red(`\u2717 ${error.message}`));
90
90
  }
91
91
  if (!resolved) {
92
92
  rl.prompt();
@@ -104,13 +104,13 @@ async function executeCommand(sandbox, command) {
104
104
  }
105
105
  }
106
106
  if (result.stderr) {
107
- process.stderr.write(pc2.red(result.stderr));
107
+ process.stderr.write(pc4.red(result.stderr));
108
108
  if (!result.stderr.endsWith("\n")) {
109
109
  console.log();
110
110
  }
111
111
  }
112
112
  if (result.exitCode !== 0) {
113
- console.log(pc2.gray(`exit code: ${result.exitCode}`));
113
+ console.log(pc4.gray(`exit code: ${result.exitCode}`));
114
114
  }
115
115
  }
116
116
  async function handleSpecialCommand(ctx, input, rl) {
@@ -124,67 +124,67 @@ async function handleSpecialCommand(ctx, input, rl) {
124
124
  case "info":
125
125
  case "i": {
126
126
  if (ctx.sandboxes.size === 0) {
127
- console.log(pc2.yellow("No active sandbox. Use /new to create one."));
127
+ console.log(pc4.yellow("No active sandbox. Use /new to create one."));
128
128
  return null;
129
129
  }
130
130
  const activeSandbox = ctx.sandboxes.get(ctx.activeSandboxId);
131
131
  console.log();
132
- console.log(pc2.bold(" Sandbox Info"));
133
- console.log(` ID: ${pc2.cyan(activeSandbox.sandboxId)}`);
132
+ console.log(pc4.bold(" Sandbox Info"));
133
+ console.log(` ID: ${pc4.cyan(activeSandbox.sandboxId)}`);
134
134
  console.log();
135
135
  return null;
136
136
  }
137
137
  case "url":
138
138
  case "u": {
139
139
  if (ctx.sandboxes.size === 0) {
140
- console.log(pc2.yellow("No active sandbox. Use /new to create one."));
140
+ console.log(pc4.yellow("No active sandbox. Use /new to create one."));
141
141
  return null;
142
142
  }
143
143
  const activeSandbox = ctx.sandboxes.get(ctx.activeSandboxId);
144
144
  if (args.length === 0) {
145
- console.log(pc2.gray("Usage: /url <port>"));
145
+ console.log(pc4.gray("Usage: /url <port>"));
146
146
  } else {
147
147
  const port = parseInt(args[0], 10);
148
148
  if (isNaN(port)) {
149
- console.log(pc2.red("Invalid port number"));
149
+ console.log(pc4.red("Invalid port number"));
150
150
  } else {
151
151
  const url = await activeSandbox.getUrl({ port });
152
- console.log(pc2.cyan(url));
152
+ console.log(pc4.cyan(url));
153
153
  }
154
154
  }
155
155
  return null;
156
156
  }
157
157
  case "new": {
158
- console.log(pc2.gray(" Creating new sandbox..."));
158
+ console.log(pc4.gray(" Creating new sandbox..."));
159
159
  const newSandbox = await compute.sandbox.create();
160
160
  ctx.sandboxes.set(newSandbox.sandboxId, newSandbox);
161
161
  ctx.activeSandboxId = newSandbox.sandboxId;
162
162
  rl.setPrompt(buildPrompt(ctx.activeSandboxId));
163
- console.log(` Sandbox ready: ${pc2.cyan(newSandbox.sandboxId)}`);
164
- console.log(` ${pc2.gray(`Active sandboxes: ${ctx.sandboxes.size}`)}`);
163
+ console.log(` Sandbox ready: ${pc4.cyan(newSandbox.sandboxId)}`);
164
+ console.log(` ${pc4.gray(`Active sandboxes: ${ctx.sandboxes.size}`)}`);
165
165
  return null;
166
166
  }
167
167
  case "destroy": {
168
168
  if (args.length === 0) {
169
- console.log(pc2.gray("Usage: /destroy <sandbox-id> or /destroy --all"));
170
- console.log(pc2.gray("Use /list to see all sandboxes"));
169
+ console.log(pc4.gray("Usage: /destroy <sandbox-id> or /destroy --all"));
170
+ console.log(pc4.gray("Use /list to see all sandboxes"));
171
171
  return null;
172
172
  }
173
173
  if (args[0] === "--all") {
174
174
  if (ctx.sandboxes.size === 0) {
175
- console.log(pc2.yellow(" No sandboxes to destroy."));
175
+ console.log(pc4.yellow(" No sandboxes to destroy."));
176
176
  return null;
177
177
  }
178
178
  const count = ctx.sandboxes.size;
179
- console.log(pc2.gray(` Destroying ${count} sandbox${count > 1 ? "es" : ""}...`));
179
+ console.log(pc4.gray(` Destroying ${count} sandbox${count > 1 ? "es" : ""}...`));
180
180
  await Promise.all(
181
181
  Array.from(ctx.sandboxes.values()).map((s) => s.destroy())
182
182
  );
183
183
  ctx.sandboxes.clear();
184
184
  ctx.activeSandboxId = "";
185
185
  rl.setPrompt(buildPrompt(ctx.activeSandboxId));
186
- console.log(` Destroyed ${pc2.cyan(String(count))} sandbox${count > 1 ? "es" : ""}`);
187
- console.log(pc2.yellow(" No sandboxes remaining. Use /new to create one or /exit to quit."));
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
188
  return null;
189
189
  }
190
190
  const targetId = args[0];
@@ -194,13 +194,13 @@ async function handleSpecialCommand(ctx, input, rl) {
194
194
  if (matches.length === 1) {
195
195
  return destroySandbox(ctx, matches[0], rl);
196
196
  } else if (matches.length > 1) {
197
- console.log(pc2.yellow(" Ambiguous ID. Matches:"));
197
+ console.log(pc4.yellow(" Ambiguous ID. Matches:"));
198
198
  for (const id of matches) {
199
- console.log(` ${pc2.cyan(id)}`);
199
+ console.log(` ${pc4.cyan(id)}`);
200
200
  }
201
201
  } else {
202
- console.log(pc2.red(` Sandbox not found: ${targetId}`));
203
- console.log(pc2.gray(" Use /list to see all sandboxes"));
202
+ console.log(pc4.red(` Sandbox not found: ${targetId}`));
203
+ console.log(pc4.gray(" Use /list to see all sandboxes"));
204
204
  }
205
205
  return null;
206
206
  }
@@ -209,10 +209,10 @@ async function handleSpecialCommand(ctx, input, rl) {
209
209
  case "list":
210
210
  case "ls": {
211
211
  console.log();
212
- console.log(pc2.bold(" Sandboxes"));
212
+ console.log(pc4.bold(" Sandboxes"));
213
213
  for (const id of ctx.sandboxes.keys()) {
214
- const marker = id === ctx.activeSandboxId ? pc2.green(" \u25CF") : " ";
215
- console.log(` ${marker} ${pc2.cyan(id)}`);
214
+ const marker = id === ctx.activeSandboxId ? pc4.green(" \u25CF") : " ";
215
+ console.log(` ${marker} ${pc4.cyan(id)}`);
216
216
  }
217
217
  console.log();
218
218
  return null;
@@ -220,30 +220,30 @@ async function handleSpecialCommand(ctx, input, rl) {
220
220
  case "switch":
221
221
  case "sw": {
222
222
  if (args.length === 0) {
223
- console.log(pc2.gray("Usage: /switch <sandbox-id>"));
224
- console.log(pc2.gray("Use /list to see all sandboxes"));
223
+ console.log(pc4.gray("Usage: /switch <sandbox-id>"));
224
+ console.log(pc4.gray("Use /list to see all sandboxes"));
225
225
  return null;
226
226
  }
227
227
  const switchId = args[0];
228
228
  if (ctx.sandboxes.has(switchId)) {
229
229
  ctx.activeSandboxId = switchId;
230
230
  rl.setPrompt(buildPrompt(ctx.activeSandboxId));
231
- console.log(` Switched to ${pc2.cyan(switchId)}`);
231
+ console.log(` Switched to ${pc4.cyan(switchId)}`);
232
232
  return null;
233
233
  }
234
234
  const matches = Array.from(ctx.sandboxes.keys()).filter((id) => id.startsWith(switchId));
235
235
  if (matches.length === 1) {
236
236
  ctx.activeSandboxId = matches[0];
237
237
  rl.setPrompt(buildPrompt(ctx.activeSandboxId));
238
- console.log(` Switched to ${pc2.cyan(matches[0])}`);
238
+ console.log(` Switched to ${pc4.cyan(matches[0])}`);
239
239
  } else if (matches.length > 1) {
240
- console.log(pc2.yellow(" Ambiguous ID. Matches:"));
240
+ console.log(pc4.yellow(" Ambiguous ID. Matches:"));
241
241
  for (const id of matches) {
242
- console.log(` ${pc2.cyan(id)}`);
242
+ console.log(` ${pc4.cyan(id)}`);
243
243
  }
244
244
  } else {
245
- console.log(pc2.red(` Sandbox not found: ${switchId}`));
246
- console.log(pc2.gray(" Use /list to see all sandboxes"));
245
+ console.log(pc4.red(` Sandbox not found: ${switchId}`));
246
+ console.log(pc4.gray(" Use /list to see all sandboxes"));
247
247
  }
248
248
  return null;
249
249
  }
@@ -252,51 +252,51 @@ async function handleSpecialCommand(ctx, input, rl) {
252
252
  case "q":
253
253
  return "exit";
254
254
  default:
255
- console.log(pc2.yellow(`Unknown command: /${cmd}`));
256
- console.log(pc2.gray("Type /help for available commands"));
255
+ console.log(pc4.yellow(`Unknown command: /${cmd}`));
256
+ console.log(pc4.gray("Type /help for available commands"));
257
257
  return null;
258
258
  }
259
259
  }
260
260
  async function destroySandbox(ctx, sandboxId, rl) {
261
261
  const target = ctx.sandboxes.get(sandboxId);
262
- console.log(pc2.gray(` Destroying ${sandboxId}...`));
262
+ console.log(pc4.gray(` Destroying ${sandboxId}...`));
263
263
  await target.destroy();
264
264
  ctx.sandboxes.delete(sandboxId);
265
- console.log(` Destroyed ${pc2.cyan(sandboxId)}`);
265
+ console.log(` Destroyed ${pc4.cyan(sandboxId)}`);
266
266
  if (ctx.activeSandboxId === sandboxId && ctx.sandboxes.size > 0) {
267
267
  ctx.activeSandboxId = ctx.sandboxes.keys().next().value;
268
268
  rl.setPrompt(buildPrompt(ctx.activeSandboxId));
269
- console.log(` Switched to ${pc2.cyan(ctx.activeSandboxId)}`);
269
+ console.log(` Switched to ${pc4.cyan(ctx.activeSandboxId)}`);
270
270
  } else if (ctx.sandboxes.size === 0) {
271
271
  ctx.activeSandboxId = "";
272
272
  rl.setPrompt(buildPrompt(ctx.activeSandboxId));
273
- console.log(pc2.yellow(" No sandboxes remaining. Use /new to create one or /exit to quit."));
273
+ console.log(pc4.yellow(" No sandboxes remaining. Use /new to create one or /exit to quit."));
274
274
  }
275
275
  return null;
276
276
  }
277
277
  function showHelp() {
278
278
  console.log();
279
- console.log(pc2.bold(" Shell Mode"));
280
- console.log(pc2.gray(" Just type commands - they run in the active sandbox"));
279
+ console.log(pc4.bold(" Shell Mode"));
280
+ console.log(pc4.gray(" Just type commands - they run in the active sandbox"));
281
281
  console.log();
282
- console.log(pc2.white(" ls -la"));
283
- console.log(pc2.white(" cat /etc/os-release"));
284
- console.log(pc2.white(" npm install lodash"));
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
285
  console.log();
286
- console.log(pc2.bold(" Sandbox Commands") + pc2.gray(" (prefix with /)"));
286
+ console.log(pc4.bold(" Sandbox Commands") + pc4.gray(" (prefix with /)"));
287
287
  console.log();
288
- console.log(pc2.cyan(" /new") + pc2.gray(" Create a new sandbox and switch to it"));
289
- console.log(pc2.cyan(" /list") + pc2.gray(" List all sandboxes"));
290
- console.log(pc2.cyan(" /switch <id>") + pc2.gray(" Switch active sandbox"));
291
- console.log(pc2.cyan(" /destroy <id>") + pc2.gray(" Destroy a sandbox by ID"));
292
- console.log(pc2.cyan(" /destroy --all") + pc2.gray(" Destroy all sandboxes"));
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
293
  console.log();
294
- console.log(pc2.bold(" Other Commands"));
294
+ console.log(pc4.bold(" Other Commands"));
295
295
  console.log();
296
- console.log(pc2.cyan(" /info") + pc2.gray(" Show active sandbox info"));
297
- console.log(pc2.cyan(" /url <port>") + pc2.gray(" Get public URL for port"));
298
- console.log(pc2.cyan(" /help") + pc2.gray(" Show this help"));
299
- console.log(pc2.cyan(" /exit") + pc2.gray(" Exit the REPL"));
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
300
  console.log();
301
301
  }
302
302
  function loadHistory(rl) {
@@ -343,7 +343,7 @@ import * as fs3 from "fs";
343
343
  import * as path4 from "path";
344
344
  import * as os3 from "os";
345
345
  import * as p3 from "@clack/prompts";
346
- import pc5 from "picocolors";
346
+ import pc7 from "picocolors";
347
347
  function shellEscape(arg) {
348
348
  if (/^[a-zA-Z0-9._\-/:=@]+$/.test(arg)) return arg;
349
349
  return `'${arg.replace(/'/g, "'\\''")}'`;
@@ -382,7 +382,7 @@ async function workspaceCreate(options) {
382
382
  }
383
383
  existing.lastUsed = (/* @__PURE__ */ new Date()).toISOString();
384
384
  saveState(state);
385
- p3.log.info(`Attaching to existing workspace: ${pc5.cyan(id)}`);
385
+ p3.log.info(`Attaching to existing workspace: ${pc7.cyan(id)}`);
386
386
  return existing;
387
387
  } catch {
388
388
  delete state.workspaces[id];
@@ -394,9 +394,9 @@ async function workspaceCreate(options) {
394
394
  let sandbox;
395
395
  try {
396
396
  sandbox = await compute3.sandbox.create();
397
- spinner4.stop(`Sandbox ready: ${pc5.cyan(sandbox.sandboxId)}`);
397
+ spinner4.stop(`Sandbox ready: ${pc7.cyan(sandbox.sandboxId)}`);
398
398
  } catch (error) {
399
- spinner4.stop(pc5.red("Failed to create sandbox"));
399
+ spinner4.stop(pc7.red("Failed to create sandbox"));
400
400
  throw error;
401
401
  }
402
402
  spinner4.start("Bootstrapping workspace (installing gh CLI, cloning repo)...");
@@ -436,12 +436,12 @@ async function workspaceCreate(options) {
436
436
  };
437
437
  state.workspaces[id] = workspace;
438
438
  saveState(state);
439
- p3.log.success(`Workspace ready: ${pc5.cyan(id)}`);
440
- p3.log.info(`Sandbox: ${pc5.dim(sandbox.sandboxId)}`);
441
- p3.log.info(`Worktree: ${pc5.dim(worktreePath)}`);
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
442
  return workspace;
443
443
  } catch (error) {
444
- spinner4.stop(pc5.red("Bootstrap failed"));
444
+ spinner4.stop(pc7.red("Bootstrap failed"));
445
445
  try {
446
446
  await sandbox.destroy();
447
447
  } catch {
@@ -466,9 +466,9 @@ async function workspaceAttach(id) {
466
466
  }
467
467
  workspace.lastUsed = (/* @__PURE__ */ new Date()).toISOString();
468
468
  saveState(state);
469
- p3.log.info(`Attaching to workspace: ${pc5.cyan(id)}`);
470
- p3.log.info(`Worktree: ${pc5.dim(workspace.worktreePath)}`);
471
- console.log(pc5.gray(`Tip: commands run at sandbox root; use { cwd: "${workspace.worktreePath}" } on runCommand to scope them.`));
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
472
  try {
473
473
  await startREPL(sandbox, workspace.provider);
474
474
  } catch (error) {
@@ -485,19 +485,19 @@ async function sandboxList() {
485
485
  return;
486
486
  }
487
487
  console.log();
488
- console.log(pc5.bold("Sandboxes"));
488
+ console.log(pc7.bold("Sandboxes"));
489
489
  console.log();
490
490
  for (const ws of workspaces) {
491
491
  const age = Math.round((Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24));
492
492
  const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
493
- const status = age > 7 ? pc5.red("stale") : age > 1 ? pc5.yellow("idle") : pc5.green("active");
494
- console.log(` ${status} ${pc5.cyan(ws.sandboxId)}`);
495
- console.log(` Workspace: ${pc5.dim(ws.id)}`);
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
496
  console.log(` Provider: ${ws.provider}`);
497
- console.log(` Last active: ${pc5.gray(ageStr)}`);
497
+ console.log(` Last active: ${pc7.gray(ageStr)}`);
498
498
  console.log();
499
499
  }
500
- console.log(pc5.gray('Tip: Use "compute workspaces" to see full workspace details'));
500
+ console.log(pc7.gray('Tip: Use "compute workspaces" to see full workspace details'));
501
501
  }
502
502
  async function workspaceList() {
503
503
  const state = loadState();
@@ -508,15 +508,15 @@ async function workspaceList() {
508
508
  return;
509
509
  }
510
510
  console.log();
511
- console.log(pc5.bold("Workspaces"));
511
+ console.log(pc7.bold("Workspaces"));
512
512
  console.log();
513
513
  for (const ws of workspaces) {
514
514
  const age = Math.round((Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24));
515
515
  const ageStr = age === 0 ? "today" : `${age}d ago`;
516
- console.log(` ${pc5.cyan(ws.id)}`);
517
- console.log(` Sandbox: ${pc5.dim(ws.sandboxId)} (${ws.provider})`);
518
- console.log(` Path: ${pc5.dim(ws.worktreePath)}`);
519
- console.log(` Last used: ${pc5.gray(ageStr)}`);
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
520
  console.log();
521
521
  }
522
522
  }
@@ -542,7 +542,7 @@ async function workspaceDestroy(id) {
542
542
  p3.log.error(`Workspace not found: ${id}`);
543
543
  return;
544
544
  }
545
- p3.log.info(`Destroying workspace: ${pc5.cyan(id)}`);
545
+ p3.log.info(`Destroying workspace: ${pc7.cyan(id)}`);
546
546
  try {
547
547
  await compute3.sandbox.destroy(workspace.sandboxId);
548
548
  delete state.workspaces[id];
@@ -586,7 +586,7 @@ init_esm_shims();
586
586
  import "dotenv/config";
587
587
  import { Command } from "commander";
588
588
  import * as p4 from "@clack/prompts";
589
- import pc6 from "picocolors";
589
+ import pc8 from "picocolors";
590
590
  import { readFileSync as readFileSync5 } from "fs";
591
591
  import { fileURLToPath as fileURLToPath2 } from "url";
592
592
  import { dirname as dirname2, join as join5 } from "path";
@@ -842,13 +842,35 @@ var ActionsClient = class {
842
842
  if (value === void 0) continue;
843
843
  for (const v of Array.isArray(value) ? value : [value]) url.searchParams.append(key, v);
844
844
  }
845
- const res = await this.fetchImpl(url, { headers: this.headers() });
845
+ const res = await this.fetchImpl(url, {
846
+ headers: { ...this.headers(), accept: "text/event-stream" }
847
+ });
846
848
  if (!res.ok) {
847
849
  await this.parse(res);
848
850
  }
849
851
  if (!res.body) throw new Error("SSE response has no body");
850
852
  yield* readSseEvents(res.body);
851
853
  }
854
+ /**
855
+ * `sse()` with the frame's `event:` name preserved — for streams whose
856
+ * events are distinguished by name rather than payload shape (process
857
+ * follow: stdout/stderr share a `{chunk}` payload).
858
+ */
859
+ async *sseNamed(path5, params) {
860
+ const url = new URL(`${this.auth.baseUrl}${path5}`);
861
+ for (const [key, value] of Object.entries(params ?? {})) {
862
+ if (value === void 0) continue;
863
+ for (const v of Array.isArray(value) ? value : [value]) url.searchParams.append(key, v);
864
+ }
865
+ const res = await this.fetchImpl(url, {
866
+ headers: { ...this.headers(), accept: "text/event-stream" }
867
+ });
868
+ if (!res.ok) {
869
+ await this.parse(res);
870
+ }
871
+ if (!res.body) throw new Error("SSE response has no body");
872
+ yield* readSseNamedEvents(res.body);
873
+ }
852
874
  /** Download bytes following redirects (artifact URLs 302 to storage). */
853
875
  async download(path5) {
854
876
  const res = await this.fetchImpl(`${this.auth.baseUrl}${path5}`, {
@@ -862,7 +884,7 @@ var ActionsClient = class {
862
884
  return new Uint8Array(await res.arrayBuffer());
863
885
  }
864
886
  headers() {
865
- return { authorization: `Bearer ${this.auth.apiKey}` };
887
+ return { authorization: `Bearer ${this.auth.apiKey}`, accept: "application/json" };
866
888
  }
867
889
  async parse(res) {
868
890
  const body = await res.json().catch(() => void 0);
@@ -900,6 +922,34 @@ async function* readSseEvents(body) {
900
922
  reader.releaseLock();
901
923
  }
902
924
  }
925
+ async function* readSseNamedEvents(body) {
926
+ const reader = body.getReader();
927
+ const decoder = new TextDecoder();
928
+ let buffer = "";
929
+ try {
930
+ for (; ; ) {
931
+ const { done, value } = await reader.read();
932
+ if (done) break;
933
+ buffer += decoder.decode(value, { stream: true });
934
+ let idx;
935
+ while ((idx = buffer.indexOf("\n\n")) !== -1) {
936
+ const frame = buffer.slice(0, idx);
937
+ buffer = buffer.slice(idx + 2);
938
+ let event = "message";
939
+ const dataLines = [];
940
+ for (const line of frame.split("\n")) {
941
+ if (line.startsWith("event: ")) event = line.slice(7);
942
+ else if (line.startsWith("data: ")) dataLines.push(line.slice(6));
943
+ }
944
+ if (dataLines.length > 0) {
945
+ yield { event, data: JSON.parse(dataLines.join("\n")) };
946
+ }
947
+ }
948
+ }
949
+ } finally {
950
+ reader.releaseLock();
951
+ }
952
+ }
903
953
  function encodeWatchCursor(id, jobId, step, offset) {
904
954
  const stepPart = step === null ? "-" : step === "runner" ? "runner" : String(step);
905
955
  return `${id}:${jobId}:${stepPart}:${offset}`;
@@ -981,6 +1031,22 @@ function sanitizePathPart(name) {
981
1031
  async function client(opts) {
982
1032
  return new ActionsClient(await resolveActionsAuth(opts));
983
1033
  }
1034
+ function assertSecretValueHost(auth, what) {
1035
+ if (!isTrustedActionsHost(auth.baseUrl)) {
1036
+ throw new ActionsCliError(
1037
+ "untrusted_host",
1038
+ `Refusing to send or read ${what} at ${auth.baseUrl} \u2014 only computesdk.com and localhost hosts are allowed, even with --allow-untrusted-host.`
1039
+ );
1040
+ }
1041
+ }
1042
+ async function secretValueClient(opts, what) {
1043
+ const auth = await resolveActionsAuth(opts);
1044
+ assertSecretValueHost(auth, what);
1045
+ return new ActionsClient(auth);
1046
+ }
1047
+ async function vaultValueClient(opts) {
1048
+ return secretValueClient(opts, "vault values");
1049
+ }
984
1050
  function output(opts, data, render) {
985
1051
  if (opts.json) {
986
1052
  process.stdout.write(JSON.stringify(data, null, 2) + "\n");
@@ -1373,6 +1439,7 @@ function dispatchBody(workflow, opts) {
1373
1439
  if (opts.providerRegion !== void 0 && opts.provider === void 0) {
1374
1440
  throw new ActionsCliError("invalid_argument", "--provider-region requires --provider.");
1375
1441
  }
1442
+ const maxPrice = maxPriceFields(opts.maxBid, opts.maxBidPer);
1376
1443
  return {
1377
1444
  workflowId: workflow.id,
1378
1445
  ref,
@@ -1381,14 +1448,66 @@ function dispatchBody(workflow, opts) {
1381
1448
  ...opts.provider !== void 0 && {
1382
1449
  provider: opts.provider,
1383
1450
  ...opts.providerRegion !== void 0 && { providerRegion: opts.providerRegion }
1384
- }
1451
+ },
1452
+ ...maxPrice !== void 0 && maxPrice
1453
+ };
1454
+ }
1455
+ function maxPriceFields(maxBid, maxBidPer) {
1456
+ if (maxBidPer !== void 0 && maxBid === void 0) {
1457
+ throw new ActionsCliError("invalid_argument", "--max-bid-per requires --max-bid.");
1458
+ }
1459
+ if (maxBid === void 0) return void 0;
1460
+ const usd = Number(maxBid);
1461
+ if (!/^\d+(\.\d+)?([eE][+-]?\d+)?$/.test(maxBid) || !Number.isFinite(usd) || usd <= 0) {
1462
+ throw new ActionsCliError("invalid_argument", `Invalid --max-bid "${maxBid}". Expected a positive dollar amount (e.g. 0.12).`);
1463
+ }
1464
+ const per = maxBidPer ?? "second";
1465
+ if (per !== "second" && per !== "minute" && per !== "hour") {
1466
+ throw new ActionsCliError("invalid_argument", `--max-bid-per must be second, minute, or hour, got "${maxBidPer}".`);
1467
+ }
1468
+ return { maxPriceUsd: usd, maxPricePer: per };
1469
+ }
1470
+ function parseVaultKind(kind) {
1471
+ if (kind === void 0 || kind === "secret" || kind === "variable") return kind ?? "secret";
1472
+ throw new ActionsCliError("invalid_argument", `--kind must be secret or variable, got "${kind}".`);
1473
+ }
1474
+ function vaultPath(path5, params) {
1475
+ const search = new URLSearchParams();
1476
+ for (const [key, value] of Object.entries(params)) {
1477
+ if (value !== void 0) search.set(key, value);
1478
+ }
1479
+ const query = search.toString();
1480
+ return query ? `${path5}?${query}` : path5;
1481
+ }
1482
+ function vaultSetBody(name, value, opts) {
1483
+ if (value === "") throw new ActionsCliError("invalid_argument", "The value is empty.");
1484
+ if (opts.revealable && opts.kind === "variable") {
1485
+ throw new ActionsCliError("invalid_argument", "--revealable applies to secrets; variables are always readable.");
1486
+ }
1487
+ return {
1488
+ name,
1489
+ kind: opts.kind,
1490
+ value,
1491
+ ...opts.revealable && { revealable: true },
1492
+ ...opts.description !== void 0 && { description: opts.description },
1493
+ ...opts.labels !== void 0 && opts.labels.length > 0 && { labels: opts.labels }
1385
1494
  };
1386
1495
  }
1496
+ function formatVaultRow(item) {
1497
+ const flags = [
1498
+ item.kind,
1499
+ ...item.revealable && item.kind === "secret" ? ["revealable"] : [],
1500
+ `v${item.version}`,
1501
+ ...item.source === "organization" ? ["inherited"] : [],
1502
+ ...item.overridesOrganization ? ["overrides org"] : []
1503
+ ];
1504
+ return `${pc.cyan(safeTerm(item.name))} ${pc.dim(flags.join(", "))} ${pc.dim(`updated ${safeTerm(item.updatedAt)}`)}`;
1505
+ }
1387
1506
  function registerActionsCommands(program2) {
1388
1507
  const actions = program2.command("actions").alias("ci").description("Drive the benchmarks-platform Actions API").configureOutput({ outputError: usageErrorOutput });
1389
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");
1390
1509
  common(
1391
- 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>)")
1510
+ 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)")
1392
1511
  ).action(async (repo, opts) => {
1393
1512
  try {
1394
1513
  const c = await client(opts);
@@ -1714,6 +1833,74 @@ function registerActionsCommands(program2) {
1714
1833
  fail(e, opts);
1715
1834
  }
1716
1835
  });
1836
+ const vault = actions.command("vault").description("The org vault: secrets and variables (owner/admin key)");
1837
+ const vaultScope = (cmd) => common(cmd).option("--repo <owner/repo>", "scope to a repo's own items instead of the org's").option("--kind <kind>", "secret or variable", "secret");
1838
+ vaultScope(
1839
+ vault.command("ls").description("List names and metadata (never values)")
1840
+ ).action(async (opts) => {
1841
+ try {
1842
+ const path5 = vaultPath("/api/v1/vault", { repo: opts.repo, kind: parseVaultKind(opts.kind) });
1843
+ const data = await (await client(opts)).get(path5);
1844
+ output(opts, data, (d) => {
1845
+ for (const item of d.items) console.log(formatVaultRow(item));
1846
+ });
1847
+ } catch (e) {
1848
+ fail(e, opts);
1849
+ }
1850
+ });
1851
+ vaultScope(
1852
+ vault.command("set").description("Store a value, read from stdin (or --from-file) and sent exactly as read").argument("<name>", "item name, e.g. NPM_TOKEN").option("--from-file <path>", "read the value from a file instead of stdin").option("--revealable", "let the value be read back with `vault get` (secrets; fixed at creation)").option("--description <text>", "what the item is for").option("--labels <labels...>", "labels (fixed at creation)")
1853
+ ).action(async (name, opts) => {
1854
+ try {
1855
+ if (opts.fromFile === void 0 && process.stdin.isTTY) {
1856
+ throw new ActionsCliError(
1857
+ "invalid_argument",
1858
+ 'Pipe the value on stdin (e.g. printf %s "$VALUE" | compute actions vault set NAME) or pass --from-file.'
1859
+ );
1860
+ }
1861
+ const value = readFileSync(opts.fromFile ?? 0, "utf8");
1862
+ const kind = parseVaultKind(opts.kind);
1863
+ const body = vaultSetBody(name, value, { ...opts, kind });
1864
+ const result = await (await vaultValueClient(opts)).put(
1865
+ vaultPath("/api/v1/vault", { repo: opts.repo }),
1866
+ body
1867
+ );
1868
+ output(opts, result, (r) => {
1869
+ const where = opts.repo ? safeTerm(opts.repo) : "org";
1870
+ console.log(`saved ${pc.cyan(safeTerm(r.item.name))} ${pc.dim(`(${r.item.kind}, v${r.item.version}, ${where})`)}`);
1871
+ if (/\r?\n$/.test(value)) {
1872
+ console.error(pc.yellow("note: the stored value ends with a newline; use printf %s rather than echo to avoid one."));
1873
+ }
1874
+ });
1875
+ } catch (e) {
1876
+ fail(e, opts);
1877
+ }
1878
+ });
1879
+ vaultScope(
1880
+ vault.command("get").description("Print a value: any variable, or a secret created --revealable").argument("<name>", "item name")
1881
+ ).action(async (name, opts) => {
1882
+ try {
1883
+ const kind = parseVaultKind(opts.kind);
1884
+ const result = await (await vaultValueClient(opts)).post(
1885
+ vaultPath("/api/v1/vault/reveal", { repo: opts.repo }),
1886
+ { name, kind }
1887
+ );
1888
+ output(opts, result, (r) => process.stdout.write(r.value));
1889
+ } catch (e) {
1890
+ fail(e, opts);
1891
+ }
1892
+ });
1893
+ vaultScope(
1894
+ vault.command("rm").description("Delete an item (with --repo, only the repo's override)").argument("<name>", "item name")
1895
+ ).action(async (name, opts) => {
1896
+ 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);
1899
+ output(opts, result, (r) => console.log(`deleted ${pc.cyan(safeTerm(r.name))} ${pc.dim(`(${r.kind})`)}`));
1900
+ } catch (e) {
1901
+ fail(e, opts);
1902
+ }
1903
+ });
1717
1904
  common(
1718
1905
  actions.command("cancel").description("Cancel a run").argument("<run-id>", "run ID")
1719
1906
  ).action(async (runId, opts) => {
@@ -1804,6 +1991,773 @@ function registerActionsCommands(program2) {
1804
1991
  });
1805
1992
  }
1806
1993
 
1994
+ // src/market.ts
1995
+ init_esm_shims();
1996
+ import pc2 from "picocolors";
1997
+ var USD_RE = /^\d+(\.\d+)?([eE][+-]?\d+)?$/;
1998
+ var RATE_UNITS = ["second", "minute", "hour"];
1999
+ function parseRate(price, per) {
2000
+ if (price === void 0) {
2001
+ throw new ActionsCliError("invalid_argument", "A price is required \u2014 pass --price <usd>.");
2002
+ }
2003
+ const usd = Number(price);
2004
+ if (!USD_RE.test(price) || !Number.isFinite(usd) || usd <= 0) {
2005
+ throw new ActionsCliError(
2006
+ "invalid_argument",
2007
+ `Invalid --price "${price}". Expected a positive dollar amount (e.g. 0.12).`
2008
+ );
2009
+ }
2010
+ const unit = per ?? "second";
2011
+ if (!RATE_UNITS.includes(unit)) {
2012
+ throw new ActionsCliError(
2013
+ "invalid_argument",
2014
+ `--per must be second, minute, or hour, got "${per}".`
2015
+ );
2016
+ }
2017
+ return { usd, per: unit };
2018
+ }
2019
+ function parseCapacity(value) {
2020
+ if (value === void 0) return void 0;
2021
+ const n = Number(value);
2022
+ if (!Number.isInteger(n) || n <= 0) {
2023
+ throw new ActionsCliError(
2024
+ "invalid_argument",
2025
+ `Invalid --capacity "${value}". Expected a positive integer.`
2026
+ );
2027
+ }
2028
+ return n;
2029
+ }
2030
+ function parseExpiresIn(value) {
2031
+ if (value === void 0) return void 0;
2032
+ const hours = Number(value);
2033
+ if (!Number.isFinite(hours) || hours <= 0) {
2034
+ throw new ActionsCliError(
2035
+ "invalid_argument",
2036
+ `Invalid --expires-in "${value}". Expected a positive number of hours.`
2037
+ );
2038
+ }
2039
+ return hours;
2040
+ }
2041
+ function sellBody(opts) {
2042
+ const rate = parseRate(opts.price, opts.per);
2043
+ const maxConcurrent = parseCapacity(opts.capacity);
2044
+ const expiresInHours = parseExpiresIn(opts.expiresIn);
2045
+ if (opts.renew && expiresInHours === void 0) {
2046
+ throw new ActionsCliError(
2047
+ "invalid_argument",
2048
+ "--renew requires --expires-in \u2014 a standing listing never expires."
2049
+ );
2050
+ }
2051
+ return {
2052
+ size: opts.size,
2053
+ ...opts.region !== void 0 && { region: opts.region },
2054
+ usd: rate.usd,
2055
+ per: rate.per,
2056
+ ...maxConcurrent !== void 0 && { maxConcurrent },
2057
+ ...expiresInHours !== void 0 && { expiresInHours },
2058
+ ...opts.renew && { rollover: true }
2059
+ };
2060
+ }
2061
+ function listingPatchBody(listingId, change) {
2062
+ const body = { askId: listingId };
2063
+ if (change.price !== void 0 || change.per !== void 0) {
2064
+ const rate = parseRate(change.price, change.per);
2065
+ body.usd = rate.usd;
2066
+ body.per = rate.per;
2067
+ }
2068
+ const maxConcurrent = parseCapacity(change.capacity);
2069
+ if (maxConcurrent !== void 0) body.maxConcurrent = maxConcurrent;
2070
+ if (change.status !== void 0) body.status = change.status;
2071
+ if (body.usd === void 0 && body.maxConcurrent === void 0 && body.status === void 0) {
2072
+ throw new ActionsCliError(
2073
+ "invalid_argument",
2074
+ "Nothing to change \u2014 pass --price, --capacity, or a status command."
2075
+ );
2076
+ }
2077
+ return body;
2078
+ }
2079
+ function credentialEnvVar(providerId, fieldName) {
2080
+ const snake = (s) => s.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").toUpperCase();
2081
+ return `${snake(providerId)}_${snake(fieldName)}`;
2082
+ }
2083
+ function credentialValues(providerId, fields, overrides, env = process.env) {
2084
+ const declared = new Set(fields.map((f) => f.name));
2085
+ const unknown = Object.keys(overrides).filter((name) => !declared.has(name));
2086
+ if (unknown.length > 0) {
2087
+ throw new ActionsCliError(
2088
+ "invalid_argument",
2089
+ `${providerId} takes no credential field "${unknown.join('", "')}" \u2014 expected one of ${fields.map((f) => f.name).join(", ")}.`
2090
+ );
2091
+ }
2092
+ const values = {};
2093
+ const fromFlag = [];
2094
+ const missing = [];
2095
+ const envNames = {};
2096
+ for (const field of fields) {
2097
+ envNames[field.name] = credentialEnvVar(providerId, field.name);
2098
+ const fromOverride = overrides[field.name];
2099
+ if (fromOverride !== void 0 && fromOverride.trim() !== "") {
2100
+ values[field.name] = fromOverride.trim();
2101
+ fromFlag.push(field.name);
2102
+ continue;
2103
+ }
2104
+ const fromEnv = env[envNames[field.name]];
2105
+ if (fromEnv !== void 0 && fromEnv.trim() !== "") {
2106
+ values[field.name] = fromEnv.trim();
2107
+ continue;
2108
+ }
2109
+ if (field.required !== false) missing.push(field);
2110
+ }
2111
+ return { values, missing, fromFlag, envNames };
2112
+ }
2113
+ function credentialBody(fields, values) {
2114
+ if (fields.length === 1) {
2115
+ return { key: values[fields[0].name] };
2116
+ }
2117
+ return { fields: values };
2118
+ }
2119
+ function formatUsd(usd) {
2120
+ return `$${usd.toFixed(6).replace(/\.?0+$/, "")}`;
2121
+ }
2122
+ function formatRate(usd, per) {
2123
+ return `${formatUsd(usd)}/${per}`;
2124
+ }
2125
+ function formatListingStatus(ask) {
2126
+ let label;
2127
+ if (ask.status === "withdrawn") label = pc2.gray("withdrawn");
2128
+ else if (ask.status === "paused") label = pc2.yellow("paused");
2129
+ else if (!ask.live) label = pc2.gray("expired");
2130
+ else label = pc2.green("live");
2131
+ if (ask.pendingUsd !== null && ask.pendingPer !== null) {
2132
+ label += pc2.dim(` \u2192 ${formatRate(ask.pendingUsd, ask.pendingPer)} next window`);
2133
+ }
2134
+ return label;
2135
+ }
2136
+ function formatListingRow(ask) {
2137
+ const expiry = ask.expiresAt ? ask.rollover ? `renews ${ask.expiresAt}` : `expires ${ask.expiresAt}` : "standing";
2138
+ return [
2139
+ ask.id,
2140
+ formatListingStatus(ask),
2141
+ safeTerm(ask.size),
2142
+ safeTerm(ask.region ?? "-"),
2143
+ formatRate(ask.usd, ask.per),
2144
+ pc2.dim(`\xD7${ask.maxConcurrent}`),
2145
+ pc2.dim(expiry)
2146
+ ].join(" ");
2147
+ }
2148
+ function formatProviderStatus(p5) {
2149
+ const lines = [];
2150
+ lines.push(`${pc2.bold(safeTerm(p5.name))} ${pc2.dim(`selling ${safeTerm(p5.provider)}`)}`);
2151
+ lines.push(`id: ${p5.id}`);
2152
+ lines.push(
2153
+ `credential: ${p5.executorCredentialConnected ? pc2.green("connected") : pc2.yellow("not connected \u2014 your listings cannot fill until `compute market credential connect`")}`
2154
+ );
2155
+ if (p5.revokedAt) lines.push(pc2.red(`revoked: ${p5.revokedAt}`));
2156
+ lines.push(
2157
+ `sizes: ${p5.sizes.map((s) => `${s.name} (${s.label})`).join(", ") || "-"}`
2158
+ );
2159
+ lines.push(`regions: ${p5.regions.join(", ") || "-"}`);
2160
+ if (p5.credentialFields.length > 0) {
2161
+ lines.push(
2162
+ `credential fields: ${p5.credentialFields.map((f) => `${f.name}${f.secret ? " (secret)" : ""}`).join(", ")}`
2163
+ );
2164
+ }
2165
+ lines.push(`created: ${p5.createdAt}`);
2166
+ return lines.join("\n");
2167
+ }
2168
+ function formatBook(book) {
2169
+ const lines = [];
2170
+ lines.push(pc2.bold("sellers' asking prices"));
2171
+ if (book.asks.length === 0) lines.push(pc2.dim(" no listings are live"));
2172
+ for (const ask of book.asks) {
2173
+ const where = ask.region ? safeTerm(ask.region) : "any region";
2174
+ const expiry = ask.expiresAt ? pc2.dim(ask.rollover ? ` renews ${ask.expiresAt}` : ` until ${ask.expiresAt}`) : "";
2175
+ lines.push(
2176
+ ` ${formatRate(ask.usd, ask.per)} ${safeTerm(ask.providerName)} ${safeTerm(ask.size)} ${where} up to ${ask.maxConcurrent} at once${expiry}`
2177
+ );
2178
+ }
2179
+ lines.push("");
2180
+ lines.push(pc2.bold("buyer offers"));
2181
+ if (book.bids.length === 0) lines.push(pc2.dim(" no open offers"));
2182
+ for (const bid of book.bids) {
2183
+ const where = bid.region ? safeTerm(bid.region) : "anywhere";
2184
+ lines.push(
2185
+ ` pays up to ${formatRate(bid.maxUsd, bid.per)} ${safeTerm(bid.size)} ${where} ${pc2.dim(`posted ${bid.createdAt}`)}`
2186
+ );
2187
+ }
2188
+ lines.push("");
2189
+ lines.push(pc2.bold("recent sales"));
2190
+ if (book.fills.length === 0) lines.push(pc2.dim(" nothing has sold yet"));
2191
+ for (const fill of book.fills) {
2192
+ const statusNote = fill.status === "replaced" ? pc2.yellow(" replaced") : fill.status === "closed" ? pc2.gray(" closed") : "";
2193
+ lines.push(
2194
+ ` ${pc2.dim(fill.id)} ${formatRate(fill.usd, fill.per)} ${safeTerm(fill.provider)} ${safeTerm(fill.size)} ${safeTerm(fill.region ?? "-")} ${pc2.dim(fill.createdAt)}${statusNote}`
2195
+ );
2196
+ }
2197
+ return lines.join("\n");
2198
+ }
2199
+ function formatReplacedFill(fill) {
2200
+ const settled = formatUsd(fill.settledMicroUsd / 1e6);
2201
+ const job = fill.jobRequeued ? pc2.dim(" \u2014 job re-places on the buyer's next provider") : "";
2202
+ return `replaced ${pc2.cyan(fill.fillId)} settled ${settled}${job}`;
2203
+ }
2204
+ function replaceTarget(listingId, fillId) {
2205
+ if (fillId !== void 0) {
2206
+ return {
2207
+ path: `/api/v1/market/fills/${encodeURIComponent(fillId)}/replace`,
2208
+ scope: "fill"
2209
+ };
2210
+ }
2211
+ if (listingId === void 0) {
2212
+ throw new ActionsCliError(
2213
+ "invalid_argument",
2214
+ "Nothing to replace \u2014 pass a listing ID or --fill <fill-id>."
2215
+ );
2216
+ }
2217
+ return {
2218
+ path: `/api/v1/market/listings/${encodeURIComponent(listingId)}/replace`,
2219
+ scope: "listing"
2220
+ };
2221
+ }
2222
+ function formatSettlementRow(s) {
2223
+ const period = `${s.periodStart.slice(0, 10)} \u2192 ${s.periodEnd.slice(0, 10)}`;
2224
+ const paid = s.paidAt ? pc2.green(`paid ${s.paidAt.slice(0, 10)}`) : s.invoiceRef ? pc2.yellow(`invoiced ${safeTerm(s.invoiceRef)}`) : pc2.dim("unpaid");
2225
+ return [
2226
+ pc2.dim(period),
2227
+ `${s.vcpuSeconds.toLocaleString("en-US")} vCPU\xB7s`,
2228
+ `gross ${formatUsd(s.grossUsd)}`,
2229
+ `net ${formatUsd(s.netUsd)}`,
2230
+ paid
2231
+ ].join(" ");
2232
+ }
2233
+ function registerMarketCommands(program2) {
2234
+ 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");
2236
+ common(
2237
+ market.command("listings").alias("list").description("Your listings \u2014 live, paused, expired, and withdrawn")
2238
+ ).action(async (opts) => {
2239
+ try {
2240
+ const { asks } = await (await client(opts)).get(
2241
+ "/api/v1/market/asks"
2242
+ );
2243
+ output(opts, asks, (rows) => {
2244
+ if (rows.length === 0) {
2245
+ console.log("No listings yet \u2014 post one with `compute market sell`.");
2246
+ return;
2247
+ }
2248
+ for (const ask of rows) console.log(formatListingRow(ask));
2249
+ });
2250
+ } catch (e) {
2251
+ fail(e, opts);
2252
+ }
2253
+ });
2254
+ common(
2255
+ market.command("sell").description("Post a listing: sell capacity at a price").requiredOption("--price <usd>", "price per unit of time (e.g. 0.12)").option("--per <unit>", "time unit the price is per: second, minute, or hour (default: second)").option("--size <tier>", "size tier to sell (default: medium, else the provider's first tier)").option("--region <region>", "region to sell in (default: anywhere the provider runs)").option("--capacity <n>", "max simultaneous fills (default: 1)").option("--expires-in <hours>", "delist after this many hours (default: standing)").option("--renew", "re-list for the same window each time it expires (requires --expires-in)")
2256
+ ).action(async (opts) => {
2257
+ try {
2258
+ const c = await client(opts);
2259
+ let size = opts.size;
2260
+ if (size === void 0) {
2261
+ const { provider } = await c.get(
2262
+ "/api/v1/market/provider"
2263
+ );
2264
+ const preset = provider.sizes.find((s) => s.name === "medium") ?? provider.sizes[0];
2265
+ if (!preset) {
2266
+ throw new ActionsCliError(
2267
+ "invalid_argument",
2268
+ `--size is required \u2014 ${provider.provider} advertises no size tiers.`
2269
+ );
2270
+ }
2271
+ size = preset.name;
2272
+ console.error(pc2.dim(`no --size given \u2014 listing at "${size}"`));
2273
+ }
2274
+ const result = await c.post(
2275
+ "/api/v1/market/asks",
2276
+ sellBody({ ...opts, size })
2277
+ );
2278
+ output(opts, result, (r) => {
2279
+ console.log(`listed ${pc2.cyan(r.ask.id)}`);
2280
+ console.log(formatListingRow(r.ask));
2281
+ });
2282
+ } catch (e) {
2283
+ fail(e, opts);
2284
+ }
2285
+ });
2286
+ common(
2287
+ market.command("price").description("Reprice a listing").argument("<listing-id>", "listing ID").requiredOption("--price <usd>", "new price per unit of time (e.g. 0.08)").option("--per <unit>", "time unit the price is per: second, minute, or hour (default: second)")
2288
+ ).action(async (listingId, opts) => {
2289
+ try {
2290
+ const result = await (await client(opts)).post(
2291
+ "/api/v1/market/asks",
2292
+ listingPatchBody(listingId, { price: opts.price, per: opts.per })
2293
+ );
2294
+ output(opts, result, (r) => {
2295
+ const rate = r.ask.pendingUsd !== null && r.ask.pendingPer !== null ? `${formatRate(r.ask.pendingUsd, r.ask.pendingPer)} ${pc2.dim("(applies next window)")}` : formatRate(r.ask.usd, r.ask.per);
2296
+ console.log(`repriced ${pc2.cyan(r.ask.id)} ${rate}`);
2297
+ });
2298
+ } catch (e) {
2299
+ fail(e, opts);
2300
+ }
2301
+ });
2302
+ const setStatus = async (listingId, status, opts) => {
2303
+ try {
2304
+ const result = await (await client(opts)).post(
2305
+ "/api/v1/market/asks",
2306
+ listingPatchBody(listingId, { status })
2307
+ );
2308
+ output(opts, result, (r) => {
2309
+ console.log(`${status === "live" ? "resumed" : "paused"} ${pc2.cyan(r.ask.id)}`);
2310
+ });
2311
+ } catch (e) {
2312
+ fail(e, opts);
2313
+ }
2314
+ };
2315
+ common(
2316
+ market.command("pause").description("Pause a listing \u2014 it stays posted but cannot fill").argument("<listing-id>", "listing ID")
2317
+ ).action((listingId, opts) => setStatus(listingId, "paused", opts));
2318
+ common(
2319
+ market.command("resume").description("Resume a paused listing").argument("<listing-id>", "listing ID")
2320
+ ).action((listingId, opts) => setStatus(listingId, "live", opts));
2321
+ common(
2322
+ market.command("withdraw").description("Withdraw a listing \u2014 it leaves the book for good").argument("<listing-id>", "listing ID")
2323
+ ).action(async (listingId, opts) => {
2324
+ try {
2325
+ const result = await (await client(opts)).del(
2326
+ `/api/v1/market/asks?id=${encodeURIComponent(listingId)}`
2327
+ );
2328
+ output(opts, result, (r) => {
2329
+ console.log(`withdrew ${pc2.cyan(r.ask.id)}`);
2330
+ });
2331
+ } catch (e) {
2332
+ fail(e, opts);
2333
+ }
2334
+ });
2335
+ common(
2336
+ market.command("replace").description(
2337
+ "Take capacity back: evict a sale, or pause a listing and evict every sale on it.\nAn evicted Actions job restarts on the buyer's next provider; the buyer pays\nonly for the seconds the sale actually ran."
2338
+ ).argument("[listing-id]", "listing to pause and evict every sale on").option("--fill <fill-id>", "evict one sale without touching the listing")
2339
+ ).action(
2340
+ async (listingId, opts) => {
2341
+ try {
2342
+ const target = replaceTarget(listingId, opts.fill);
2343
+ const c = await client(opts);
2344
+ if (target.scope === "fill") {
2345
+ const result = await c.post(target.path, {});
2346
+ output(opts, result, (r) => console.log(formatReplacedFill(r.fill)));
2347
+ } else {
2348
+ const result = await c.post(target.path, {});
2349
+ output(opts, result, (r) => {
2350
+ console.log(`paused ${pc2.cyan(r.listing.id)}`);
2351
+ if (r.fills.length === 0) {
2352
+ console.log(pc2.dim(" no live sales to evict"));
2353
+ return;
2354
+ }
2355
+ for (const f of r.fills) {
2356
+ if (f.ok) console.log(formatReplacedFill(f.fill));
2357
+ else console.log(pc2.red(` could not evict a sale \u2014 ${f.error}`));
2358
+ }
2359
+ });
2360
+ if (result.fills.some((f) => !f.ok)) process.exitCode = 1;
2361
+ }
2362
+ } catch (e) {
2363
+ fail(e, opts);
2364
+ }
2365
+ }
2366
+ );
2367
+ common(
2368
+ market.command("book").description("Live prices: sellers' asking prices, buyer offers, recent sales")
2369
+ ).action(async (opts) => {
2370
+ try {
2371
+ const book = await (await client(opts)).get(
2372
+ "/api/v1/market/book"
2373
+ );
2374
+ output(opts, book, (b) => console.log(formatBook(b)));
2375
+ } catch (e) {
2376
+ fail(e, opts);
2377
+ }
2378
+ });
2379
+ common(
2380
+ market.command("settlements").description("Your monthly payout reports")
2381
+ ).action(async (opts) => {
2382
+ try {
2383
+ const { settlements } = await (await client(opts)).get("/api/v1/market/settlements");
2384
+ output(opts, settlements, (rows) => {
2385
+ if (rows.length === 0) {
2386
+ console.log("No settlements yet.");
2387
+ return;
2388
+ }
2389
+ for (const s of rows) console.log(formatSettlementRow(s));
2390
+ });
2391
+ } catch (e) {
2392
+ fail(e, opts);
2393
+ }
2394
+ });
2395
+ common(
2396
+ market.command("status").description("Who you are on the market: seller identity, credential, sizes, regions")
2397
+ ).action(async (opts) => {
2398
+ try {
2399
+ const { provider } = await (await client(opts)).get("/api/v1/market/provider");
2400
+ output(opts, provider, (p5) => console.log(formatProviderStatus(p5)));
2401
+ } catch (e) {
2402
+ fail(e, opts);
2403
+ }
2404
+ });
2405
+ const credential = market.command("credential").description("Connect the credential your listings fill under");
2406
+ common(
2407
+ credential.command("connect").description(
2408
+ "Save the executor credential your sold capacity runs under. Each field is read from\na <PROVIDER>_<FIELD> env var (e.g. TENSORLAKE_API_KEY) \u2014 never pass secrets in argv;\n--field name=value overrides a field (visible in shell history)"
2409
+ ).option("--field <pairs...>", "credential fields as name=value (prefer env vars for secrets)")
2410
+ ).action(async (opts) => {
2411
+ try {
2412
+ const c = await secretValueClient(opts, "credential values");
2413
+ const { provider } = await c.get(
2414
+ "/api/v1/market/provider"
2415
+ );
2416
+ if (provider.credentialFields.length === 0) {
2417
+ throw new ActionsCliError(
2418
+ "invalid_argument",
2419
+ `${provider.provider} authenticates with the deployment's own identity \u2014 there is no credential to connect.`
2420
+ );
2421
+ }
2422
+ const resolved = credentialValues(
2423
+ provider.provider,
2424
+ provider.credentialFields,
2425
+ parseInputs(opts.field)
2426
+ );
2427
+ if (resolved.missing.length > 0) {
2428
+ const hints = resolved.missing.map((f) => ` export ${resolved.envNames[f.name]}=\u2026 # ${f.label}`).join("\n");
2429
+ throw new ActionsCliError(
2430
+ "invalid_argument",
2431
+ `Missing credential field${resolved.missing.length === 1 ? "" : "s"}. Set:
2432
+ ${hints}`
2433
+ );
2434
+ }
2435
+ if (resolved.fromFlag.some((name) => provider.credentialFields.find((f) => f.name === name)?.secret)) {
2436
+ console.error(
2437
+ pc2.yellow("note: a secret passed via --field stays in your shell history \u2014 prefer the env vars.")
2438
+ );
2439
+ }
2440
+ const result = await c.post(
2441
+ "/api/v1/market/provider/credential",
2442
+ credentialBody(provider.credentialFields, resolved.values)
2443
+ );
2444
+ output(opts, result, (r) => {
2445
+ console.log(`connected ${pc2.cyan(safeTerm(r.provider.name))} credential saved`);
2446
+ });
2447
+ } catch (e) {
2448
+ fail(e, opts);
2449
+ }
2450
+ });
2451
+ common(
2452
+ credential.command("disconnect").description("Remove the stored credential \u2014 listings stop filling until a new one connects")
2453
+ ).action(async (opts) => {
2454
+ try {
2455
+ const result = await (await client(opts)).del(
2456
+ "/api/v1/market/provider/credential"
2457
+ );
2458
+ output(opts, result, (r) => {
2459
+ console.log(`disconnected ${pc2.cyan(safeTerm(r.provider.name))} credential removed`);
2460
+ });
2461
+ } catch (e) {
2462
+ fail(e, opts);
2463
+ }
2464
+ });
2465
+ }
2466
+
2467
+ // src/sandboxes.ts
2468
+ init_esm_shims();
2469
+ import pc3 from "picocolors";
2470
+ function shellQuote(arg) {
2471
+ return `'${arg.replace(/'/g, `'\\''`)}'`;
2472
+ }
2473
+ function printSandbox(s) {
2474
+ const attach = "attach" in s ? s.attach : void 0;
2475
+ console.log(`${pc3.cyan(s.id)} ${s.status} ${s.provider ?? "\u2014"} ${s.providerSandboxId ?? "\u2014"}`);
2476
+ console.log(` label: ${s.label ?? "\u2014"} commands: ${s.commandCount} created: ${s.createdAt}`);
2477
+ if (s.destroyError) console.log(` destroyError: ${pc3.red(s.destroyError)}`);
2478
+ if (attach !== void 0) {
2479
+ console.log(
2480
+ attach === null ? " attach: none (market fill or ambient provider \u2014 platform drive only)" : ` attach: ${attach.provider} / ${attach.providerSandboxId}`
2481
+ );
2482
+ }
2483
+ }
2484
+ function printProcess(p5) {
2485
+ const exit = p5.status === "exited" ? `exit=${p5.exitCode ?? "?"}${p5.signal ? ` (${p5.signal})` : ""}` : "";
2486
+ console.log(`${pc3.cyan(p5.jobId)} ${p5.status}${exit ? " " + exit : ""} ${p5.command}`);
2487
+ }
2488
+ function registerSandboxesCommands(program2) {
2489
+ const cmd = program2.command("sandboxes").alias("sbx").description("Create and drive sandboxes through the platform").configureOutput({ outputError: usageErrorOutput });
2490
+ cmd.command("create").description('Place a sandbox (provider order walks 1,2,3; "market" bids first)').option("--order <providers>", 'provider order, comma-separated (e.g. "market,blaxel,vercel")').option("--label <label>", "label for the sandbox").option("--image <image>", "container image to boot").option("--snapshot-id <id>", "provider snapshot to resume").option("--cpus <n>", "CPU cores").option("--memory-mb <n>", "memory in MB").option("--disk-mb <n>", "ephemeral disk in MB").option("--timeout-ms <ms>", "sandbox timeout in ms").option("--secret <name>", "vault secret name to inject (repeatable)", (v, a) => a.concat(v), []).option("--api-key <key>", "platform API key (or COMPUTE_API_KEY)").option("--base-url <url>", "platform base URL").option("--allow-untrusted-host", "allow non-computesdk.com base URLs").option("--json", "print the raw response").action(async (opts) => {
2491
+ try {
2492
+ const c = await client(opts);
2493
+ const resources = {};
2494
+ if (opts.cpus) resources.cpus = Number(opts.cpus);
2495
+ if (opts.memoryMb) resources.memoryMb = Number(opts.memoryMb);
2496
+ if (opts.diskMb) resources.ephemeralDiskMb = Number(opts.diskMb);
2497
+ const body = {};
2498
+ if (opts.order) body.providerOrder = opts.order.split(",").map((s) => s.trim());
2499
+ if (opts.label) body.label = opts.label;
2500
+ if (opts.image) body.image = opts.image;
2501
+ if (opts.snapshotId) body.snapshotId = opts.snapshotId;
2502
+ if (opts.timeoutMs) body.timeoutMs = Number(opts.timeoutMs);
2503
+ if (opts.secret && opts.secret.length > 0) body.secrets = opts.secret;
2504
+ if (Object.keys(resources).length > 0) body.resources = resources;
2505
+ const res = await c.post("/api/v1/sandboxes", body);
2506
+ output(opts, res.sandbox, printSandbox);
2507
+ } catch (e) {
2508
+ fail(e, opts);
2509
+ }
2510
+ });
2511
+ cmd.command("list").description("List sandboxes").option("--status <status>", "creating | running | destroyed").option("--limit <n>", "page size").option("--cursor <cursor>", "page cursor from a previous response").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (opts) => {
2512
+ try {
2513
+ const c = await client(opts);
2514
+ const res = await c.get(
2515
+ "/api/v1/sandboxes",
2516
+ { status: opts.status, limit: opts.limit, cursor: opts.cursor }
2517
+ );
2518
+ output(opts, res, (page) => {
2519
+ page.sandboxes.forEach(printSandbox);
2520
+ if (page.nextCursor) console.log(pc3.dim(`nextCursor: ${page.nextCursor}`));
2521
+ });
2522
+ } catch (e) {
2523
+ fail(e, opts);
2524
+ }
2525
+ });
2526
+ cmd.command("get <sandboxId>").description("Show one sandbox (includes the BYOK attach descriptor)").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, opts) => {
2527
+ try {
2528
+ const c = await client(opts);
2529
+ const res = await c.get(`/api/v1/sandboxes/${sandboxId}`);
2530
+ output(opts, res.sandbox, printSandbox);
2531
+ } catch (e) {
2532
+ fail(e, opts);
2533
+ }
2534
+ });
2535
+ cmd.command("destroy <sandboxId>").description("Destroy a sandbox (verified teardown + cost settle)").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, opts) => {
2536
+ try {
2537
+ const c = await client(opts);
2538
+ const res = await c.del(`/api/v1/sandboxes/${sandboxId}`);
2539
+ output(opts, res.sandbox, printSandbox);
2540
+ } catch (e) {
2541
+ fail(e, opts);
2542
+ }
2543
+ });
2544
+ cmd.command("exec <sandboxId> <command...>").description("Run a one-shot command and print its result").option("--timeout-ms <ms>", "command timeout in ms").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, command, opts) => {
2545
+ try {
2546
+ const c = await client(opts);
2547
+ const res = await c.post(
2548
+ `/api/v1/sandboxes/${sandboxId}/commands`,
2549
+ {
2550
+ command: command.map(shellQuote).join(" "),
2551
+ ...opts.timeoutMs ? { timeoutMs: Number(opts.timeoutMs) } : {}
2552
+ }
2553
+ );
2554
+ output(opts, res, (r) => {
2555
+ process.stdout.write(r.stdout);
2556
+ if (r.stderr) process.stderr.write(r.stderr);
2557
+ if (!r.stdout) console.log(`exit ${r.exitCode} (${r.durationMs}ms)`);
2558
+ });
2559
+ if (res.exitCode !== 0) process.exitCode = res.exitCode;
2560
+ } catch (e) {
2561
+ fail(e, opts);
2562
+ }
2563
+ });
2564
+ cmd.command("spawn <sandboxId> <command...>").description("Start a detached process that outlives this request").option("--cwd <dir>", "working directory").option("-e, --env <key=value>", "environment variable (repeatable)", (v, a) => a.concat(v), []).option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, command, opts) => {
2565
+ try {
2566
+ const c = await client(opts);
2567
+ const res = await c.post(
2568
+ `/api/v1/sandboxes/${sandboxId}/processes`,
2569
+ {
2570
+ command: command.map(shellQuote).join(" "),
2571
+ ...opts.cwd ? { cwd: opts.cwd } : {},
2572
+ ...opts.env && opts.env.length > 0 ? { env: parseInputs(opts.env) } : {}
2573
+ }
2574
+ );
2575
+ output(opts, res.process, printProcess);
2576
+ } catch (e) {
2577
+ fail(e, opts);
2578
+ }
2579
+ });
2580
+ cmd.command("ps <sandboxId>").description("List the sandbox's tracked processes").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, opts) => {
2581
+ try {
2582
+ const c = await client(opts);
2583
+ const res = await c.get(
2584
+ `/api/v1/sandboxes/${sandboxId}/processes`
2585
+ );
2586
+ output(opts, res.processes, (list) => list.forEach(printProcess));
2587
+ } catch (e) {
2588
+ fail(e, opts);
2589
+ }
2590
+ });
2591
+ cmd.command("logs <sandboxId> <jobId>").description("Show a process's buffered output (--follow streams until exit)").option("-f, --follow", "stream output until the process exits").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, jobId, opts) => {
2592
+ try {
2593
+ const c = await client(opts);
2594
+ if (opts.follow) {
2595
+ await followProcess(c, sandboxId, jobId);
2596
+ return;
2597
+ }
2598
+ const res = await c.get(
2599
+ `/api/v1/sandboxes/${sandboxId}/processes/${jobId}`
2600
+ );
2601
+ output(opts, res.process, (p5) => {
2602
+ if (p5.stdout) process.stdout.write(p5.stdout);
2603
+ if (p5.stderr) process.stderr.write(p5.stderr);
2604
+ if (!p5.stdout && !p5.stderr) printProcess(p5);
2605
+ });
2606
+ } catch (e) {
2607
+ fail(e, opts);
2608
+ }
2609
+ });
2610
+ cmd.command("wait <sandboxId> <jobId>").description("Block until a process exits").option("--timeout-ms <ms>", "bound the wait (job keeps running on timeout)").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, jobId, opts) => {
2611
+ try {
2612
+ const c = await client(opts);
2613
+ const res = await c.post(
2614
+ `/api/v1/sandboxes/${sandboxId}/processes/${jobId}/wait`,
2615
+ opts.timeoutMs ? { timeoutMs: Number(opts.timeoutMs) } : {}
2616
+ );
2617
+ output(opts, res.process, printProcess);
2618
+ if (res.process.status === "exited") {
2619
+ process.exitCode = res.process.exitCode ?? 128;
2620
+ }
2621
+ } catch (e) {
2622
+ fail(e, opts);
2623
+ }
2624
+ });
2625
+ cmd.command("kill <sandboxId> <jobId>").description("Signal a process (default SIGTERM)").option("--signal <signal>", "signal name or number, e.g. SIGKILL or 9").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, jobId, opts) => {
2626
+ try {
2627
+ const c = await client(opts);
2628
+ const res = await c.post(
2629
+ `/api/v1/sandboxes/${sandboxId}/processes/${jobId}/kill`,
2630
+ opts.signal ? { signal: opts.signal } : {}
2631
+ );
2632
+ output(opts, res.process, printProcess);
2633
+ } catch (e) {
2634
+ fail(e, opts);
2635
+ }
2636
+ });
2637
+ 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) => {
2638
+ try {
2639
+ const c = await client(opts);
2640
+ const res = await c.get(`/api/v1/sandboxes/${sandboxId}/files`, {
2641
+ path: path5 ?? "/"
2642
+ });
2643
+ output(opts, res, (view) => {
2644
+ if (view.type === "directory") {
2645
+ for (const e of view.entries) {
2646
+ console.log(`${e.type === "directory" ? pc3.blue("d") : " "} ${e.name}${e.size !== void 0 ? ` ${pc3.dim(String(e.size))}` : ""}`);
2647
+ }
2648
+ } else {
2649
+ console.log(`${view.path} file ${view.content.length} chars`);
2650
+ }
2651
+ });
2652
+ } catch (e) {
2653
+ fail(e, opts);
2654
+ }
2655
+ });
2656
+ 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) => {
2657
+ try {
2658
+ const c = await client(opts);
2659
+ const res = await c.get(`/api/v1/sandboxes/${sandboxId}/files`, { path: path5 });
2660
+ output(opts, res, (view) => {
2661
+ if (view.type !== "file") {
2662
+ console.error(pc3.red(`${view.path} is a directory`));
2663
+ process.exit(1);
2664
+ }
2665
+ process.stdout.write(view.content);
2666
+ if (!view.content.endsWith("\n")) process.stdout.write("\n");
2667
+ });
2668
+ } catch (e) {
2669
+ fail(e, opts);
2670
+ }
2671
+ });
2672
+ 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) => {
2673
+ try {
2674
+ let content = opts.content;
2675
+ if (content === void 0 && opts.file) {
2676
+ const { readFile } = await import("fs/promises");
2677
+ content = await readFile(opts.file, "utf8");
2678
+ }
2679
+ if (content === void 0 && !process.stdin.isTTY) {
2680
+ content = await new Promise((resolve) => {
2681
+ let data = "";
2682
+ process.stdin.setEncoding("utf8");
2683
+ process.stdin.on("data", (chunk) => data += chunk);
2684
+ process.stdin.on("end", () => resolve(data));
2685
+ });
2686
+ }
2687
+ if (content === void 0) {
2688
+ console.error(pc3.red("Pass --content, --file, or pipe stdin"));
2689
+ process.exit(1);
2690
+ }
2691
+ const c = await client(opts);
2692
+ const res = await c.post(
2693
+ `/api/v1/sandboxes/${sandboxId}/files`,
2694
+ { path: path5, content }
2695
+ );
2696
+ output(opts, res, (r) => console.log(`wrote ${r.path}`));
2697
+ } catch (e) {
2698
+ fail(e, opts);
2699
+ }
2700
+ });
2701
+ 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) => {
2702
+ try {
2703
+ const c = await client(opts);
2704
+ const res = await c.post(`/api/v1/sandboxes/${sandboxId}/files`, {
2705
+ path: path5,
2706
+ mkdir: true
2707
+ });
2708
+ output(opts, res, (r) => console.log(`created ${r.path}`));
2709
+ } catch (e) {
2710
+ fail(e, opts);
2711
+ }
2712
+ });
2713
+ 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) => {
2714
+ try {
2715
+ const c = await client(opts);
2716
+ const res = await c.del(
2717
+ `/api/v1/sandboxes/${sandboxId}/files?path=${encodeURIComponent(path5)}`
2718
+ );
2719
+ output(opts, res, (r) => console.log(`removed ${r.path}`));
2720
+ } catch (e) {
2721
+ fail(e, opts);
2722
+ }
2723
+ });
2724
+ cmd.command("url <sandboxId>").description("Resolve the provider's public URL for a port").requiredOption("--port <n>", "port the service listens on").option("--protocol <protocol>", "protocol (default provider-chosen)").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, opts) => {
2725
+ try {
2726
+ const c = await client(opts);
2727
+ const res = await c.get(`/api/v1/sandboxes/${sandboxId}/urls`, {
2728
+ port: opts.port,
2729
+ protocol: opts.protocol
2730
+ });
2731
+ output(opts, res, (r) => console.log(safeTerm(r.url)));
2732
+ } catch (e) {
2733
+ fail(e, opts);
2734
+ }
2735
+ });
2736
+ }
2737
+ async function followProcess(c, sandboxId, jobId) {
2738
+ let exitCode = 0;
2739
+ for await (const { event, data } of c.sseNamed(
2740
+ `/api/v1/sandboxes/${sandboxId}/processes/${jobId}`
2741
+ )) {
2742
+ const payload = data;
2743
+ if (event === "stdout") process.stdout.write(String(payload.chunk ?? ""));
2744
+ else if (event === "stderr") process.stderr.write(String(payload.chunk ?? ""));
2745
+ else if (event === "exit") {
2746
+ exitCode = typeof payload.exitCode === "number" ? payload.exitCode : 128;
2747
+ return finish();
2748
+ } else if (event === "error") {
2749
+ console.error(pc3.red(`Error: ${String(payload.error ?? "stream error")}`));
2750
+ return finish(1);
2751
+ }
2752
+ }
2753
+ console.error(pc3.yellow("stream ended before the process exited"));
2754
+ return finish(2);
2755
+ function finish(code) {
2756
+ if (code !== void 0) exitCode = code;
2757
+ if (exitCode !== 0) process.exitCode = exitCode;
2758
+ }
2759
+ }
2760
+
1807
2761
  // src/index.ts
1808
2762
  init_repl();
1809
2763
 
@@ -1816,7 +2770,7 @@ import * as fs2 from "fs";
1816
2770
  import * as path3 from "path";
1817
2771
  import * as os2 from "os";
1818
2772
  import * as p from "@clack/prompts";
1819
- import pc3 from "picocolors";
2773
+ import pc5 from "picocolors";
1820
2774
  function getCredentialsDir() {
1821
2775
  return path3.join(os2.homedir(), ".computesdk");
1822
2776
  }
@@ -1874,7 +2828,7 @@ function openBrowser(url) {
1874
2828
  if (err) {
1875
2829
  p.log.warn("Could not open browser automatically.");
1876
2830
  p.log.info(`Open this URL manually:
1877
- ${pc3.cyan(url)}`);
2831
+ ${pc5.cyan(url)}`);
1878
2832
  }
1879
2833
  });
1880
2834
  }
@@ -1945,7 +2899,7 @@ async function resolveApiKey(options) {
1945
2899
  const stored = loadStoredCredentials();
1946
2900
  if (stored) {
1947
2901
  p.log.info(
1948
- `Using stored credentials from ${pc3.dim("~/.computesdk/credentials.json")}`
2902
+ `Using stored credentials from ${pc5.dim("~/.computesdk/credentials.json")}`
1949
2903
  );
1950
2904
  return stored;
1951
2905
  }
@@ -1959,14 +2913,14 @@ async function resolveApiKey(options) {
1959
2913
  try {
1960
2914
  storeCredentials(apiKey);
1961
2915
  p.log.success(
1962
- `Credentials saved to ${pc3.dim("~/.computesdk/credentials.json")}`
2916
+ `Credentials saved to ${pc5.dim("~/.computesdk/credentials.json")}`
1963
2917
  );
1964
2918
  } catch {
1965
2919
  p.log.warn("Could not save credentials to disk. You may need to authenticate again next time.");
1966
2920
  }
1967
2921
  return apiKey;
1968
2922
  } catch (error) {
1969
- spinner4.stop(pc3.red("Authentication failed"));
2923
+ spinner4.stop(pc5.red("Authentication failed"));
1970
2924
  throw error;
1971
2925
  }
1972
2926
  }
@@ -1974,7 +2928,7 @@ async function resolveApiKey(options) {
1974
2928
  // src/setup.ts
1975
2929
  init_esm_shims();
1976
2930
  import * as p2 from "@clack/prompts";
1977
- import pc4 from "picocolors";
2931
+ import pc6 from "picocolors";
1978
2932
  import { compute as compute2 } from "computesdk";
1979
2933
  async function ensureAuth(options) {
1980
2934
  try {
@@ -1982,8 +2936,8 @@ async function ensureAuth(options) {
1982
2936
  process.env.COMPUTESDK_API_KEY = apiKey;
1983
2937
  } catch (error) {
1984
2938
  p2.log.error(error.message);
1985
- p2.log.info(`Get your API key at ${pc4.cyan("https://console.computesdk.com")}`);
1986
- p2.outro(pc4.red("Setup incomplete"));
2939
+ p2.log.info(`Get your API key at ${pc6.cyan("https://console.computesdk.com")}`);
2940
+ p2.outro(pc6.red("Setup incomplete"));
1987
2941
  process.exit(1);
1988
2942
  }
1989
2943
  }
@@ -2012,7 +2966,7 @@ async function resolveProvider(providerFlag, options) {
2012
2966
  if (available.length === 0) {
2013
2967
  p2.log.error("No provider credentials detected");
2014
2968
  p2.log.info("Set one of: E2B_API_KEY, MODAL_TOKEN_ID, RAILWAY_API_KEY, etc.");
2015
- p2.outro(pc4.red("Setup incomplete"));
2969
+ p2.outro(pc6.red("Setup incomplete"));
2016
2970
  process.exit(1);
2017
2971
  }
2018
2972
  if (available.length === 1) {
@@ -2056,24 +3010,24 @@ program.name("compute").description("Cloud-native workspace management").version
2056
3010
  program.option("--login", "force re-authentication").option("--logout", "clear stored credentials").action(async (opts) => {
2057
3011
  if (opts.logout) {
2058
3012
  console.log();
2059
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3013
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2060
3014
  clearStoredCredentials();
2061
3015
  p4.log.success("Logged out. Stored credentials removed.");
2062
- p4.outro(pc6.green("Done!"));
3016
+ p4.outro(pc8.green("Done!"));
2063
3017
  process.exit(0);
2064
3018
  }
2065
3019
  if (opts.login) {
2066
3020
  console.log();
2067
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3021
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2068
3022
  await ensureAuth({ forceLogin: true });
2069
- p4.outro(pc6.green("Authenticated!"));
3023
+ p4.outro(pc8.green("Authenticated!"));
2070
3024
  process.exit(0);
2071
3025
  }
2072
3026
  program.help();
2073
3027
  });
2074
3028
  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) => {
2075
3029
  console.log();
2076
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3030
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2077
3031
  await ensureAuth();
2078
3032
  if (!repo) {
2079
3033
  const state = loadWorkspaceState();
@@ -2094,7 +3048,7 @@ program.command("create").description("Create a new workspace (same as create-co
2094
3048
  message: "Select workspace to attach (or create new)",
2095
3049
  options: [
2096
3050
  ...choices,
2097
- { value: "__new__", label: pc6.gray("+ Create new workspace") }
3051
+ { value: "__new__", label: pc8.gray("+ Create new workspace") }
2098
3052
  ]
2099
3053
  });
2100
3054
  if (p4.isCancel(selected)) {
@@ -2135,15 +3089,15 @@ program.command("create").description("Create a new workspace (same as create-co
2135
3089
  branch: options.branch,
2136
3090
  provider
2137
3091
  });
2138
- spinner4.stop(`Workspace ready: ${pc6.cyan(workspace.id)}`);
3092
+ spinner4.stop(`Workspace ready: ${pc8.cyan(workspace.id)}`);
2139
3093
  if (options.attach !== false) {
2140
3094
  console.log();
2141
3095
  await workspaceAttach(workspace.id);
2142
3096
  } else {
2143
- p4.outro(pc6.green("Workspace created!"));
3097
+ p4.outro(pc8.green("Workspace created!"));
2144
3098
  }
2145
3099
  } catch (error) {
2146
- spinner4.stop(pc6.red("Failed to create workspace"));
3100
+ spinner4.stop(pc8.red("Failed to create workspace"));
2147
3101
  p4.log.error(error.message);
2148
3102
  process.exit(1);
2149
3103
  }
@@ -2152,7 +3106,7 @@ var sandboxCmd = program.command("sandbox").alias("sb").description("Manage raw
2152
3106
  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) => {
2153
3107
  if (!opts.silent) {
2154
3108
  console.log();
2155
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3109
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2156
3110
  }
2157
3111
  await ensureAuth();
2158
3112
  const provider = await resolveProvider(opts.provider);
@@ -2164,7 +3118,7 @@ sandboxCmd.command("create").description("Create a new sandbox").option("-p, --p
2164
3118
  const sandbox = await compute4.sandbox.create(
2165
3119
  buildCreateOptions(opts)
2166
3120
  );
2167
- spinner4.stop(`Sandbox ready: ${pc6.cyan(sandbox.sandboxId)}`);
3121
+ spinner4.stop(`Sandbox ready: ${pc8.cyan(sandbox.sandboxId)}`);
2168
3122
  const { saveSandbox: saveSandbox2 } = await Promise.resolve().then(() => (init_workspace_cli(), workspace_cli_exports));
2169
3123
  await saveSandbox2({
2170
3124
  id: sandbox.sandboxId,
@@ -2177,7 +3131,7 @@ sandboxCmd.command("create").description("Create a new sandbox").option("-p, --p
2177
3131
  await startREPL(sandbox, provider);
2178
3132
  }
2179
3133
  } catch (error) {
2180
- spinner4.stop(pc6.red("Failed to create sandbox"));
3134
+ spinner4.stop(pc8.red("Failed to create sandbox"));
2181
3135
  p4.log.error(error.message);
2182
3136
  process.exit(1);
2183
3137
  }
@@ -2206,14 +3160,14 @@ sandboxCmd.command("create").description("Create a new sandbox").option("-p, --p
2206
3160
  });
2207
3161
  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) => {
2208
3162
  console.log();
2209
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3163
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2210
3164
  const { loadState: loadState2, saveState: saveState2 } = await Promise.resolve().then(() => (init_workspace_cli(), workspace_cli_exports));
2211
3165
  const state = loadState2();
2212
3166
  if (!sandboxIds || sandboxIds.length === 0) {
2213
3167
  const workspaces = Object.values(state.workspaces);
2214
3168
  if (workspaces.length === 0) {
2215
3169
  p4.log.info("No sandboxes to destroy.");
2216
- p4.outro(pc6.green("Done!"));
3170
+ p4.outro(pc8.green("Done!"));
2217
3171
  return;
2218
3172
  }
2219
3173
  const choices = workspaces.map((ws) => {
@@ -2221,7 +3175,7 @@ sandboxCmd.command("destroy").description("Destroy one or more sandboxes").alias
2221
3175
  (Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24)
2222
3176
  );
2223
3177
  const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
2224
- const status = age > 7 ? pc6.red("stale") : age > 1 ? pc6.yellow("idle") : pc6.green("active");
3178
+ const status = age > 7 ? pc8.red("stale") : age > 1 ? pc8.yellow("idle") : pc8.green("active");
2225
3179
  return {
2226
3180
  value: ws.sandboxId,
2227
3181
  label: `${status} ${ws.sandboxId} (${ws.provider}, last active ${ageStr})`
@@ -2249,7 +3203,7 @@ sandboxCmd.command("destroy").description("Destroy one or more sandboxes").alias
2249
3203
  for (const result of results) {
2250
3204
  if (result.status === "fulfilled") {
2251
3205
  const sandboxId = result.value;
2252
- console.log(pc6.green(`Destroyed ${sandboxId}`));
3206
+ console.log(pc8.green(`Destroyed ${sandboxId}`));
2253
3207
  const workspaceId = Object.keys(state.workspaces).find(
2254
3208
  (key) => state.workspaces[key].sandboxId === sandboxId
2255
3209
  );
@@ -2257,19 +3211,19 @@ sandboxCmd.command("destroy").description("Destroy one or more sandboxes").alias
2257
3211
  delete state.workspaces[workspaceId];
2258
3212
  }
2259
3213
  } else {
2260
- console.error(pc6.red(`Failed to destroy: ${result.reason?.message || result.reason}`));
3214
+ console.error(pc8.red(`Failed to destroy: ${result.reason?.message || result.reason}`));
2261
3215
  }
2262
3216
  }
2263
3217
  saveState2(state);
2264
3218
  });
2265
3219
  sandboxCmd.command("list").alias("ls").description("List all sandboxes").action(async () => {
2266
3220
  console.log();
2267
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3221
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2268
3222
  await sandboxList();
2269
3223
  });
2270
3224
  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) => {
2271
3225
  console.log();
2272
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3226
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2273
3227
  await ensureAuth();
2274
3228
  const provider = await resolveProvider(opts.provider);
2275
3229
  configureCompute(provider);
@@ -2280,7 +3234,7 @@ sandboxCmd.command("connect").description("Connect to a sandbox (opens REPL; sam
2280
3234
  if (workspaces.length === 0) {
2281
3235
  p4.log.info("No sandboxes found.");
2282
3236
  p4.log.info("Create one with: compute sandbox create");
2283
- p4.outro(pc6.green("Done!"));
3237
+ p4.outro(pc8.green("Done!"));
2284
3238
  return;
2285
3239
  }
2286
3240
  const choices = workspaces.map((ws) => {
@@ -2288,7 +3242,7 @@ sandboxCmd.command("connect").description("Connect to a sandbox (opens REPL; sam
2288
3242
  (Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24)
2289
3243
  );
2290
3244
  const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
2291
- const status = age > 7 ? pc6.red("stale") : age > 1 ? pc6.yellow("idle") : pc6.green("active");
3245
+ const status = age > 7 ? pc8.red("stale") : age > 1 ? pc8.yellow("idle") : pc8.green("active");
2292
3246
  return {
2293
3247
  value: ws.sandboxId,
2294
3248
  label: `${status} ${ws.sandboxId} (${ws.provider}, last active ${ageStr})`
@@ -2308,7 +3262,7 @@ sandboxCmd.command("connect").description("Connect to a sandbox (opens REPL; sam
2308
3262
  spinner4.start("Connecting...");
2309
3263
  const sandbox = await compute4.sandbox.getById(sandboxId);
2310
3264
  if (!sandbox) {
2311
- spinner4.stop(pc6.red(`Sandbox not found: ${sandboxId}`));
3265
+ spinner4.stop(pc8.red(`Sandbox not found: ${sandboxId}`));
2312
3266
  process.exit(1);
2313
3267
  }
2314
3268
  spinner4.stop("");
@@ -2320,7 +3274,7 @@ sandboxCmd.command("exec").description("Execute a command in a sandbox").argumen
2320
3274
  configureCompute(provider);
2321
3275
  const sandbox = await compute4.sandbox.getById(sandboxId);
2322
3276
  if (!sandbox) {
2323
- console.error(pc6.red(`Sandbox not found: ${sandboxId}`));
3277
+ console.error(pc8.red(`Sandbox not found: ${sandboxId}`));
2324
3278
  process.exit(1);
2325
3279
  }
2326
3280
  const cmd = buildExecCommand(command, opts);
@@ -2335,7 +3289,7 @@ sandboxCmd.command("exec").description("Execute a command in a sandbox").argumen
2335
3289
  });
2336
3290
  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) => {
2337
3291
  console.log();
2338
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3292
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2339
3293
  await ensureAuth();
2340
3294
  const provider = await resolveProvider(opts.provider);
2341
3295
  configureCompute(provider);
@@ -2346,7 +3300,7 @@ program.command("connect").description("Connect to a sandbox (opens REPL)").alia
2346
3300
  if (workspaces.length === 0) {
2347
3301
  p4.log.info("No sandboxes found.");
2348
3302
  p4.log.info("Create one with: compute sandbox create");
2349
- p4.outro(pc6.green("Done!"));
3303
+ p4.outro(pc8.green("Done!"));
2350
3304
  return;
2351
3305
  }
2352
3306
  const choices = workspaces.map((ws) => {
@@ -2354,7 +3308,7 @@ program.command("connect").description("Connect to a sandbox (opens REPL)").alia
2354
3308
  (Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24)
2355
3309
  );
2356
3310
  const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
2357
- const status = age > 7 ? pc6.red("stale") : age > 1 ? pc6.yellow("idle") : pc6.green("active");
3311
+ const status = age > 7 ? pc8.red("stale") : age > 1 ? pc8.yellow("idle") : pc8.green("active");
2358
3312
  return {
2359
3313
  value: ws.sandboxId,
2360
3314
  label: `${status} ${ws.sandboxId} (${ws.provider}, last active ${ageStr})`
@@ -2374,7 +3328,7 @@ program.command("connect").description("Connect to a sandbox (opens REPL)").alia
2374
3328
  spinner4.start("Connecting...");
2375
3329
  const sandbox = await compute4.sandbox.getById(sandboxId);
2376
3330
  if (!sandbox) {
2377
- spinner4.stop(pc6.red(`Sandbox not found: ${sandboxId}`));
3331
+ spinner4.stop(pc8.red(`Sandbox not found: ${sandboxId}`));
2378
3332
  process.exit(1);
2379
3333
  }
2380
3334
  spinner4.stop("");
@@ -2383,11 +3337,11 @@ program.command("connect").description("Connect to a sandbox (opens REPL)").alia
2383
3337
  var workspaceCmd = program.command("workspace").alias("ws").description("Manage workspaces (sandbox + git + worktree)");
2384
3338
  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) => {
2385
3339
  console.log();
2386
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3340
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2387
3341
  await ensureAuth();
2388
3342
  if (!repo) {
2389
3343
  p4.log.info("Usage: compute workspace create owner/repo --branch main");
2390
- p4.outro(pc6.green("Done!"));
3344
+ p4.outro(pc8.green("Done!"));
2391
3345
  return;
2392
3346
  }
2393
3347
  const provider = await resolveProvider(options.provider, { interactive: true });
@@ -2400,33 +3354,33 @@ workspaceCmd.command("create").description("Create a new workspace").argument("[
2400
3354
  branch: options.branch,
2401
3355
  provider
2402
3356
  });
2403
- spinner4.stop(`Workspace ready: ${pc6.cyan(workspace.id)}`);
3357
+ spinner4.stop(`Workspace ready: ${pc8.cyan(workspace.id)}`);
2404
3358
  if (options.attach !== false) {
2405
3359
  console.log();
2406
3360
  await workspaceAttach(workspace.id);
2407
3361
  } else {
2408
- p4.outro(pc6.green("Workspace created!"));
3362
+ p4.outro(pc8.green("Workspace created!"));
2409
3363
  }
2410
3364
  } catch (error) {
2411
- spinner4.stop(pc6.red("Failed to create workspace"));
3365
+ spinner4.stop(pc8.red("Failed to create workspace"));
2412
3366
  p4.log.error(error.message);
2413
3367
  process.exit(1);
2414
3368
  }
2415
3369
  });
2416
3370
  workspaceCmd.command("list").alias("ls").description("List all workspaces").action(async () => {
2417
3371
  console.log();
2418
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3372
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2419
3373
  await workspaceList();
2420
3374
  });
2421
3375
  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) => {
2422
3376
  console.log();
2423
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3377
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2424
3378
  if (!id) {
2425
3379
  const state = loadWorkspaceState();
2426
3380
  const workspaceIds = Object.keys(state.workspaces);
2427
3381
  if (workspaceIds.length === 0) {
2428
3382
  p4.log.info("No workspaces to destroy.");
2429
- p4.outro(pc6.green("Done!"));
3383
+ p4.outro(pc8.green("Done!"));
2430
3384
  return;
2431
3385
  }
2432
3386
  const choices = workspaceIds.map((key) => {
@@ -2437,7 +3391,7 @@ workspaceCmd.command("destroy").alias("rm").description("Destroy a workspace").a
2437
3391
  const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
2438
3392
  return {
2439
3393
  value: key,
2440
- label: `${pc6.cyan(key)} (${ws.provider}, last used ${ageStr})`
3394
+ label: `${pc8.cyan(key)} (${ws.provider}, last used ${ageStr})`
2441
3395
  };
2442
3396
  });
2443
3397
  const selected = await p4.multiselect({
@@ -2457,14 +3411,14 @@ workspaceCmd.command("destroy").alias("rm").description("Destroy a workspace").a
2457
3411
  });
2458
3412
  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) => {
2459
3413
  console.log();
2460
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3414
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2461
3415
  if (!id) {
2462
3416
  const state = loadWorkspaceState();
2463
3417
  const workspaceIds = Object.keys(state.workspaces);
2464
3418
  if (workspaceIds.length === 0) {
2465
3419
  p4.log.info("No workspaces found.");
2466
3420
  p4.log.info("Create one with: compute create owner/repo");
2467
- p4.outro(pc6.green("Done!"));
3421
+ p4.outro(pc8.green("Done!"));
2468
3422
  return;
2469
3423
  }
2470
3424
  const choices = workspaceIds.map((key) => {
@@ -2475,7 +3429,7 @@ workspaceCmd.command("connect").alias("attach").description("Connect to an exist
2475
3429
  const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
2476
3430
  return {
2477
3431
  value: key,
2478
- label: `${pc6.cyan(key)} (${ws.provider}, last used ${ageStr})`
3432
+ label: `${pc8.cyan(key)} (${ws.provider}, last used ${ageStr})`
2479
3433
  };
2480
3434
  });
2481
3435
  const selected = await p4.select({
@@ -2500,7 +3454,7 @@ program.command("run").description("Create a sandbox, run a command, then option
2500
3454
  buildCreateOptions(opts)
2501
3455
  );
2502
3456
  } catch (error) {
2503
- console.error(pc6.red(`Failed to create sandbox: ${error.message}`));
3457
+ console.error(pc8.red(`Failed to create sandbox: ${error.message}`));
2504
3458
  process.exit(1);
2505
3459
  }
2506
3460
  try {
@@ -2523,37 +3477,39 @@ program.command("run").description("Create a sandbox, run a command, then option
2523
3477
  } catch {
2524
3478
  }
2525
3479
  }
2526
- console.error(pc6.red(error.message));
3480
+ console.error(pc8.red(error.message));
2527
3481
  process.exit(1);
2528
3482
  }
2529
3483
  });
2530
3484
  program.command("providers").description("List all providers and their configuration status").action(async () => {
2531
3485
  console.log();
2532
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3486
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2533
3487
  const statuses = getProviderStatus();
2534
3488
  console.log();
2535
3489
  for (const status of statuses) {
2536
- const icon = status.ready ? pc6.green("\u25CF") : pc6.gray("\u25CB");
2537
- const name = status.ready ? pc6.white(status.name) : pc6.gray(status.name);
2538
- const detail = status.ready ? pc6.green("ready") : pc6.gray(`missing: ${status.missing.join(", ")}`);
3490
+ const icon = status.ready ? pc8.green("\u25CF") : pc8.gray("\u25CB");
3491
+ const name = status.ready ? pc8.white(status.name) : pc8.gray(status.name);
3492
+ const detail = status.ready ? pc8.green("ready") : pc8.gray(`missing: ${status.missing.join(", ")}`);
2539
3493
  console.log(` ${icon} ${name} ${detail}`);
2540
3494
  }
2541
3495
  console.log();
2542
3496
  });
2543
3497
  program.command("login").description("Authenticate with ComputeSDK").action(async () => {
2544
3498
  console.log();
2545
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3499
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2546
3500
  await ensureAuth({ forceLogin: true });
2547
- p4.outro(pc6.green("Authenticated!"));
3501
+ p4.outro(pc8.green("Authenticated!"));
2548
3502
  });
2549
3503
  program.command("logout").description("Clear stored credentials").action(() => {
2550
3504
  console.log();
2551
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3505
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2552
3506
  clearStoredCredentials();
2553
3507
  p4.log.success("Logged out. Stored credentials removed.");
2554
- p4.outro(pc6.green("Done!"));
3508
+ p4.outro(pc8.green("Done!"));
2555
3509
  });
2556
3510
  registerActionsCommands(program);
3511
+ registerMarketCommands(program);
3512
+ registerSandboxesCommands(program);
2557
3513
  program.command("bench").description("Benchmarks platform CLI (run, auth, org, benchmarks, runs, results, artifacts, export, ...)").allowUnknownOption().helpOption(false).action(() => {
2558
3514
  });
2559
3515
  function buildCreateOptions(opts) {
@@ -2631,7 +3587,7 @@ async function main() {
2631
3587
  await program.parseAsync(process.argv);
2632
3588
  }
2633
3589
  main().catch((error) => {
2634
- console.error(pc6.red(`
3590
+ console.error(pc8.red(`
2635
3591
  Error: ${error.message}`));
2636
3592
  process.exit(1);
2637
3593
  });