@computesdk/cli 1.0.13 → 1.0.15

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,19 +1031,22 @@ function sanitizePathPart(name) {
981
1031
  async function client(opts) {
982
1032
  return new ActionsClient(await resolveActionsAuth(opts));
983
1033
  }
984
- function assertVaultValueHost(auth) {
1034
+ function assertSecretValueHost(auth, what) {
985
1035
  if (!isTrustedActionsHost(auth.baseUrl)) {
986
1036
  throw new ActionsCliError(
987
1037
  "untrusted_host",
988
- `Refusing to send or read vault values at ${auth.baseUrl} \u2014 only computesdk.com and localhost hosts are allowed, even with --allow-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.`
989
1039
  );
990
1040
  }
991
1041
  }
992
- async function vaultValueClient(opts) {
1042
+ async function secretValueClient(opts, what) {
993
1043
  const auth = await resolveActionsAuth(opts);
994
- assertVaultValueHost(auth);
1044
+ assertSecretValueHost(auth, what);
995
1045
  return new ActionsClient(auth);
996
1046
  }
1047
+ async function vaultValueClient(opts) {
1048
+ return secretValueClient(opts, "vault values");
1049
+ }
997
1050
  function output(opts, data, render) {
998
1051
  if (opts.json) {
999
1052
  process.stdout.write(JSON.stringify(data, null, 2) + "\n");
@@ -1386,6 +1439,7 @@ function dispatchBody(workflow, opts) {
1386
1439
  if (opts.providerRegion !== void 0 && opts.provider === void 0) {
1387
1440
  throw new ActionsCliError("invalid_argument", "--provider-region requires --provider.");
1388
1441
  }
1442
+ const maxPrice = maxPriceFields(opts.maxBid, opts.maxBidPer);
1389
1443
  return {
1390
1444
  workflowId: workflow.id,
1391
1445
  ref,
@@ -1394,9 +1448,25 @@ function dispatchBody(workflow, opts) {
1394
1448
  ...opts.provider !== void 0 && {
1395
1449
  provider: opts.provider,
1396
1450
  ...opts.providerRegion !== void 0 && { providerRegion: opts.providerRegion }
1397
- }
1451
+ },
1452
+ ...maxPrice !== void 0 && maxPrice
1398
1453
  };
1399
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
+ }
1400
1470
  function parseVaultKind(kind) {
1401
1471
  if (kind === void 0 || kind === "secret" || kind === "variable") return kind ?? "secret";
1402
1472
  throw new ActionsCliError("invalid_argument", `--kind must be secret or variable, got "${kind}".`);
@@ -1437,7 +1507,7 @@ function registerActionsCommands(program2) {
1437
1507
  const actions = program2.command("actions").alias("ci").description("Drive the benchmarks-platform Actions API").configureOutput({ outputError: usageErrorOutput });
1438
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");
1439
1509
  common(
1440
- 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)")
1441
1511
  ).action(async (repo, opts) => {
1442
1512
  try {
1443
1513
  const c = await client(opts);
@@ -1921,6 +1991,863 @@ function registerActionsCommands(program2) {
1921
1991
  });
1922
1992
  }
1923
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.image !== void 0) {
2478
+ console.log(` image: ${s.image ?? "\u2014"}${s.requestedImage !== null && s.requestedImage !== s.image ? ` ${pc3.dim(`(requested: ${s.requestedImage})`)}` : ""}`);
2479
+ }
2480
+ if (s.destroyError) console.log(` destroyError: ${pc3.red(s.destroyError)}`);
2481
+ if (attach !== void 0) {
2482
+ console.log(
2483
+ attach === null ? " attach: none (market fill or ambient provider \u2014 platform drive only)" : ` attach: ${attach.provider} / ${attach.providerSandboxId}${attach.region ? ` (${attach.region})` : ""}`
2484
+ );
2485
+ }
2486
+ }
2487
+ function printProcess(p5) {
2488
+ const exit = p5.status === "exited" ? `exit=${p5.exitCode ?? "?"}${p5.signal ? ` (${p5.signal})` : ""}` : "";
2489
+ const stdin = p5.stdin && p5.status === "running" ? " [stdin open]" : "";
2490
+ console.log(`${pc3.cyan(p5.jobId)} ${p5.status}${exit ? " " + exit : ""}${stdin} ${p5.command}`);
2491
+ }
2492
+ function printSnapshot(s) {
2493
+ const state = s.deletedAt !== null ? ` ${pc3.dim(`(deleted ${s.deletedAt})`)}` : "";
2494
+ console.log(`${pc3.cyan(s.id)} ${s.providerSnapshotId}${state}`);
2495
+ console.log(
2496
+ ` provider: ${s.provider}${s.region ? ` (${s.region})` : ""} label: ${s.label ?? "\u2014"} created: ${s.createdAt}${s.expiresAt ? ` expires: ${s.expiresAt}` : ""}`
2497
+ );
2498
+ }
2499
+ function registerSandboxesCommands(program2) {
2500
+ const cmd = program2.command("sandboxes").alias("sbx").description("Create and drive sandboxes through the platform").configureOutput({ outputError: usageErrorOutput });
2501
+ 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) => {
2502
+ try {
2503
+ const c = await client(opts);
2504
+ const resources = {};
2505
+ if (opts.cpus) resources.cpus = Number(opts.cpus);
2506
+ if (opts.memoryMb) resources.memoryMb = Number(opts.memoryMb);
2507
+ if (opts.diskMb) resources.ephemeralDiskMb = Number(opts.diskMb);
2508
+ const body = {};
2509
+ if (opts.order) body.providerOrder = opts.order.split(",").map((s) => s.trim());
2510
+ if (opts.label) body.label = opts.label;
2511
+ if (opts.image) body.image = opts.image;
2512
+ if (opts.snapshotId) body.snapshotId = opts.snapshotId;
2513
+ if (opts.timeoutMs) body.timeoutMs = Number(opts.timeoutMs);
2514
+ if (opts.secret && opts.secret.length > 0) body.secrets = opts.secret;
2515
+ if (Object.keys(resources).length > 0) body.resources = resources;
2516
+ const res = await c.post("/api/v1/sandboxes", body);
2517
+ output(opts, res.sandbox, printSandbox);
2518
+ } catch (e) {
2519
+ fail(e, opts);
2520
+ }
2521
+ });
2522
+ 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) => {
2523
+ try {
2524
+ const c = await client(opts);
2525
+ const res = await c.get(
2526
+ "/api/v1/sandboxes",
2527
+ { status: opts.status, limit: opts.limit, cursor: opts.cursor }
2528
+ );
2529
+ output(opts, res, (page) => {
2530
+ page.sandboxes.forEach(printSandbox);
2531
+ if (page.nextCursor) console.log(pc3.dim(`nextCursor: ${page.nextCursor}`));
2532
+ });
2533
+ } catch (e) {
2534
+ fail(e, opts);
2535
+ }
2536
+ });
2537
+ 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) => {
2538
+ try {
2539
+ const c = await client(opts);
2540
+ const res = await c.get(`/api/v1/sandboxes/${sandboxId}`);
2541
+ output(opts, res.sandbox, printSandbox);
2542
+ } catch (e) {
2543
+ fail(e, opts);
2544
+ }
2545
+ });
2546
+ 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) => {
2547
+ try {
2548
+ const c = await client(opts);
2549
+ const res = await c.del(`/api/v1/sandboxes/${sandboxId}`);
2550
+ output(opts, res.sandbox, printSandbox);
2551
+ } catch (e) {
2552
+ fail(e, opts);
2553
+ }
2554
+ });
2555
+ 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) => {
2556
+ try {
2557
+ const c = await client(opts);
2558
+ const res = await c.post(
2559
+ `/api/v1/sandboxes/${sandboxId}/commands`,
2560
+ {
2561
+ command: command.map(shellQuote).join(" "),
2562
+ ...opts.timeoutMs ? { timeoutMs: Number(opts.timeoutMs) } : {}
2563
+ }
2564
+ );
2565
+ output(opts, res, (r) => {
2566
+ process.stdout.write(r.stdout);
2567
+ if (r.stderr) process.stderr.write(r.stderr);
2568
+ if (!r.stdout) console.log(`exit ${r.exitCode} (${r.durationMs}ms)`);
2569
+ });
2570
+ if (res.exitCode !== 0) process.exitCode = res.exitCode;
2571
+ } catch (e) {
2572
+ fail(e, opts);
2573
+ }
2574
+ });
2575
+ 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("--stdin", "keep the process's stdin pipe open for `stdin`/`close-stdin`").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, command, opts) => {
2576
+ try {
2577
+ const c = await client(opts);
2578
+ const res = await c.post(
2579
+ `/api/v1/sandboxes/${sandboxId}/processes`,
2580
+ {
2581
+ command: command.map(shellQuote).join(" "),
2582
+ ...opts.cwd ? { cwd: opts.cwd } : {},
2583
+ ...opts.env && opts.env.length > 0 ? { env: parseInputs(opts.env) } : {},
2584
+ ...opts.stdin ? { stdin: true } : {}
2585
+ }
2586
+ );
2587
+ output(opts, res.process, printProcess);
2588
+ } catch (e) {
2589
+ fail(e, opts);
2590
+ }
2591
+ });
2592
+ 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) => {
2593
+ try {
2594
+ const c = await client(opts);
2595
+ const res = await c.get(
2596
+ `/api/v1/sandboxes/${sandboxId}/processes`
2597
+ );
2598
+ output(opts, res.processes, (list) => list.forEach(printProcess));
2599
+ } catch (e) {
2600
+ fail(e, opts);
2601
+ }
2602
+ });
2603
+ 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) => {
2604
+ try {
2605
+ const c = await client(opts);
2606
+ if (opts.follow) {
2607
+ await followProcess(c, sandboxId, jobId);
2608
+ return;
2609
+ }
2610
+ const res = await c.get(
2611
+ `/api/v1/sandboxes/${sandboxId}/processes/${jobId}`
2612
+ );
2613
+ output(opts, res.process, (p5) => {
2614
+ if (p5.stdout) process.stdout.write(p5.stdout);
2615
+ if (p5.stderr) process.stderr.write(p5.stderr);
2616
+ if (!p5.stdout && !p5.stderr) printProcess(p5);
2617
+ });
2618
+ } catch (e) {
2619
+ fail(e, opts);
2620
+ }
2621
+ });
2622
+ 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) => {
2623
+ try {
2624
+ const c = await client(opts);
2625
+ const res = await c.post(
2626
+ `/api/v1/sandboxes/${sandboxId}/processes/${jobId}/wait`,
2627
+ opts.timeoutMs ? { timeoutMs: Number(opts.timeoutMs) } : {}
2628
+ );
2629
+ output(opts, res.process, printProcess);
2630
+ if (res.process.status === "exited") {
2631
+ process.exitCode = res.process.exitCode ?? 128;
2632
+ }
2633
+ } catch (e) {
2634
+ fail(e, opts);
2635
+ }
2636
+ });
2637
+ 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) => {
2638
+ try {
2639
+ const c = await client(opts);
2640
+ const res = await c.post(
2641
+ `/api/v1/sandboxes/${sandboxId}/processes/${jobId}/kill`,
2642
+ opts.signal ? { signal: opts.signal } : {}
2643
+ );
2644
+ output(opts, res.process, printProcess);
2645
+ } catch (e) {
2646
+ fail(e, opts);
2647
+ }
2648
+ });
2649
+ cmd.command("stdin <sandboxId> <jobId>").description("Write to a process's stdin pipe (spawn with --stdin; --data, --file, or piped)").option("--data <text>", "data to write").option("--file <local>", "read data from a local file").option("--base64", "base64-encode the input before sending (binary-safe)").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, jobId, opts) => {
2650
+ try {
2651
+ let data = opts.data;
2652
+ if (data === void 0 && opts.file) {
2653
+ const { readFile } = await import("fs/promises");
2654
+ data = opts.base64 ? (await readFile(opts.file)).toString("base64") : await readFile(opts.file, "utf8");
2655
+ }
2656
+ if (data === void 0 && !process.stdin.isTTY) {
2657
+ const piped = await new Promise((resolve) => {
2658
+ const chunks = [];
2659
+ process.stdin.on("data", (chunk) => chunks.push(chunk));
2660
+ process.stdin.on("end", () => resolve(Buffer.concat(chunks)));
2661
+ });
2662
+ data = opts.base64 ? piped.toString("base64") : piped.toString("utf8");
2663
+ }
2664
+ if (data === void 0 || data === "") {
2665
+ console.error(pc3.red("Pass --data, --file, or pipe stdin"));
2666
+ process.exit(1);
2667
+ }
2668
+ if (opts.base64 && opts.data !== void 0) {
2669
+ data = Buffer.from(data).toString("base64");
2670
+ }
2671
+ const c = await client(opts);
2672
+ const res = await c.post(
2673
+ `/api/v1/sandboxes/${sandboxId}/processes/${jobId}/stdin`,
2674
+ { data, ...opts.base64 ? { encoding: "base64" } : {} }
2675
+ );
2676
+ output(opts, res, () => console.log(`wrote ${data.length} chars to ${jobId}`));
2677
+ } catch (e) {
2678
+ fail(e, opts);
2679
+ }
2680
+ });
2681
+ cmd.command("close-stdin <sandboxId> <jobId>").description("Close a process's stdin pipe").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, jobId, opts) => {
2682
+ try {
2683
+ const c = await client(opts);
2684
+ const res = await c.post(
2685
+ `/api/v1/sandboxes/${sandboxId}/processes/${jobId}/close-stdin`,
2686
+ {}
2687
+ );
2688
+ output(opts, res, () => console.log(`closed stdin on ${jobId}`));
2689
+ } catch (e) {
2690
+ fail(e, opts);
2691
+ }
2692
+ });
2693
+ cmd.command("ls <sandboxId> [path]").description("List a directory (or stat a path) inside the sandbox").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, path5, opts) => {
2694
+ try {
2695
+ const c = await client(opts);
2696
+ const res = await c.get(`/api/v1/sandboxes/${sandboxId}/files`, {
2697
+ path: path5 ?? "/"
2698
+ });
2699
+ output(opts, res, (view) => {
2700
+ if (view.type === "directory") {
2701
+ for (const e of view.entries) {
2702
+ console.log(`${e.type === "directory" ? pc3.blue("d") : " "} ${e.name}${e.size !== void 0 ? ` ${pc3.dim(String(e.size))}` : ""}`);
2703
+ }
2704
+ } else {
2705
+ console.log(`${view.path} file ${view.content.length} chars`);
2706
+ }
2707
+ });
2708
+ } catch (e) {
2709
+ fail(e, opts);
2710
+ }
2711
+ });
2712
+ cmd.command("cat <sandboxId> <path>").description("Print a file's contents").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, path5, opts) => {
2713
+ try {
2714
+ const c = await client(opts);
2715
+ const res = await c.get(`/api/v1/sandboxes/${sandboxId}/files`, { path: path5 });
2716
+ output(opts, res, (view) => {
2717
+ if (view.type !== "file") {
2718
+ console.error(pc3.red(`${view.path} is a directory`));
2719
+ process.exit(1);
2720
+ }
2721
+ process.stdout.write(view.content);
2722
+ if (!view.content.endsWith("\n")) process.stdout.write("\n");
2723
+ });
2724
+ } catch (e) {
2725
+ fail(e, opts);
2726
+ }
2727
+ });
2728
+ cmd.command("write <sandboxId> <path>").description("Write a file inside the sandbox (--content or --file <local>, else stdin)").option("--content <text>", "file contents").option("--file <local>", "read contents from a local file").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, path5, opts) => {
2729
+ try {
2730
+ let content = opts.content;
2731
+ if (content === void 0 && opts.file) {
2732
+ const { readFile } = await import("fs/promises");
2733
+ content = await readFile(opts.file, "utf8");
2734
+ }
2735
+ if (content === void 0 && !process.stdin.isTTY) {
2736
+ content = await new Promise((resolve) => {
2737
+ let data = "";
2738
+ process.stdin.setEncoding("utf8");
2739
+ process.stdin.on("data", (chunk) => data += chunk);
2740
+ process.stdin.on("end", () => resolve(data));
2741
+ });
2742
+ }
2743
+ if (content === void 0) {
2744
+ console.error(pc3.red("Pass --content, --file, or pipe stdin"));
2745
+ process.exit(1);
2746
+ }
2747
+ const c = await client(opts);
2748
+ const res = await c.post(
2749
+ `/api/v1/sandboxes/${sandboxId}/files`,
2750
+ { path: path5, content }
2751
+ );
2752
+ output(opts, res, (r) => console.log(`wrote ${r.path}`));
2753
+ } catch (e) {
2754
+ fail(e, opts);
2755
+ }
2756
+ });
2757
+ cmd.command("mkdir <sandboxId> <path>").description("Create a directory (parents included)").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, path5, opts) => {
2758
+ try {
2759
+ const c = await client(opts);
2760
+ const res = await c.post(`/api/v1/sandboxes/${sandboxId}/files`, {
2761
+ path: path5,
2762
+ mkdir: true
2763
+ });
2764
+ output(opts, res, (r) => console.log(`created ${r.path}`));
2765
+ } catch (e) {
2766
+ fail(e, opts);
2767
+ }
2768
+ });
2769
+ cmd.command("rm <sandboxId> <path>").description("Remove a file or directory inside the sandbox").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, path5, opts) => {
2770
+ try {
2771
+ const c = await client(opts);
2772
+ const res = await c.del(
2773
+ `/api/v1/sandboxes/${sandboxId}/files?path=${encodeURIComponent(path5)}`
2774
+ );
2775
+ output(opts, res, (r) => console.log(`removed ${r.path}`));
2776
+ } catch (e) {
2777
+ fail(e, opts);
2778
+ }
2779
+ });
2780
+ 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) => {
2781
+ try {
2782
+ const c = await client(opts);
2783
+ const res = await c.get(`/api/v1/sandboxes/${sandboxId}/urls`, {
2784
+ port: opts.port,
2785
+ protocol: opts.protocol
2786
+ });
2787
+ output(opts, res, (r) => console.log(safeTerm(r.url)));
2788
+ } catch (e) {
2789
+ fail(e, opts);
2790
+ }
2791
+ });
2792
+ cmd.command("snapshots <sandboxId>").description("List the sandbox's snapshots").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, opts) => {
2793
+ try {
2794
+ const c = await client(opts);
2795
+ const res = await c.get(
2796
+ `/api/v1/sandboxes/${sandboxId}/snapshots`
2797
+ );
2798
+ output(opts, res.snapshots, (list) => list.forEach(printSnapshot));
2799
+ } catch (e) {
2800
+ fail(e, opts);
2801
+ }
2802
+ });
2803
+ cmd.command("snapshot <sandboxId>").description("Snapshot a running sandbox through its provider").option("--label <label>", "label for the snapshot").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, opts) => {
2804
+ try {
2805
+ const c = await client(opts);
2806
+ const res = await c.post(
2807
+ `/api/v1/sandboxes/${sandboxId}/snapshots`,
2808
+ opts.label ? { label: opts.label } : {}
2809
+ );
2810
+ output(opts, res.snapshot, printSnapshot);
2811
+ } catch (e) {
2812
+ fail(e, opts);
2813
+ }
2814
+ });
2815
+ cmd.command("snapshot-delete <sandboxId> <snapshotId>").description("Delete a snapshot's provider artifact").option("--api-key <key>").option("--base-url <url>").option("--allow-untrusted-host").option("--json", "print the raw response").action(async (sandboxId, snapshotId, opts) => {
2816
+ try {
2817
+ const c = await client(opts);
2818
+ const res = await c.del(
2819
+ `/api/v1/sandboxes/${sandboxId}/snapshots/${snapshotId}`
2820
+ );
2821
+ output(opts, res, () => console.log(`deleted snapshot ${snapshotId}`));
2822
+ } catch (e) {
2823
+ fail(e, opts);
2824
+ }
2825
+ });
2826
+ }
2827
+ async function followProcess(c, sandboxId, jobId) {
2828
+ let exitCode = 0;
2829
+ for await (const { event, data } of c.sseNamed(
2830
+ `/api/v1/sandboxes/${sandboxId}/processes/${jobId}`
2831
+ )) {
2832
+ const payload = data;
2833
+ if (event === "stdout") process.stdout.write(String(payload.chunk ?? ""));
2834
+ else if (event === "stderr") process.stderr.write(String(payload.chunk ?? ""));
2835
+ else if (event === "exit") {
2836
+ exitCode = typeof payload.exitCode === "number" ? payload.exitCode : 128;
2837
+ return finish();
2838
+ } else if (event === "error") {
2839
+ console.error(pc3.red(`Error: ${String(payload.error ?? "stream error")}`));
2840
+ return finish(1);
2841
+ }
2842
+ }
2843
+ console.error(pc3.yellow("stream ended before the process exited"));
2844
+ return finish(2);
2845
+ function finish(code) {
2846
+ if (code !== void 0) exitCode = code;
2847
+ if (exitCode !== 0) process.exitCode = exitCode;
2848
+ }
2849
+ }
2850
+
1924
2851
  // src/index.ts
1925
2852
  init_repl();
1926
2853
 
@@ -1933,7 +2860,7 @@ import * as fs2 from "fs";
1933
2860
  import * as path3 from "path";
1934
2861
  import * as os2 from "os";
1935
2862
  import * as p from "@clack/prompts";
1936
- import pc3 from "picocolors";
2863
+ import pc5 from "picocolors";
1937
2864
  function getCredentialsDir() {
1938
2865
  return path3.join(os2.homedir(), ".computesdk");
1939
2866
  }
@@ -1991,7 +2918,7 @@ function openBrowser(url) {
1991
2918
  if (err) {
1992
2919
  p.log.warn("Could not open browser automatically.");
1993
2920
  p.log.info(`Open this URL manually:
1994
- ${pc3.cyan(url)}`);
2921
+ ${pc5.cyan(url)}`);
1995
2922
  }
1996
2923
  });
1997
2924
  }
@@ -2062,7 +2989,7 @@ async function resolveApiKey(options) {
2062
2989
  const stored = loadStoredCredentials();
2063
2990
  if (stored) {
2064
2991
  p.log.info(
2065
- `Using stored credentials from ${pc3.dim("~/.computesdk/credentials.json")}`
2992
+ `Using stored credentials from ${pc5.dim("~/.computesdk/credentials.json")}`
2066
2993
  );
2067
2994
  return stored;
2068
2995
  }
@@ -2076,14 +3003,14 @@ async function resolveApiKey(options) {
2076
3003
  try {
2077
3004
  storeCredentials(apiKey);
2078
3005
  p.log.success(
2079
- `Credentials saved to ${pc3.dim("~/.computesdk/credentials.json")}`
3006
+ `Credentials saved to ${pc5.dim("~/.computesdk/credentials.json")}`
2080
3007
  );
2081
3008
  } catch {
2082
3009
  p.log.warn("Could not save credentials to disk. You may need to authenticate again next time.");
2083
3010
  }
2084
3011
  return apiKey;
2085
3012
  } catch (error) {
2086
- spinner4.stop(pc3.red("Authentication failed"));
3013
+ spinner4.stop(pc5.red("Authentication failed"));
2087
3014
  throw error;
2088
3015
  }
2089
3016
  }
@@ -2091,7 +3018,7 @@ async function resolveApiKey(options) {
2091
3018
  // src/setup.ts
2092
3019
  init_esm_shims();
2093
3020
  import * as p2 from "@clack/prompts";
2094
- import pc4 from "picocolors";
3021
+ import pc6 from "picocolors";
2095
3022
  import { compute as compute2 } from "computesdk";
2096
3023
  async function ensureAuth(options) {
2097
3024
  try {
@@ -2099,8 +3026,8 @@ async function ensureAuth(options) {
2099
3026
  process.env.COMPUTESDK_API_KEY = apiKey;
2100
3027
  } catch (error) {
2101
3028
  p2.log.error(error.message);
2102
- p2.log.info(`Get your API key at ${pc4.cyan("https://console.computesdk.com")}`);
2103
- p2.outro(pc4.red("Setup incomplete"));
3029
+ p2.log.info(`Get your API key at ${pc6.cyan("https://console.computesdk.com")}`);
3030
+ p2.outro(pc6.red("Setup incomplete"));
2104
3031
  process.exit(1);
2105
3032
  }
2106
3033
  }
@@ -2129,7 +3056,7 @@ async function resolveProvider(providerFlag, options) {
2129
3056
  if (available.length === 0) {
2130
3057
  p2.log.error("No provider credentials detected");
2131
3058
  p2.log.info("Set one of: E2B_API_KEY, MODAL_TOKEN_ID, RAILWAY_API_KEY, etc.");
2132
- p2.outro(pc4.red("Setup incomplete"));
3059
+ p2.outro(pc6.red("Setup incomplete"));
2133
3060
  process.exit(1);
2134
3061
  }
2135
3062
  if (available.length === 1) {
@@ -2173,24 +3100,24 @@ program.name("compute").description("Cloud-native workspace management").version
2173
3100
  program.option("--login", "force re-authentication").option("--logout", "clear stored credentials").action(async (opts) => {
2174
3101
  if (opts.logout) {
2175
3102
  console.log();
2176
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3103
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2177
3104
  clearStoredCredentials();
2178
3105
  p4.log.success("Logged out. Stored credentials removed.");
2179
- p4.outro(pc6.green("Done!"));
3106
+ p4.outro(pc8.green("Done!"));
2180
3107
  process.exit(0);
2181
3108
  }
2182
3109
  if (opts.login) {
2183
3110
  console.log();
2184
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3111
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2185
3112
  await ensureAuth({ forceLogin: true });
2186
- p4.outro(pc6.green("Authenticated!"));
3113
+ p4.outro(pc8.green("Authenticated!"));
2187
3114
  process.exit(0);
2188
3115
  }
2189
3116
  program.help();
2190
3117
  });
2191
3118
  program.command("create").description("Create a new workspace (same as create-compute)").argument("[repo]", "Repository in owner/repo format").option("-b, --branch <branch>", "Branch name", "main").option("-p, --provider <provider>", "provider to use").option("--no-attach", "Create workspace but do not attach").action(async (repo, options) => {
2192
3119
  console.log();
2193
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3120
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2194
3121
  await ensureAuth();
2195
3122
  if (!repo) {
2196
3123
  const state = loadWorkspaceState();
@@ -2211,7 +3138,7 @@ program.command("create").description("Create a new workspace (same as create-co
2211
3138
  message: "Select workspace to attach (or create new)",
2212
3139
  options: [
2213
3140
  ...choices,
2214
- { value: "__new__", label: pc6.gray("+ Create new workspace") }
3141
+ { value: "__new__", label: pc8.gray("+ Create new workspace") }
2215
3142
  ]
2216
3143
  });
2217
3144
  if (p4.isCancel(selected)) {
@@ -2252,15 +3179,15 @@ program.command("create").description("Create a new workspace (same as create-co
2252
3179
  branch: options.branch,
2253
3180
  provider
2254
3181
  });
2255
- spinner4.stop(`Workspace ready: ${pc6.cyan(workspace.id)}`);
3182
+ spinner4.stop(`Workspace ready: ${pc8.cyan(workspace.id)}`);
2256
3183
  if (options.attach !== false) {
2257
3184
  console.log();
2258
3185
  await workspaceAttach(workspace.id);
2259
3186
  } else {
2260
- p4.outro(pc6.green("Workspace created!"));
3187
+ p4.outro(pc8.green("Workspace created!"));
2261
3188
  }
2262
3189
  } catch (error) {
2263
- spinner4.stop(pc6.red("Failed to create workspace"));
3190
+ spinner4.stop(pc8.red("Failed to create workspace"));
2264
3191
  p4.log.error(error.message);
2265
3192
  process.exit(1);
2266
3193
  }
@@ -2269,7 +3196,7 @@ var sandboxCmd = program.command("sandbox").alias("sb").description("Manage raw
2269
3196
  sandboxCmd.command("create").description("Create a new sandbox").option("-p, --provider <provider>", "provider to use").option("--timeout <duration>", "sandbox timeout (e.g. 5m, 1h)").option("--name <name>", "sandbox name").option("--connect", "drop into the REPL after creation").option("--silent", "only output the sandbox ID").action(async (opts) => {
2270
3197
  if (!opts.silent) {
2271
3198
  console.log();
2272
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3199
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2273
3200
  }
2274
3201
  await ensureAuth();
2275
3202
  const provider = await resolveProvider(opts.provider);
@@ -2281,7 +3208,7 @@ sandboxCmd.command("create").description("Create a new sandbox").option("-p, --p
2281
3208
  const sandbox = await compute4.sandbox.create(
2282
3209
  buildCreateOptions(opts)
2283
3210
  );
2284
- spinner4.stop(`Sandbox ready: ${pc6.cyan(sandbox.sandboxId)}`);
3211
+ spinner4.stop(`Sandbox ready: ${pc8.cyan(sandbox.sandboxId)}`);
2285
3212
  const { saveSandbox: saveSandbox2 } = await Promise.resolve().then(() => (init_workspace_cli(), workspace_cli_exports));
2286
3213
  await saveSandbox2({
2287
3214
  id: sandbox.sandboxId,
@@ -2294,7 +3221,7 @@ sandboxCmd.command("create").description("Create a new sandbox").option("-p, --p
2294
3221
  await startREPL(sandbox, provider);
2295
3222
  }
2296
3223
  } catch (error) {
2297
- spinner4.stop(pc6.red("Failed to create sandbox"));
3224
+ spinner4.stop(pc8.red("Failed to create sandbox"));
2298
3225
  p4.log.error(error.message);
2299
3226
  process.exit(1);
2300
3227
  }
@@ -2323,14 +3250,14 @@ sandboxCmd.command("create").description("Create a new sandbox").option("-p, --p
2323
3250
  });
2324
3251
  sandboxCmd.command("destroy").description("Destroy one or more sandboxes").aliases(["rm", "stop"]).argument("[sandbox_id...]", "sandbox IDs to destroy (optional - will show picker if not provided)").option("-p, --provider <provider>", "provider to use").action(async (sandboxIds, opts) => {
2325
3252
  console.log();
2326
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3253
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2327
3254
  const { loadState: loadState2, saveState: saveState2 } = await Promise.resolve().then(() => (init_workspace_cli(), workspace_cli_exports));
2328
3255
  const state = loadState2();
2329
3256
  if (!sandboxIds || sandboxIds.length === 0) {
2330
3257
  const workspaces = Object.values(state.workspaces);
2331
3258
  if (workspaces.length === 0) {
2332
3259
  p4.log.info("No sandboxes to destroy.");
2333
- p4.outro(pc6.green("Done!"));
3260
+ p4.outro(pc8.green("Done!"));
2334
3261
  return;
2335
3262
  }
2336
3263
  const choices = workspaces.map((ws) => {
@@ -2338,7 +3265,7 @@ sandboxCmd.command("destroy").description("Destroy one or more sandboxes").alias
2338
3265
  (Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24)
2339
3266
  );
2340
3267
  const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
2341
- const status = age > 7 ? pc6.red("stale") : age > 1 ? pc6.yellow("idle") : pc6.green("active");
3268
+ const status = age > 7 ? pc8.red("stale") : age > 1 ? pc8.yellow("idle") : pc8.green("active");
2342
3269
  return {
2343
3270
  value: ws.sandboxId,
2344
3271
  label: `${status} ${ws.sandboxId} (${ws.provider}, last active ${ageStr})`
@@ -2366,7 +3293,7 @@ sandboxCmd.command("destroy").description("Destroy one or more sandboxes").alias
2366
3293
  for (const result of results) {
2367
3294
  if (result.status === "fulfilled") {
2368
3295
  const sandboxId = result.value;
2369
- console.log(pc6.green(`Destroyed ${sandboxId}`));
3296
+ console.log(pc8.green(`Destroyed ${sandboxId}`));
2370
3297
  const workspaceId = Object.keys(state.workspaces).find(
2371
3298
  (key) => state.workspaces[key].sandboxId === sandboxId
2372
3299
  );
@@ -2374,19 +3301,19 @@ sandboxCmd.command("destroy").description("Destroy one or more sandboxes").alias
2374
3301
  delete state.workspaces[workspaceId];
2375
3302
  }
2376
3303
  } else {
2377
- console.error(pc6.red(`Failed to destroy: ${result.reason?.message || result.reason}`));
3304
+ console.error(pc8.red(`Failed to destroy: ${result.reason?.message || result.reason}`));
2378
3305
  }
2379
3306
  }
2380
3307
  saveState2(state);
2381
3308
  });
2382
3309
  sandboxCmd.command("list").alias("ls").description("List all sandboxes").action(async () => {
2383
3310
  console.log();
2384
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3311
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2385
3312
  await sandboxList();
2386
3313
  });
2387
3314
  sandboxCmd.command("connect").description("Connect to a sandbox (opens REPL; same as compute connect)").aliases(["ssh", "shell"]).argument("[sandbox_id]", "sandbox ID (optional - will show picker if not provided)").option("-p, --provider <provider>", "provider to use").action(async (sandboxId, opts) => {
2388
3315
  console.log();
2389
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3316
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2390
3317
  await ensureAuth();
2391
3318
  const provider = await resolveProvider(opts.provider);
2392
3319
  configureCompute(provider);
@@ -2397,7 +3324,7 @@ sandboxCmd.command("connect").description("Connect to a sandbox (opens REPL; sam
2397
3324
  if (workspaces.length === 0) {
2398
3325
  p4.log.info("No sandboxes found.");
2399
3326
  p4.log.info("Create one with: compute sandbox create");
2400
- p4.outro(pc6.green("Done!"));
3327
+ p4.outro(pc8.green("Done!"));
2401
3328
  return;
2402
3329
  }
2403
3330
  const choices = workspaces.map((ws) => {
@@ -2405,7 +3332,7 @@ sandboxCmd.command("connect").description("Connect to a sandbox (opens REPL; sam
2405
3332
  (Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24)
2406
3333
  );
2407
3334
  const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
2408
- const status = age > 7 ? pc6.red("stale") : age > 1 ? pc6.yellow("idle") : pc6.green("active");
3335
+ const status = age > 7 ? pc8.red("stale") : age > 1 ? pc8.yellow("idle") : pc8.green("active");
2409
3336
  return {
2410
3337
  value: ws.sandboxId,
2411
3338
  label: `${status} ${ws.sandboxId} (${ws.provider}, last active ${ageStr})`
@@ -2425,7 +3352,7 @@ sandboxCmd.command("connect").description("Connect to a sandbox (opens REPL; sam
2425
3352
  spinner4.start("Connecting...");
2426
3353
  const sandbox = await compute4.sandbox.getById(sandboxId);
2427
3354
  if (!sandbox) {
2428
- spinner4.stop(pc6.red(`Sandbox not found: ${sandboxId}`));
3355
+ spinner4.stop(pc8.red(`Sandbox not found: ${sandboxId}`));
2429
3356
  process.exit(1);
2430
3357
  }
2431
3358
  spinner4.stop("");
@@ -2437,7 +3364,7 @@ sandboxCmd.command("exec").description("Execute a command in a sandbox").argumen
2437
3364
  configureCompute(provider);
2438
3365
  const sandbox = await compute4.sandbox.getById(sandboxId);
2439
3366
  if (!sandbox) {
2440
- console.error(pc6.red(`Sandbox not found: ${sandboxId}`));
3367
+ console.error(pc8.red(`Sandbox not found: ${sandboxId}`));
2441
3368
  process.exit(1);
2442
3369
  }
2443
3370
  const cmd = buildExecCommand(command, opts);
@@ -2452,7 +3379,7 @@ sandboxCmd.command("exec").description("Execute a command in a sandbox").argumen
2452
3379
  });
2453
3380
  program.command("connect").description("Connect to a sandbox (opens REPL)").aliases(["ssh", "shell"]).argument("[sandbox_id]", "sandbox ID (optional - will show picker if not provided)").option("-p, --provider <provider>", "provider to use").action(async (sandboxId, opts) => {
2454
3381
  console.log();
2455
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3382
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2456
3383
  await ensureAuth();
2457
3384
  const provider = await resolveProvider(opts.provider);
2458
3385
  configureCompute(provider);
@@ -2463,7 +3390,7 @@ program.command("connect").description("Connect to a sandbox (opens REPL)").alia
2463
3390
  if (workspaces.length === 0) {
2464
3391
  p4.log.info("No sandboxes found.");
2465
3392
  p4.log.info("Create one with: compute sandbox create");
2466
- p4.outro(pc6.green("Done!"));
3393
+ p4.outro(pc8.green("Done!"));
2467
3394
  return;
2468
3395
  }
2469
3396
  const choices = workspaces.map((ws) => {
@@ -2471,7 +3398,7 @@ program.command("connect").description("Connect to a sandbox (opens REPL)").alia
2471
3398
  (Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24)
2472
3399
  );
2473
3400
  const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
2474
- const status = age > 7 ? pc6.red("stale") : age > 1 ? pc6.yellow("idle") : pc6.green("active");
3401
+ const status = age > 7 ? pc8.red("stale") : age > 1 ? pc8.yellow("idle") : pc8.green("active");
2475
3402
  return {
2476
3403
  value: ws.sandboxId,
2477
3404
  label: `${status} ${ws.sandboxId} (${ws.provider}, last active ${ageStr})`
@@ -2491,7 +3418,7 @@ program.command("connect").description("Connect to a sandbox (opens REPL)").alia
2491
3418
  spinner4.start("Connecting...");
2492
3419
  const sandbox = await compute4.sandbox.getById(sandboxId);
2493
3420
  if (!sandbox) {
2494
- spinner4.stop(pc6.red(`Sandbox not found: ${sandboxId}`));
3421
+ spinner4.stop(pc8.red(`Sandbox not found: ${sandboxId}`));
2495
3422
  process.exit(1);
2496
3423
  }
2497
3424
  spinner4.stop("");
@@ -2500,11 +3427,11 @@ program.command("connect").description("Connect to a sandbox (opens REPL)").alia
2500
3427
  var workspaceCmd = program.command("workspace").alias("ws").description("Manage workspaces (sandbox + git + worktree)");
2501
3428
  workspaceCmd.command("create").description("Create a new workspace").argument("[repo]", "Repository in owner/repo format").option("-b, --branch <branch>", "Branch name", "main").option("-p, --provider <provider>", "provider to use").option("--no-attach", "Create workspace but do not attach").action(async (repo, options) => {
2502
3429
  console.log();
2503
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3430
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2504
3431
  await ensureAuth();
2505
3432
  if (!repo) {
2506
3433
  p4.log.info("Usage: compute workspace create owner/repo --branch main");
2507
- p4.outro(pc6.green("Done!"));
3434
+ p4.outro(pc8.green("Done!"));
2508
3435
  return;
2509
3436
  }
2510
3437
  const provider = await resolveProvider(options.provider, { interactive: true });
@@ -2517,33 +3444,33 @@ workspaceCmd.command("create").description("Create a new workspace").argument("[
2517
3444
  branch: options.branch,
2518
3445
  provider
2519
3446
  });
2520
- spinner4.stop(`Workspace ready: ${pc6.cyan(workspace.id)}`);
3447
+ spinner4.stop(`Workspace ready: ${pc8.cyan(workspace.id)}`);
2521
3448
  if (options.attach !== false) {
2522
3449
  console.log();
2523
3450
  await workspaceAttach(workspace.id);
2524
3451
  } else {
2525
- p4.outro(pc6.green("Workspace created!"));
3452
+ p4.outro(pc8.green("Workspace created!"));
2526
3453
  }
2527
3454
  } catch (error) {
2528
- spinner4.stop(pc6.red("Failed to create workspace"));
3455
+ spinner4.stop(pc8.red("Failed to create workspace"));
2529
3456
  p4.log.error(error.message);
2530
3457
  process.exit(1);
2531
3458
  }
2532
3459
  });
2533
3460
  workspaceCmd.command("list").alias("ls").description("List all workspaces").action(async () => {
2534
3461
  console.log();
2535
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3462
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2536
3463
  await workspaceList();
2537
3464
  });
2538
3465
  workspaceCmd.command("destroy").alias("rm").description("Destroy a workspace").argument("[id]", "Workspace ID (repo:branch format, optional - will show picker if not provided)").action(async (id) => {
2539
3466
  console.log();
2540
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3467
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2541
3468
  if (!id) {
2542
3469
  const state = loadWorkspaceState();
2543
3470
  const workspaceIds = Object.keys(state.workspaces);
2544
3471
  if (workspaceIds.length === 0) {
2545
3472
  p4.log.info("No workspaces to destroy.");
2546
- p4.outro(pc6.green("Done!"));
3473
+ p4.outro(pc8.green("Done!"));
2547
3474
  return;
2548
3475
  }
2549
3476
  const choices = workspaceIds.map((key) => {
@@ -2554,7 +3481,7 @@ workspaceCmd.command("destroy").alias("rm").description("Destroy a workspace").a
2554
3481
  const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
2555
3482
  return {
2556
3483
  value: key,
2557
- label: `${pc6.cyan(key)} (${ws.provider}, last used ${ageStr})`
3484
+ label: `${pc8.cyan(key)} (${ws.provider}, last used ${ageStr})`
2558
3485
  };
2559
3486
  });
2560
3487
  const selected = await p4.multiselect({
@@ -2574,14 +3501,14 @@ workspaceCmd.command("destroy").alias("rm").description("Destroy a workspace").a
2574
3501
  });
2575
3502
  workspaceCmd.command("connect").alias("attach").description("Connect to an existing workspace (with git context)").argument("[id]", "Workspace ID (optional - will show picker if not provided)").action(async (id) => {
2576
3503
  console.log();
2577
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3504
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2578
3505
  if (!id) {
2579
3506
  const state = loadWorkspaceState();
2580
3507
  const workspaceIds = Object.keys(state.workspaces);
2581
3508
  if (workspaceIds.length === 0) {
2582
3509
  p4.log.info("No workspaces found.");
2583
3510
  p4.log.info("Create one with: compute create owner/repo");
2584
- p4.outro(pc6.green("Done!"));
3511
+ p4.outro(pc8.green("Done!"));
2585
3512
  return;
2586
3513
  }
2587
3514
  const choices = workspaceIds.map((key) => {
@@ -2592,7 +3519,7 @@ workspaceCmd.command("connect").alias("attach").description("Connect to an exist
2592
3519
  const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
2593
3520
  return {
2594
3521
  value: key,
2595
- label: `${pc6.cyan(key)} (${ws.provider}, last used ${ageStr})`
3522
+ label: `${pc8.cyan(key)} (${ws.provider}, last used ${ageStr})`
2596
3523
  };
2597
3524
  });
2598
3525
  const selected = await p4.select({
@@ -2617,7 +3544,7 @@ program.command("run").description("Create a sandbox, run a command, then option
2617
3544
  buildCreateOptions(opts)
2618
3545
  );
2619
3546
  } catch (error) {
2620
- console.error(pc6.red(`Failed to create sandbox: ${error.message}`));
3547
+ console.error(pc8.red(`Failed to create sandbox: ${error.message}`));
2621
3548
  process.exit(1);
2622
3549
  }
2623
3550
  try {
@@ -2640,37 +3567,39 @@ program.command("run").description("Create a sandbox, run a command, then option
2640
3567
  } catch {
2641
3568
  }
2642
3569
  }
2643
- console.error(pc6.red(error.message));
3570
+ console.error(pc8.red(error.message));
2644
3571
  process.exit(1);
2645
3572
  }
2646
3573
  });
2647
3574
  program.command("providers").description("List all providers and their configuration status").action(async () => {
2648
3575
  console.log();
2649
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3576
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2650
3577
  const statuses = getProviderStatus();
2651
3578
  console.log();
2652
3579
  for (const status of statuses) {
2653
- const icon = status.ready ? pc6.green("\u25CF") : pc6.gray("\u25CB");
2654
- const name = status.ready ? pc6.white(status.name) : pc6.gray(status.name);
2655
- const detail = status.ready ? pc6.green("ready") : pc6.gray(`missing: ${status.missing.join(", ")}`);
3580
+ const icon = status.ready ? pc8.green("\u25CF") : pc8.gray("\u25CB");
3581
+ const name = status.ready ? pc8.white(status.name) : pc8.gray(status.name);
3582
+ const detail = status.ready ? pc8.green("ready") : pc8.gray(`missing: ${status.missing.join(", ")}`);
2656
3583
  console.log(` ${icon} ${name} ${detail}`);
2657
3584
  }
2658
3585
  console.log();
2659
3586
  });
2660
3587
  program.command("login").description("Authenticate with ComputeSDK").action(async () => {
2661
3588
  console.log();
2662
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3589
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2663
3590
  await ensureAuth({ forceLogin: true });
2664
- p4.outro(pc6.green("Authenticated!"));
3591
+ p4.outro(pc8.green("Authenticated!"));
2665
3592
  });
2666
3593
  program.command("logout").description("Clear stored credentials").action(() => {
2667
3594
  console.log();
2668
- p4.intro(pc6.cyan(`@computesdk/cli v${VERSION}`));
3595
+ p4.intro(pc8.cyan(`@computesdk/cli v${VERSION}`));
2669
3596
  clearStoredCredentials();
2670
3597
  p4.log.success("Logged out. Stored credentials removed.");
2671
- p4.outro(pc6.green("Done!"));
3598
+ p4.outro(pc8.green("Done!"));
2672
3599
  });
2673
3600
  registerActionsCommands(program);
3601
+ registerMarketCommands(program);
3602
+ registerSandboxesCommands(program);
2674
3603
  program.command("bench").description("Benchmarks platform CLI (run, auth, org, benchmarks, runs, results, artifacts, export, ...)").allowUnknownOption().helpOption(false).action(() => {
2675
3604
  });
2676
3605
  function buildCreateOptions(opts) {
@@ -2748,7 +3677,7 @@ async function main() {
2748
3677
  await program.parseAsync(process.argv);
2749
3678
  }
2750
3679
  main().catch((error) => {
2751
- console.error(pc6.red(`
3680
+ console.error(pc8.red(`
2752
3681
  Error: ${error.message}`));
2753
3682
  process.exit(1);
2754
3683
  });