@computesdk/cli 0.1.2 → 1.0.1
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.d.ts +1 -1
- package/dist/index.js +423 -216
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -18,95 +18,310 @@ var init_esm_shims = __esm({
|
|
|
18
18
|
}
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
// src/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
21
|
+
// src/repl.ts
|
|
22
|
+
import * as readline from "readline";
|
|
23
|
+
import * as path2 from "path";
|
|
24
|
+
import * as os from "os";
|
|
25
|
+
import * as fs from "fs";
|
|
26
|
+
import pc from "picocolors";
|
|
27
|
+
import { compute } from "computesdk";
|
|
28
|
+
async function startREPL(sandbox, provider) {
|
|
29
|
+
void provider;
|
|
30
|
+
const ctx = {
|
|
31
|
+
sandboxes: /* @__PURE__ */ new Map([[sandbox.sandboxId, sandbox]]),
|
|
32
|
+
activeSandboxId: sandbox.sandboxId
|
|
33
|
+
};
|
|
34
|
+
await runSession(ctx);
|
|
35
|
+
}
|
|
36
|
+
function buildPrompt(sandboxId) {
|
|
37
|
+
if (!sandboxId) return pc.gray("[no sandbox] ") + pc.bold("$ ");
|
|
38
|
+
return pc.cyan(`[${sandboxId}] `) + pc.bold("$ ");
|
|
39
|
+
}
|
|
40
|
+
function runSession(ctx) {
|
|
41
|
+
const rl = readline.createInterface({
|
|
42
|
+
input: process.stdin,
|
|
43
|
+
output: process.stdout,
|
|
44
|
+
prompt: buildPrompt(ctx.activeSandboxId),
|
|
45
|
+
terminal: true,
|
|
46
|
+
historySize: 1e3
|
|
47
|
+
});
|
|
48
|
+
loadHistory(rl);
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
let resolved = false;
|
|
51
|
+
const done = (result) => {
|
|
52
|
+
if (resolved) return;
|
|
53
|
+
resolved = true;
|
|
54
|
+
rl.close();
|
|
55
|
+
resolve(result);
|
|
56
|
+
};
|
|
57
|
+
rl.on("SIGINT", () => {
|
|
58
|
+
console.log();
|
|
59
|
+
rl.prompt();
|
|
60
|
+
});
|
|
61
|
+
rl.on("close", () => {
|
|
62
|
+
done("exit");
|
|
63
|
+
});
|
|
64
|
+
rl.on("line", async (line) => {
|
|
65
|
+
const trimmed = line.trim();
|
|
66
|
+
if (!trimmed) {
|
|
67
|
+
rl.prompt();
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (trimmed === ".exit") {
|
|
71
|
+
done("exit");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
saveToHistory(trimmed);
|
|
75
|
+
try {
|
|
76
|
+
if (trimmed.startsWith("/")) {
|
|
77
|
+
const result = await handleSpecialCommand(ctx, trimmed, rl);
|
|
78
|
+
if (result === "exit") {
|
|
79
|
+
done(result);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
} else if (ctx.sandboxes.size === 0) {
|
|
83
|
+
console.log(pc.yellow("No active sandbox. Use /new to create one."));
|
|
84
|
+
} else {
|
|
85
|
+
const sandbox = ctx.sandboxes.get(ctx.activeSandboxId);
|
|
86
|
+
await executeCommand(sandbox, trimmed);
|
|
87
|
+
}
|
|
88
|
+
} catch (error) {
|
|
89
|
+
console.log(pc.red(`\u2717 ${error.message}`));
|
|
90
|
+
}
|
|
91
|
+
if (!resolved) {
|
|
92
|
+
rl.prompt();
|
|
40
93
|
}
|
|
41
94
|
});
|
|
95
|
+
rl.prompt();
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async function executeCommand(sandbox, command) {
|
|
99
|
+
const result = await sandbox.runCommand(command);
|
|
100
|
+
if (result.stdout) {
|
|
101
|
+
process.stdout.write(result.stdout);
|
|
102
|
+
if (!result.stdout.endsWith("\n")) {
|
|
103
|
+
console.log();
|
|
104
|
+
}
|
|
42
105
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
106
|
+
if (result.stderr) {
|
|
107
|
+
process.stderr.write(pc.red(result.stderr));
|
|
108
|
+
if (!result.stderr.endsWith("\n")) {
|
|
109
|
+
console.log();
|
|
46
110
|
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
111
|
+
}
|
|
112
|
+
if (result.exitCode !== 0) {
|
|
113
|
+
console.log(pc.gray(`exit code: ${result.exitCode}`));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async function handleSpecialCommand(ctx, input, rl) {
|
|
117
|
+
const [cmd, ...args] = input.slice(1).split(/\s+/);
|
|
118
|
+
switch (cmd.toLowerCase()) {
|
|
119
|
+
case "help":
|
|
120
|
+
case "h":
|
|
121
|
+
case "?":
|
|
122
|
+
showHelp();
|
|
123
|
+
return null;
|
|
124
|
+
case "info":
|
|
125
|
+
case "i": {
|
|
126
|
+
if (ctx.sandboxes.size === 0) {
|
|
127
|
+
console.log(pc.yellow("No active sandbox. Use /new to create one."));
|
|
128
|
+
return null;
|
|
52
129
|
}
|
|
53
|
-
|
|
130
|
+
const activeSandbox = ctx.sandboxes.get(ctx.activeSandboxId);
|
|
131
|
+
console.log();
|
|
132
|
+
console.log(pc.bold(" Sandbox Info"));
|
|
133
|
+
console.log(` ID: ${pc.cyan(activeSandbox.sandboxId)}`);
|
|
134
|
+
console.log();
|
|
135
|
+
return null;
|
|
54
136
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
terminal.write(data.toString());
|
|
61
|
-
};
|
|
62
|
-
process.stdin.on("data", onData);
|
|
63
|
-
const onResize = () => {
|
|
64
|
-
if (process.stdout.columns && process.stdout.rows) {
|
|
65
|
-
try {
|
|
66
|
-
terminal.resize(process.stdout.columns, process.stdout.rows);
|
|
67
|
-
} catch {
|
|
137
|
+
case "url":
|
|
138
|
+
case "u": {
|
|
139
|
+
if (ctx.sandboxes.size === 0) {
|
|
140
|
+
console.log(pc.yellow("No active sandbox. Use /new to create one."));
|
|
141
|
+
return null;
|
|
68
142
|
}
|
|
143
|
+
const activeSandbox = ctx.sandboxes.get(ctx.activeSandboxId);
|
|
144
|
+
if (args.length === 0) {
|
|
145
|
+
console.log(pc.gray("Usage: /url <port>"));
|
|
146
|
+
} else {
|
|
147
|
+
const port = parseInt(args[0], 10);
|
|
148
|
+
if (isNaN(port)) {
|
|
149
|
+
console.log(pc.red("Invalid port number"));
|
|
150
|
+
} else {
|
|
151
|
+
const url = await activeSandbox.getUrl({ port });
|
|
152
|
+
console.log(pc.cyan(url));
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
69
156
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
157
|
+
case "new": {
|
|
158
|
+
console.log(pc.gray(" Creating new sandbox..."));
|
|
159
|
+
const newSandbox = await compute.sandbox.create();
|
|
160
|
+
ctx.sandboxes.set(newSandbox.sandboxId, newSandbox);
|
|
161
|
+
ctx.activeSandboxId = newSandbox.sandboxId;
|
|
162
|
+
rl.setPrompt(buildPrompt(ctx.activeSandboxId));
|
|
163
|
+
console.log(` Sandbox ready: ${pc.cyan(newSandbox.sandboxId)}`);
|
|
164
|
+
console.log(` ${pc.gray(`Active sandboxes: ${ctx.sandboxes.size}`)}`);
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
case "destroy": {
|
|
168
|
+
if (args.length === 0) {
|
|
169
|
+
console.log(pc.gray("Usage: /destroy <sandbox-id> or /destroy --all"));
|
|
170
|
+
console.log(pc.gray("Use /list to see all sandboxes"));
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
if (args[0] === "--all") {
|
|
174
|
+
if (ctx.sandboxes.size === 0) {
|
|
175
|
+
console.log(pc.yellow(" No sandboxes to destroy."));
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
const count = ctx.sandboxes.size;
|
|
179
|
+
console.log(pc.gray(` Destroying ${count} sandbox${count > 1 ? "es" : ""}...`));
|
|
180
|
+
await Promise.all(
|
|
181
|
+
Array.from(ctx.sandboxes.values()).map((s) => s.destroy())
|
|
182
|
+
);
|
|
183
|
+
ctx.sandboxes.clear();
|
|
184
|
+
ctx.activeSandboxId = "";
|
|
185
|
+
rl.setPrompt(buildPrompt(ctx.activeSandboxId));
|
|
186
|
+
console.log(` Destroyed ${pc.cyan(String(count))} sandbox${count > 1 ? "es" : ""}`);
|
|
187
|
+
console.log(pc.yellow(" No sandboxes remaining. Use /new to create one or /exit to quit."));
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
const targetId = args[0];
|
|
191
|
+
const target = ctx.sandboxes.get(targetId);
|
|
192
|
+
if (!target) {
|
|
193
|
+
const matches = Array.from(ctx.sandboxes.keys()).filter((id) => id.startsWith(targetId));
|
|
194
|
+
if (matches.length === 1) {
|
|
195
|
+
return destroySandbox(ctx, matches[0], rl);
|
|
196
|
+
} else if (matches.length > 1) {
|
|
197
|
+
console.log(pc.yellow(" Ambiguous ID. Matches:"));
|
|
198
|
+
for (const id of matches) {
|
|
199
|
+
console.log(` ${pc.cyan(id)}`);
|
|
200
|
+
}
|
|
201
|
+
} else {
|
|
202
|
+
console.log(pc.red(` Sandbox not found: ${targetId}`));
|
|
203
|
+
console.log(pc.gray(" Use /list to see all sandboxes"));
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
return destroySandbox(ctx, targetId, rl);
|
|
80
208
|
}
|
|
81
|
-
|
|
82
|
-
|
|
209
|
+
case "list":
|
|
210
|
+
case "ls": {
|
|
211
|
+
console.log();
|
|
212
|
+
console.log(pc.bold(" Sandboxes"));
|
|
213
|
+
for (const id of ctx.sandboxes.keys()) {
|
|
214
|
+
const marker = id === ctx.activeSandboxId ? pc.green(" \u25CF") : " ";
|
|
215
|
+
console.log(` ${marker} ${pc.cyan(id)}`);
|
|
216
|
+
}
|
|
217
|
+
console.log();
|
|
218
|
+
return null;
|
|
83
219
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
220
|
+
case "switch":
|
|
221
|
+
case "sw": {
|
|
222
|
+
if (args.length === 0) {
|
|
223
|
+
console.log(pc.gray("Usage: /switch <sandbox-id>"));
|
|
224
|
+
console.log(pc.gray("Use /list to see all sandboxes"));
|
|
225
|
+
return null;
|
|
226
|
+
}
|
|
227
|
+
const switchId = args[0];
|
|
228
|
+
if (ctx.sandboxes.has(switchId)) {
|
|
229
|
+
ctx.activeSandboxId = switchId;
|
|
230
|
+
rl.setPrompt(buildPrompt(ctx.activeSandboxId));
|
|
231
|
+
console.log(` Switched to ${pc.cyan(switchId)}`);
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
const matches = Array.from(ctx.sandboxes.keys()).filter((id) => id.startsWith(switchId));
|
|
235
|
+
if (matches.length === 1) {
|
|
236
|
+
ctx.activeSandboxId = matches[0];
|
|
237
|
+
rl.setPrompt(buildPrompt(ctx.activeSandboxId));
|
|
238
|
+
console.log(` Switched to ${pc.cyan(matches[0])}`);
|
|
239
|
+
} else if (matches.length > 1) {
|
|
240
|
+
console.log(pc.yellow(" Ambiguous ID. Matches:"));
|
|
241
|
+
for (const id of matches) {
|
|
242
|
+
console.log(` ${pc.cyan(id)}`);
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
console.log(pc.red(` Sandbox not found: ${switchId}`));
|
|
246
|
+
console.log(pc.gray(" Use /list to see all sandboxes"));
|
|
247
|
+
}
|
|
248
|
+
return null;
|
|
90
249
|
}
|
|
91
|
-
|
|
250
|
+
case "exit":
|
|
251
|
+
case "quit":
|
|
252
|
+
case "q":
|
|
253
|
+
return "exit";
|
|
254
|
+
default:
|
|
255
|
+
console.log(pc.yellow(`Unknown command: /${cmd}`));
|
|
256
|
+
console.log(pc.gray("Type /help for available commands"));
|
|
257
|
+
return null;
|
|
92
258
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
259
|
+
}
|
|
260
|
+
async function destroySandbox(ctx, sandboxId, rl) {
|
|
261
|
+
const target = ctx.sandboxes.get(sandboxId);
|
|
262
|
+
console.log(pc.gray(` Destroying ${sandboxId}...`));
|
|
263
|
+
await target.destroy();
|
|
264
|
+
ctx.sandboxes.delete(sandboxId);
|
|
265
|
+
console.log(` Destroyed ${pc.cyan(sandboxId)}`);
|
|
266
|
+
if (ctx.activeSandboxId === sandboxId && ctx.sandboxes.size > 0) {
|
|
267
|
+
ctx.activeSandboxId = ctx.sandboxes.keys().next().value;
|
|
268
|
+
rl.setPrompt(buildPrompt(ctx.activeSandboxId));
|
|
269
|
+
console.log(` Switched to ${pc.cyan(ctx.activeSandboxId)}`);
|
|
270
|
+
} else if (ctx.sandboxes.size === 0) {
|
|
271
|
+
ctx.activeSandboxId = "";
|
|
272
|
+
rl.setPrompt(buildPrompt(ctx.activeSandboxId));
|
|
273
|
+
console.log(pc.yellow(" No sandboxes remaining. Use /new to create one or /exit to quit."));
|
|
274
|
+
}
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
function showHelp() {
|
|
278
|
+
console.log();
|
|
279
|
+
console.log(pc.bold(" Shell Mode"));
|
|
280
|
+
console.log(pc.gray(" Just type commands - they run in the active sandbox"));
|
|
281
|
+
console.log();
|
|
282
|
+
console.log(pc.white(" ls -la"));
|
|
283
|
+
console.log(pc.white(" cat /etc/os-release"));
|
|
284
|
+
console.log(pc.white(" npm install lodash"));
|
|
285
|
+
console.log();
|
|
286
|
+
console.log(pc.bold(" Sandbox Commands") + pc.gray(" (prefix with /)"));
|
|
287
|
+
console.log();
|
|
288
|
+
console.log(pc.cyan(" /new") + pc.gray(" Create a new sandbox and switch to it"));
|
|
289
|
+
console.log(pc.cyan(" /list") + pc.gray(" List all sandboxes"));
|
|
290
|
+
console.log(pc.cyan(" /switch <id>") + pc.gray(" Switch active sandbox"));
|
|
291
|
+
console.log(pc.cyan(" /destroy <id>") + pc.gray(" Destroy a sandbox by ID"));
|
|
292
|
+
console.log(pc.cyan(" /destroy --all") + pc.gray(" Destroy all sandboxes"));
|
|
293
|
+
console.log();
|
|
294
|
+
console.log(pc.bold(" Other Commands"));
|
|
295
|
+
console.log();
|
|
296
|
+
console.log(pc.cyan(" /info") + pc.gray(" Show active sandbox info"));
|
|
297
|
+
console.log(pc.cyan(" /url <port>") + pc.gray(" Get public URL for port"));
|
|
298
|
+
console.log(pc.cyan(" /help") + pc.gray(" Show this help"));
|
|
299
|
+
console.log(pc.cyan(" /exit") + pc.gray(" Exit the REPL"));
|
|
300
|
+
console.log();
|
|
301
|
+
}
|
|
302
|
+
function loadHistory(rl) {
|
|
303
|
+
try {
|
|
304
|
+
if (fs.existsSync(HISTORY_FILE)) {
|
|
305
|
+
const history = fs.readFileSync(HISTORY_FILE, "utf-8").split("\n").filter((line) => line.trim()).reverse();
|
|
306
|
+
for (const line of history.slice(0, 1e3)) {
|
|
307
|
+
rl.history?.push(line);
|
|
99
308
|
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
});
|
|
104
|
-
});
|
|
309
|
+
}
|
|
310
|
+
} catch {
|
|
311
|
+
}
|
|
105
312
|
}
|
|
106
|
-
|
|
107
|
-
|
|
313
|
+
function saveToHistory(line) {
|
|
314
|
+
try {
|
|
315
|
+
fs.appendFileSync(HISTORY_FILE, line + "\n");
|
|
316
|
+
} catch {
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
var HISTORY_FILE;
|
|
320
|
+
var init_repl = __esm({
|
|
321
|
+
"src/repl.ts"() {
|
|
108
322
|
"use strict";
|
|
109
323
|
init_esm_shims();
|
|
324
|
+
HISTORY_FILE = path2.join(os.homedir(), ".create_compute_history");
|
|
110
325
|
}
|
|
111
326
|
});
|
|
112
327
|
|
|
@@ -123,33 +338,33 @@ __export(workspace_cli_exports, {
|
|
|
123
338
|
workspaceDestroy: () => workspaceDestroy,
|
|
124
339
|
workspaceList: () => workspaceList
|
|
125
340
|
});
|
|
126
|
-
import { compute as
|
|
127
|
-
import * as
|
|
128
|
-
import * as
|
|
129
|
-
import * as
|
|
341
|
+
import { compute as compute3 } from "computesdk";
|
|
342
|
+
import * as fs3 from "fs";
|
|
343
|
+
import * as path4 from "path";
|
|
344
|
+
import * as os3 from "os";
|
|
130
345
|
import * as p3 from "@clack/prompts";
|
|
131
|
-
import
|
|
346
|
+
import pc4 from "picocolors";
|
|
132
347
|
function shellEscape(arg) {
|
|
133
348
|
if (/^[a-zA-Z0-9._\-/:=@]+$/.test(arg)) return arg;
|
|
134
349
|
return `'${arg.replace(/'/g, "'\\''")}'`;
|
|
135
350
|
}
|
|
136
351
|
function loadState() {
|
|
137
352
|
try {
|
|
138
|
-
if (!
|
|
353
|
+
if (!fs3.existsSync(WORKSPACE_STATE_FILE)) {
|
|
139
354
|
return { workspaces: {} };
|
|
140
355
|
}
|
|
141
|
-
const raw =
|
|
356
|
+
const raw = fs3.readFileSync(WORKSPACE_STATE_FILE, "utf-8");
|
|
142
357
|
return JSON.parse(raw);
|
|
143
358
|
} catch {
|
|
144
359
|
return { workspaces: {} };
|
|
145
360
|
}
|
|
146
361
|
}
|
|
147
362
|
function saveState(state) {
|
|
148
|
-
const dir =
|
|
149
|
-
if (!
|
|
150
|
-
|
|
363
|
+
const dir = path4.dirname(WORKSPACE_STATE_FILE);
|
|
364
|
+
if (!fs3.existsSync(dir)) {
|
|
365
|
+
fs3.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
151
366
|
}
|
|
152
|
-
|
|
367
|
+
fs3.writeFileSync(WORKSPACE_STATE_FILE, JSON.stringify(state, null, 2) + "\n", { mode: 384 });
|
|
153
368
|
}
|
|
154
369
|
function getWorkspaceId(repo, branch) {
|
|
155
370
|
return `${repo}:${branch}`;
|
|
@@ -161,13 +376,13 @@ async function workspaceCreate(options) {
|
|
|
161
376
|
if (state.workspaces[id]) {
|
|
162
377
|
const existing = state.workspaces[id];
|
|
163
378
|
try {
|
|
164
|
-
const sandbox2 = await
|
|
379
|
+
const sandbox2 = await compute3.sandbox.getById(existing.sandboxId);
|
|
165
380
|
if (!sandbox2) {
|
|
166
381
|
throw new Error("Sandbox not found");
|
|
167
382
|
}
|
|
168
383
|
existing.lastUsed = (/* @__PURE__ */ new Date()).toISOString();
|
|
169
384
|
saveState(state);
|
|
170
|
-
p3.log.info(`Attaching to existing workspace: ${
|
|
385
|
+
p3.log.info(`Attaching to existing workspace: ${pc4.cyan(id)}`);
|
|
171
386
|
return existing;
|
|
172
387
|
} catch {
|
|
173
388
|
delete state.workspaces[id];
|
|
@@ -178,14 +393,14 @@ async function workspaceCreate(options) {
|
|
|
178
393
|
spinner4.start("Provisioning via ComputeSDK...");
|
|
179
394
|
let sandbox;
|
|
180
395
|
try {
|
|
181
|
-
sandbox = await
|
|
182
|
-
spinner4.stop(`Sandbox ready: ${
|
|
396
|
+
sandbox = await compute3.sandbox.create();
|
|
397
|
+
spinner4.stop(`Sandbox ready: ${pc4.cyan(sandbox.sandboxId)}`);
|
|
183
398
|
} catch (error) {
|
|
184
|
-
spinner4.stop(
|
|
399
|
+
spinner4.stop(pc4.red("Failed to create sandbox"));
|
|
185
400
|
throw error;
|
|
186
401
|
}
|
|
187
402
|
spinner4.start("Bootstrapping workspace (installing gh CLI, cloning repo)...");
|
|
188
|
-
const bootstrapScript =
|
|
403
|
+
const bootstrapScript = fs3.readFileSync(
|
|
189
404
|
new URL("./bootstrap.sh", import.meta.url),
|
|
190
405
|
"utf-8"
|
|
191
406
|
);
|
|
@@ -200,7 +415,7 @@ async function workspaceCreate(options) {
|
|
|
200
415
|
);
|
|
201
416
|
const output = result.stdout || "";
|
|
202
417
|
const jsonMatch = output.match(/\{[\s\S]*\}/);
|
|
203
|
-
let worktreePath = `/workspaces/${
|
|
418
|
+
let worktreePath = `/workspaces/${path4.basename(repo)}-${branch}`;
|
|
204
419
|
if (jsonMatch) {
|
|
205
420
|
try {
|
|
206
421
|
const bootstrapInfo = JSON.parse(jsonMatch[0]);
|
|
@@ -221,12 +436,12 @@ async function workspaceCreate(options) {
|
|
|
221
436
|
};
|
|
222
437
|
state.workspaces[id] = workspace;
|
|
223
438
|
saveState(state);
|
|
224
|
-
p3.log.success(`Workspace ready: ${
|
|
225
|
-
p3.log.info(`Sandbox: ${
|
|
226
|
-
p3.log.info(`Worktree: ${
|
|
439
|
+
p3.log.success(`Workspace ready: ${pc4.cyan(id)}`);
|
|
440
|
+
p3.log.info(`Sandbox: ${pc4.dim(sandbox.sandboxId)}`);
|
|
441
|
+
p3.log.info(`Worktree: ${pc4.dim(worktreePath)}`);
|
|
227
442
|
return workspace;
|
|
228
443
|
} catch (error) {
|
|
229
|
-
spinner4.stop(
|
|
444
|
+
spinner4.stop(pc4.red("Bootstrap failed"));
|
|
230
445
|
try {
|
|
231
446
|
await sandbox.destroy();
|
|
232
447
|
} catch {
|
|
@@ -242,7 +457,7 @@ async function workspaceAttach(id) {
|
|
|
242
457
|
p3.log.info(`Run: compute workspace create --repo owner/repo --branch ${id.split(":")[1] || "main"}`);
|
|
243
458
|
process.exit(1);
|
|
244
459
|
}
|
|
245
|
-
const sandbox = await
|
|
460
|
+
const sandbox = await compute3.sandbox.getById(workspace.sandboxId);
|
|
246
461
|
if (!sandbox) {
|
|
247
462
|
p3.log.error(`Sandbox no longer exists: ${workspace.sandboxId}`);
|
|
248
463
|
delete state.workspaces[id];
|
|
@@ -251,12 +466,11 @@ async function workspaceAttach(id) {
|
|
|
251
466
|
}
|
|
252
467
|
workspace.lastUsed = (/* @__PURE__ */ new Date()).toISOString();
|
|
253
468
|
saveState(state);
|
|
254
|
-
p3.log.info(`Attaching to workspace: ${
|
|
255
|
-
p3.log.info(`
|
|
256
|
-
console.log(
|
|
469
|
+
p3.log.info(`Attaching to workspace: ${pc4.cyan(id)}`);
|
|
470
|
+
p3.log.info(`Worktree: ${pc4.dim(workspace.worktreePath)}`);
|
|
471
|
+
console.log(pc4.gray(`Tip: commands run at sandbox root; use { cwd: "${workspace.worktreePath}" } on runCommand to scope them.`));
|
|
257
472
|
try {
|
|
258
|
-
|
|
259
|
-
await startPTY(sandbox, { initialCommand: `cd ${shellEscape(workspace.worktreePath)}` });
|
|
473
|
+
await startREPL(sandbox, workspace.provider);
|
|
260
474
|
} catch (error) {
|
|
261
475
|
p3.log.error(`Failed to attach: ${error.message}`);
|
|
262
476
|
process.exit(1);
|
|
@@ -271,19 +485,19 @@ async function sandboxList() {
|
|
|
271
485
|
return;
|
|
272
486
|
}
|
|
273
487
|
console.log();
|
|
274
|
-
console.log(
|
|
488
|
+
console.log(pc4.bold("Sandboxes"));
|
|
275
489
|
console.log();
|
|
276
490
|
for (const ws of workspaces) {
|
|
277
491
|
const age = Math.round((Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24));
|
|
278
492
|
const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
|
|
279
|
-
const status = age > 7 ?
|
|
280
|
-
console.log(` ${status} ${
|
|
281
|
-
console.log(` Workspace: ${
|
|
493
|
+
const status = age > 7 ? pc4.red("stale") : age > 1 ? pc4.yellow("idle") : pc4.green("active");
|
|
494
|
+
console.log(` ${status} ${pc4.cyan(ws.sandboxId)}`);
|
|
495
|
+
console.log(` Workspace: ${pc4.dim(ws.id)}`);
|
|
282
496
|
console.log(` Provider: ${ws.provider}`);
|
|
283
|
-
console.log(` Last active: ${
|
|
497
|
+
console.log(` Last active: ${pc4.gray(ageStr)}`);
|
|
284
498
|
console.log();
|
|
285
499
|
}
|
|
286
|
-
console.log(
|
|
500
|
+
console.log(pc4.gray('Tip: Use "compute workspaces" to see full workspace details'));
|
|
287
501
|
}
|
|
288
502
|
async function workspaceList() {
|
|
289
503
|
const state = loadState();
|
|
@@ -294,15 +508,15 @@ async function workspaceList() {
|
|
|
294
508
|
return;
|
|
295
509
|
}
|
|
296
510
|
console.log();
|
|
297
|
-
console.log(
|
|
511
|
+
console.log(pc4.bold("Workspaces"));
|
|
298
512
|
console.log();
|
|
299
513
|
for (const ws of workspaces) {
|
|
300
514
|
const age = Math.round((Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24));
|
|
301
515
|
const ageStr = age === 0 ? "today" : `${age}d ago`;
|
|
302
|
-
console.log(` ${
|
|
303
|
-
console.log(` Sandbox: ${
|
|
304
|
-
console.log(` Path: ${
|
|
305
|
-
console.log(` Last used: ${
|
|
516
|
+
console.log(` ${pc4.cyan(ws.id)}`);
|
|
517
|
+
console.log(` Sandbox: ${pc4.dim(ws.sandboxId)} (${ws.provider})`);
|
|
518
|
+
console.log(` Path: ${pc4.dim(ws.worktreePath)}`);
|
|
519
|
+
console.log(` Last used: ${pc4.gray(ageStr)}`);
|
|
306
520
|
console.log();
|
|
307
521
|
}
|
|
308
522
|
}
|
|
@@ -328,9 +542,9 @@ async function workspaceDestroy(id) {
|
|
|
328
542
|
p3.log.error(`Workspace not found: ${id}`);
|
|
329
543
|
return;
|
|
330
544
|
}
|
|
331
|
-
p3.log.info(`Destroying workspace: ${
|
|
545
|
+
p3.log.info(`Destroying workspace: ${pc4.cyan(id)}`);
|
|
332
546
|
try {
|
|
333
|
-
await
|
|
547
|
+
await compute3.sandbox.destroy(workspace.sandboxId);
|
|
334
548
|
delete state.workspaces[id];
|
|
335
549
|
saveState(state);
|
|
336
550
|
p3.log.success("Workspace destroyed");
|
|
@@ -361,8 +575,8 @@ var init_workspace_cli = __esm({
|
|
|
361
575
|
"src/workspace-cli.ts"() {
|
|
362
576
|
"use strict";
|
|
363
577
|
init_esm_shims();
|
|
364
|
-
|
|
365
|
-
WORKSPACE_STATE_FILE =
|
|
578
|
+
init_repl();
|
|
579
|
+
WORKSPACE_STATE_FILE = path4.join(os3.homedir(), ".computesdk", "workspaces.json");
|
|
366
580
|
loadWorkspaceState = loadState;
|
|
367
581
|
}
|
|
368
582
|
});
|
|
@@ -372,11 +586,11 @@ init_esm_shims();
|
|
|
372
586
|
import "dotenv/config";
|
|
373
587
|
import { Command } from "commander";
|
|
374
588
|
import * as p4 from "@clack/prompts";
|
|
375
|
-
import
|
|
376
|
-
import { readFileSync as
|
|
589
|
+
import pc5 from "picocolors";
|
|
590
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
377
591
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
378
|
-
import { dirname as dirname2, join as
|
|
379
|
-
import { compute as
|
|
592
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
593
|
+
import { compute as compute4 } from "computesdk";
|
|
380
594
|
|
|
381
595
|
// src/providers.ts
|
|
382
596
|
init_esm_shims();
|
|
@@ -449,23 +663,23 @@ function buildProviderConfig(provider) {
|
|
|
449
663
|
}
|
|
450
664
|
|
|
451
665
|
// src/index.ts
|
|
452
|
-
|
|
666
|
+
init_repl();
|
|
453
667
|
|
|
454
668
|
// src/auth.ts
|
|
455
669
|
init_esm_shims();
|
|
456
670
|
import * as http from "http";
|
|
457
671
|
import * as crypto from "crypto";
|
|
458
672
|
import { exec } from "child_process";
|
|
459
|
-
import * as
|
|
460
|
-
import * as
|
|
461
|
-
import * as
|
|
673
|
+
import * as fs2 from "fs";
|
|
674
|
+
import * as path3 from "path";
|
|
675
|
+
import * as os2 from "os";
|
|
462
676
|
import * as p from "@clack/prompts";
|
|
463
|
-
import
|
|
677
|
+
import pc2 from "picocolors";
|
|
464
678
|
function getCredentialsDir() {
|
|
465
|
-
return
|
|
679
|
+
return path3.join(os2.homedir(), ".computesdk");
|
|
466
680
|
}
|
|
467
681
|
function getCredentialsFile() {
|
|
468
|
-
return
|
|
682
|
+
return path3.join(getCredentialsDir(), "credentials.json");
|
|
469
683
|
}
|
|
470
684
|
var AUTH_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
471
685
|
var CONSOLE_AUTH_URL = "https://console.computesdk.com/cli/auth";
|
|
@@ -481,10 +695,10 @@ var SUCCESS_HTML = `<!DOCTYPE html>
|
|
|
481
695
|
</html>`;
|
|
482
696
|
function loadStoredCredentials() {
|
|
483
697
|
try {
|
|
484
|
-
if (!
|
|
698
|
+
if (!fs2.existsSync(getCredentialsFile())) {
|
|
485
699
|
return null;
|
|
486
700
|
}
|
|
487
|
-
const raw =
|
|
701
|
+
const raw = fs2.readFileSync(getCredentialsFile(), "utf-8");
|
|
488
702
|
const data = JSON.parse(raw);
|
|
489
703
|
if (typeof data.apiKey === "string" && data.apiKey.length > 0) {
|
|
490
704
|
return data.apiKey;
|
|
@@ -495,10 +709,10 @@ function loadStoredCredentials() {
|
|
|
495
709
|
}
|
|
496
710
|
}
|
|
497
711
|
function storeCredentials(apiKey) {
|
|
498
|
-
if (!
|
|
499
|
-
|
|
712
|
+
if (!fs2.existsSync(getCredentialsDir())) {
|
|
713
|
+
fs2.mkdirSync(getCredentialsDir(), { recursive: true, mode: 448 });
|
|
500
714
|
}
|
|
501
|
-
|
|
715
|
+
fs2.writeFileSync(
|
|
502
716
|
getCredentialsFile(),
|
|
503
717
|
JSON.stringify({ apiKey }, null, 2) + "\n",
|
|
504
718
|
{ mode: 384 }
|
|
@@ -506,8 +720,8 @@ function storeCredentials(apiKey) {
|
|
|
506
720
|
}
|
|
507
721
|
function clearStoredCredentials() {
|
|
508
722
|
try {
|
|
509
|
-
if (
|
|
510
|
-
|
|
723
|
+
if (fs2.existsSync(getCredentialsFile())) {
|
|
724
|
+
fs2.unlinkSync(getCredentialsFile());
|
|
511
725
|
}
|
|
512
726
|
} catch {
|
|
513
727
|
}
|
|
@@ -518,7 +732,7 @@ function openBrowser(url) {
|
|
|
518
732
|
if (err) {
|
|
519
733
|
p.log.warn("Could not open browser automatically.");
|
|
520
734
|
p.log.info(`Open this URL manually:
|
|
521
|
-
${
|
|
735
|
+
${pc2.cyan(url)}`);
|
|
522
736
|
}
|
|
523
737
|
});
|
|
524
738
|
}
|
|
@@ -589,7 +803,7 @@ async function resolveApiKey(options) {
|
|
|
589
803
|
const stored = loadStoredCredentials();
|
|
590
804
|
if (stored) {
|
|
591
805
|
p.log.info(
|
|
592
|
-
`Using stored credentials from ${
|
|
806
|
+
`Using stored credentials from ${pc2.dim("~/.computesdk/credentials.json")}`
|
|
593
807
|
);
|
|
594
808
|
return stored;
|
|
595
809
|
}
|
|
@@ -603,14 +817,14 @@ async function resolveApiKey(options) {
|
|
|
603
817
|
try {
|
|
604
818
|
storeCredentials(apiKey);
|
|
605
819
|
p.log.success(
|
|
606
|
-
`Credentials saved to ${
|
|
820
|
+
`Credentials saved to ${pc2.dim("~/.computesdk/credentials.json")}`
|
|
607
821
|
);
|
|
608
822
|
} catch {
|
|
609
823
|
p.log.warn("Could not save credentials to disk. You may need to authenticate again next time.");
|
|
610
824
|
}
|
|
611
825
|
return apiKey;
|
|
612
826
|
} catch (error) {
|
|
613
|
-
spinner4.stop(
|
|
827
|
+
spinner4.stop(pc2.red("Authentication failed"));
|
|
614
828
|
throw error;
|
|
615
829
|
}
|
|
616
830
|
}
|
|
@@ -618,16 +832,16 @@ async function resolveApiKey(options) {
|
|
|
618
832
|
// src/setup.ts
|
|
619
833
|
init_esm_shims();
|
|
620
834
|
import * as p2 from "@clack/prompts";
|
|
621
|
-
import
|
|
622
|
-
import { compute } from "computesdk";
|
|
835
|
+
import pc3 from "picocolors";
|
|
836
|
+
import { compute as compute2 } from "computesdk";
|
|
623
837
|
async function ensureAuth(options) {
|
|
624
838
|
try {
|
|
625
839
|
const apiKey = await resolveApiKey({ forceLogin: options?.forceLogin });
|
|
626
840
|
process.env.COMPUTESDK_API_KEY = apiKey;
|
|
627
841
|
} catch (error) {
|
|
628
842
|
p2.log.error(error.message);
|
|
629
|
-
p2.log.info(`Get your API key at ${
|
|
630
|
-
p2.outro(
|
|
843
|
+
p2.log.info(`Get your API key at ${pc3.cyan("https://console.computesdk.com")}`);
|
|
844
|
+
p2.outro(pc3.red("Setup incomplete"));
|
|
631
845
|
process.exit(1);
|
|
632
846
|
}
|
|
633
847
|
}
|
|
@@ -656,7 +870,7 @@ async function resolveProvider(providerFlag, options) {
|
|
|
656
870
|
if (available.length === 0) {
|
|
657
871
|
p2.log.error("No provider credentials detected");
|
|
658
872
|
p2.log.info("Set one of: E2B_API_KEY, MODAL_TOKEN_ID, RAILWAY_API_KEY, etc.");
|
|
659
|
-
p2.outro(
|
|
873
|
+
p2.outro(pc3.red("Setup incomplete"));
|
|
660
874
|
process.exit(1);
|
|
661
875
|
}
|
|
662
876
|
if (available.length === 1) {
|
|
@@ -683,7 +897,7 @@ async function resolveProvider(providerFlag, options) {
|
|
|
683
897
|
}
|
|
684
898
|
function configureCompute(provider) {
|
|
685
899
|
const config = buildProviderConfig(provider);
|
|
686
|
-
|
|
900
|
+
compute2.setConfig(config);
|
|
687
901
|
}
|
|
688
902
|
|
|
689
903
|
// src/index.ts
|
|
@@ -691,7 +905,7 @@ init_workspace_cli();
|
|
|
691
905
|
var __filename2 = fileURLToPath2(import.meta.url);
|
|
692
906
|
var __dirname2 = dirname2(__filename2);
|
|
693
907
|
var packageJson = JSON.parse(
|
|
694
|
-
|
|
908
|
+
readFileSync4(join4(__dirname2, "..", "package.json"), "utf-8")
|
|
695
909
|
);
|
|
696
910
|
var isDevBuild = import.meta.url.includes("worktrees") || import.meta.url.includes("/packages/cli/");
|
|
697
911
|
var VERSION = isDevBuild ? `${packageJson.version}-dev` : packageJson.version;
|
|
@@ -700,24 +914,24 @@ program.name("compute").description("Cloud-native workspace management").version
|
|
|
700
914
|
program.option("--login", "force re-authentication").option("--logout", "clear stored credentials").action(async (opts) => {
|
|
701
915
|
if (opts.logout) {
|
|
702
916
|
console.log();
|
|
703
|
-
p4.intro(
|
|
917
|
+
p4.intro(pc5.cyan(`@computesdk/cli v${VERSION}`));
|
|
704
918
|
clearStoredCredentials();
|
|
705
919
|
p4.log.success("Logged out. Stored credentials removed.");
|
|
706
|
-
p4.outro(
|
|
920
|
+
p4.outro(pc5.green("Done!"));
|
|
707
921
|
process.exit(0);
|
|
708
922
|
}
|
|
709
923
|
if (opts.login) {
|
|
710
924
|
console.log();
|
|
711
|
-
p4.intro(
|
|
925
|
+
p4.intro(pc5.cyan(`@computesdk/cli v${VERSION}`));
|
|
712
926
|
await ensureAuth({ forceLogin: true });
|
|
713
|
-
p4.outro(
|
|
927
|
+
p4.outro(pc5.green("Authenticated!"));
|
|
714
928
|
process.exit(0);
|
|
715
929
|
}
|
|
716
930
|
program.help();
|
|
717
931
|
});
|
|
718
932
|
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) => {
|
|
719
933
|
console.log();
|
|
720
|
-
p4.intro(
|
|
934
|
+
p4.intro(pc5.cyan(`@computesdk/cli v${VERSION}`));
|
|
721
935
|
await ensureAuth();
|
|
722
936
|
if (!repo) {
|
|
723
937
|
const state = loadWorkspaceState();
|
|
@@ -738,7 +952,7 @@ program.command("create").description("Create a new workspace (same as create-co
|
|
|
738
952
|
message: "Select workspace to attach (or create new)",
|
|
739
953
|
options: [
|
|
740
954
|
...choices,
|
|
741
|
-
{ value: "__new__", label:
|
|
955
|
+
{ value: "__new__", label: pc5.gray("+ Create new workspace") }
|
|
742
956
|
]
|
|
743
957
|
});
|
|
744
958
|
if (p4.isCancel(selected)) {
|
|
@@ -779,24 +993,24 @@ program.command("create").description("Create a new workspace (same as create-co
|
|
|
779
993
|
branch: options.branch,
|
|
780
994
|
provider
|
|
781
995
|
});
|
|
782
|
-
spinner4.stop(`Workspace ready: ${
|
|
996
|
+
spinner4.stop(`Workspace ready: ${pc5.cyan(workspace.id)}`);
|
|
783
997
|
if (options.attach !== false) {
|
|
784
998
|
console.log();
|
|
785
999
|
await workspaceAttach(workspace.id);
|
|
786
1000
|
} else {
|
|
787
|
-
p4.outro(
|
|
1001
|
+
p4.outro(pc5.green("Workspace created!"));
|
|
788
1002
|
}
|
|
789
1003
|
} catch (error) {
|
|
790
|
-
spinner4.stop(
|
|
1004
|
+
spinner4.stop(pc5.red("Failed to create workspace"));
|
|
791
1005
|
p4.log.error(error.message);
|
|
792
1006
|
process.exit(1);
|
|
793
1007
|
}
|
|
794
1008
|
});
|
|
795
1009
|
var sandboxCmd = program.command("sandbox").alias("sb").description("Manage raw sandboxes (VMs without git/worktree)");
|
|
796
|
-
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
|
|
1010
|
+
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) => {
|
|
797
1011
|
if (!opts.silent) {
|
|
798
1012
|
console.log();
|
|
799
|
-
p4.intro(
|
|
1013
|
+
p4.intro(pc5.cyan(`@computesdk/cli v${VERSION}`));
|
|
800
1014
|
}
|
|
801
1015
|
await ensureAuth();
|
|
802
1016
|
const provider = await resolveProvider(opts.provider);
|
|
@@ -805,10 +1019,10 @@ sandboxCmd.command("create").description("Create a new sandbox").option("-p, --p
|
|
|
805
1019
|
const spinner4 = p4.spinner();
|
|
806
1020
|
spinner4.start("Creating sandbox...");
|
|
807
1021
|
try {
|
|
808
|
-
const sandbox = await
|
|
1022
|
+
const sandbox = await compute4.sandbox.create(
|
|
809
1023
|
buildCreateOptions(opts)
|
|
810
1024
|
);
|
|
811
|
-
spinner4.stop(`Sandbox ready: ${
|
|
1025
|
+
spinner4.stop(`Sandbox ready: ${pc5.cyan(sandbox.sandboxId)}`);
|
|
812
1026
|
const { saveSandbox: saveSandbox2 } = await Promise.resolve().then(() => (init_workspace_cli(), workspace_cli_exports));
|
|
813
1027
|
await saveSandbox2({
|
|
814
1028
|
id: sandbox.sandboxId,
|
|
@@ -818,19 +1032,16 @@ sandboxCmd.command("create").description("Create a new sandbox").option("-p, --p
|
|
|
818
1032
|
lastUsed: (/* @__PURE__ */ new Date()).toISOString()
|
|
819
1033
|
});
|
|
820
1034
|
if (opts.connect) {
|
|
821
|
-
|
|
822
|
-
console.log();
|
|
823
|
-
process.stdout.write("\x1B[2J\x1B[H\x1B[3J");
|
|
824
|
-
await startPTY(sandbox);
|
|
1035
|
+
await startREPL(sandbox, provider);
|
|
825
1036
|
}
|
|
826
1037
|
} catch (error) {
|
|
827
|
-
spinner4.stop(
|
|
1038
|
+
spinner4.stop(pc5.red("Failed to create sandbox"));
|
|
828
1039
|
p4.log.error(error.message);
|
|
829
1040
|
process.exit(1);
|
|
830
1041
|
}
|
|
831
1042
|
} else {
|
|
832
1043
|
try {
|
|
833
|
-
const sandbox = await
|
|
1044
|
+
const sandbox = await compute4.sandbox.create(
|
|
834
1045
|
buildCreateOptions(opts)
|
|
835
1046
|
);
|
|
836
1047
|
process.stdout.write(sandbox.sandboxId);
|
|
@@ -843,9 +1054,7 @@ sandboxCmd.command("create").description("Create a new sandbox").option("-p, --p
|
|
|
843
1054
|
lastUsed: (/* @__PURE__ */ new Date()).toISOString()
|
|
844
1055
|
});
|
|
845
1056
|
if (opts.connect) {
|
|
846
|
-
|
|
847
|
-
process.stdout.write("\x1B[2J\x1B[H\x1B[3J");
|
|
848
|
-
await startPTY(sandbox);
|
|
1057
|
+
await startREPL(sandbox, provider);
|
|
849
1058
|
}
|
|
850
1059
|
} catch (error) {
|
|
851
1060
|
process.stderr.write(error.message + "\n");
|
|
@@ -855,14 +1064,14 @@ sandboxCmd.command("create").description("Create a new sandbox").option("-p, --p
|
|
|
855
1064
|
});
|
|
856
1065
|
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) => {
|
|
857
1066
|
console.log();
|
|
858
|
-
p4.intro(
|
|
1067
|
+
p4.intro(pc5.cyan(`@computesdk/cli v${VERSION}`));
|
|
859
1068
|
const { loadState: loadState2, saveState: saveState2 } = await Promise.resolve().then(() => (init_workspace_cli(), workspace_cli_exports));
|
|
860
1069
|
const state = loadState2();
|
|
861
1070
|
if (!sandboxIds || sandboxIds.length === 0) {
|
|
862
1071
|
const workspaces = Object.values(state.workspaces);
|
|
863
1072
|
if (workspaces.length === 0) {
|
|
864
1073
|
p4.log.info("No sandboxes to destroy.");
|
|
865
|
-
p4.outro(
|
|
1074
|
+
p4.outro(pc5.green("Done!"));
|
|
866
1075
|
return;
|
|
867
1076
|
}
|
|
868
1077
|
const choices = workspaces.map((ws) => {
|
|
@@ -870,7 +1079,7 @@ sandboxCmd.command("destroy").description("Destroy one or more sandboxes").alias
|
|
|
870
1079
|
(Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24)
|
|
871
1080
|
);
|
|
872
1081
|
const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
|
|
873
|
-
const status = age > 7 ?
|
|
1082
|
+
const status = age > 7 ? pc5.red("stale") : age > 1 ? pc5.yellow("idle") : pc5.green("active");
|
|
874
1083
|
return {
|
|
875
1084
|
value: ws.sandboxId,
|
|
876
1085
|
label: `${status} ${ws.sandboxId} (${ws.provider}, last active ${ageStr})`
|
|
@@ -891,14 +1100,14 @@ sandboxCmd.command("destroy").description("Destroy one or more sandboxes").alias
|
|
|
891
1100
|
configureCompute(provider);
|
|
892
1101
|
const results = await Promise.allSettled(
|
|
893
1102
|
sandboxIds.map(async (id) => {
|
|
894
|
-
await
|
|
1103
|
+
await compute4.sandbox.destroy(id);
|
|
895
1104
|
return id;
|
|
896
1105
|
})
|
|
897
1106
|
);
|
|
898
1107
|
for (const result of results) {
|
|
899
1108
|
if (result.status === "fulfilled") {
|
|
900
1109
|
const sandboxId = result.value;
|
|
901
|
-
console.log(
|
|
1110
|
+
console.log(pc5.green(`Destroyed ${sandboxId}`));
|
|
902
1111
|
const workspaceId = Object.keys(state.workspaces).find(
|
|
903
1112
|
(key) => state.workspaces[key].sandboxId === sandboxId
|
|
904
1113
|
);
|
|
@@ -906,19 +1115,19 @@ sandboxCmd.command("destroy").description("Destroy one or more sandboxes").alias
|
|
|
906
1115
|
delete state.workspaces[workspaceId];
|
|
907
1116
|
}
|
|
908
1117
|
} else {
|
|
909
|
-
console.error(
|
|
1118
|
+
console.error(pc5.red(`Failed to destroy: ${result.reason?.message || result.reason}`));
|
|
910
1119
|
}
|
|
911
1120
|
}
|
|
912
1121
|
saveState2(state);
|
|
913
1122
|
});
|
|
914
1123
|
sandboxCmd.command("list").alias("ls").description("List all sandboxes").action(async () => {
|
|
915
1124
|
console.log();
|
|
916
|
-
p4.intro(
|
|
1125
|
+
p4.intro(pc5.cyan(`@computesdk/cli v${VERSION}`));
|
|
917
1126
|
await sandboxList();
|
|
918
1127
|
});
|
|
919
|
-
sandboxCmd.command("connect").description("Connect to a sandbox
|
|
1128
|
+
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) => {
|
|
920
1129
|
console.log();
|
|
921
|
-
p4.intro(
|
|
1130
|
+
p4.intro(pc5.cyan(`@computesdk/cli v${VERSION}`));
|
|
922
1131
|
await ensureAuth();
|
|
923
1132
|
const provider = await resolveProvider(opts.provider);
|
|
924
1133
|
configureCompute(provider);
|
|
@@ -929,7 +1138,7 @@ sandboxCmd.command("connect").description("Connect to a sandbox via PTY (same as
|
|
|
929
1138
|
if (workspaces.length === 0) {
|
|
930
1139
|
p4.log.info("No sandboxes found.");
|
|
931
1140
|
p4.log.info("Create one with: compute sandbox create");
|
|
932
|
-
p4.outro(
|
|
1141
|
+
p4.outro(pc5.green("Done!"));
|
|
933
1142
|
return;
|
|
934
1143
|
}
|
|
935
1144
|
const choices = workspaces.map((ws) => {
|
|
@@ -937,7 +1146,7 @@ sandboxCmd.command("connect").description("Connect to a sandbox via PTY (same as
|
|
|
937
1146
|
(Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24)
|
|
938
1147
|
);
|
|
939
1148
|
const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
|
|
940
|
-
const status = age > 7 ?
|
|
1149
|
+
const status = age > 7 ? pc5.red("stale") : age > 1 ? pc5.yellow("idle") : pc5.green("active");
|
|
941
1150
|
return {
|
|
942
1151
|
value: ws.sandboxId,
|
|
943
1152
|
label: `${status} ${ws.sandboxId} (${ws.provider}, last active ${ageStr})`
|
|
@@ -955,22 +1164,21 @@ sandboxCmd.command("connect").description("Connect to a sandbox via PTY (same as
|
|
|
955
1164
|
}
|
|
956
1165
|
const spinner4 = p4.spinner();
|
|
957
1166
|
spinner4.start("Connecting...");
|
|
958
|
-
const sandbox = await
|
|
1167
|
+
const sandbox = await compute4.sandbox.getById(sandboxId);
|
|
959
1168
|
if (!sandbox) {
|
|
960
|
-
spinner4.stop(
|
|
1169
|
+
spinner4.stop(pc5.red(`Sandbox not found: ${sandboxId}`));
|
|
961
1170
|
process.exit(1);
|
|
962
1171
|
}
|
|
963
1172
|
spinner4.stop("");
|
|
964
|
-
|
|
965
|
-
await startPTY(sandbox);
|
|
1173
|
+
await startREPL(sandbox, provider);
|
|
966
1174
|
});
|
|
967
1175
|
sandboxCmd.command("exec").description("Execute a command in a sandbox").argument("<sandbox_id>", "sandbox ID").argument("<command...>", "command to run").option("-p, --provider <provider>", "provider to use").option("-e, --env <key=value...>", "environment variables").option("-w, --workdir <directory>", "working directory").passThroughOptions().action(async (sandboxId, command, opts) => {
|
|
968
1176
|
await ensureAuth();
|
|
969
1177
|
const provider = await resolveProvider(opts.provider);
|
|
970
1178
|
configureCompute(provider);
|
|
971
|
-
const sandbox = await
|
|
1179
|
+
const sandbox = await compute4.sandbox.getById(sandboxId);
|
|
972
1180
|
if (!sandbox) {
|
|
973
|
-
console.error(
|
|
1181
|
+
console.error(pc5.red(`Sandbox not found: ${sandboxId}`));
|
|
974
1182
|
process.exit(1);
|
|
975
1183
|
}
|
|
976
1184
|
const cmd = buildExecCommand(command, opts);
|
|
@@ -983,9 +1191,9 @@ sandboxCmd.command("exec").description("Execute a command in a sandbox").argumen
|
|
|
983
1191
|
}
|
|
984
1192
|
process.exit(result.exitCode);
|
|
985
1193
|
});
|
|
986
|
-
program.command("connect").description("Connect to a sandbox
|
|
1194
|
+
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) => {
|
|
987
1195
|
console.log();
|
|
988
|
-
p4.intro(
|
|
1196
|
+
p4.intro(pc5.cyan(`@computesdk/cli v${VERSION}`));
|
|
989
1197
|
await ensureAuth();
|
|
990
1198
|
const provider = await resolveProvider(opts.provider);
|
|
991
1199
|
configureCompute(provider);
|
|
@@ -996,7 +1204,7 @@ program.command("connect").description("Connect to a sandbox via PTY").aliases([
|
|
|
996
1204
|
if (workspaces.length === 0) {
|
|
997
1205
|
p4.log.info("No sandboxes found.");
|
|
998
1206
|
p4.log.info("Create one with: compute sandbox create");
|
|
999
|
-
p4.outro(
|
|
1207
|
+
p4.outro(pc5.green("Done!"));
|
|
1000
1208
|
return;
|
|
1001
1209
|
}
|
|
1002
1210
|
const choices = workspaces.map((ws) => {
|
|
@@ -1004,7 +1212,7 @@ program.command("connect").description("Connect to a sandbox via PTY").aliases([
|
|
|
1004
1212
|
(Date.now() - new Date(ws.lastUsed).getTime()) / (1e3 * 60 * 60 * 24)
|
|
1005
1213
|
);
|
|
1006
1214
|
const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
|
|
1007
|
-
const status = age > 7 ?
|
|
1215
|
+
const status = age > 7 ? pc5.red("stale") : age > 1 ? pc5.yellow("idle") : pc5.green("active");
|
|
1008
1216
|
return {
|
|
1009
1217
|
value: ws.sandboxId,
|
|
1010
1218
|
label: `${status} ${ws.sandboxId} (${ws.provider}, last active ${ageStr})`
|
|
@@ -1022,23 +1230,22 @@ program.command("connect").description("Connect to a sandbox via PTY").aliases([
|
|
|
1022
1230
|
}
|
|
1023
1231
|
const spinner4 = p4.spinner();
|
|
1024
1232
|
spinner4.start("Connecting...");
|
|
1025
|
-
const sandbox = await
|
|
1233
|
+
const sandbox = await compute4.sandbox.getById(sandboxId);
|
|
1026
1234
|
if (!sandbox) {
|
|
1027
|
-
spinner4.stop(
|
|
1235
|
+
spinner4.stop(pc5.red(`Sandbox not found: ${sandboxId}`));
|
|
1028
1236
|
process.exit(1);
|
|
1029
1237
|
}
|
|
1030
1238
|
spinner4.stop("");
|
|
1031
|
-
|
|
1032
|
-
await startPTY(sandbox);
|
|
1239
|
+
await startREPL(sandbox, provider);
|
|
1033
1240
|
});
|
|
1034
1241
|
var workspaceCmd = program.command("workspace").alias("ws").description("Manage workspaces (sandbox + git + worktree)");
|
|
1035
1242
|
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) => {
|
|
1036
1243
|
console.log();
|
|
1037
|
-
p4.intro(
|
|
1244
|
+
p4.intro(pc5.cyan(`@computesdk/cli v${VERSION}`));
|
|
1038
1245
|
await ensureAuth();
|
|
1039
1246
|
if (!repo) {
|
|
1040
1247
|
p4.log.info("Usage: compute workspace create owner/repo --branch main");
|
|
1041
|
-
p4.outro(
|
|
1248
|
+
p4.outro(pc5.green("Done!"));
|
|
1042
1249
|
return;
|
|
1043
1250
|
}
|
|
1044
1251
|
const provider = await resolveProvider(options.provider, { interactive: true });
|
|
@@ -1051,33 +1258,33 @@ workspaceCmd.command("create").description("Create a new workspace").argument("[
|
|
|
1051
1258
|
branch: options.branch,
|
|
1052
1259
|
provider
|
|
1053
1260
|
});
|
|
1054
|
-
spinner4.stop(`Workspace ready: ${
|
|
1261
|
+
spinner4.stop(`Workspace ready: ${pc5.cyan(workspace.id)}`);
|
|
1055
1262
|
if (options.attach !== false) {
|
|
1056
1263
|
console.log();
|
|
1057
1264
|
await workspaceAttach(workspace.id);
|
|
1058
1265
|
} else {
|
|
1059
|
-
p4.outro(
|
|
1266
|
+
p4.outro(pc5.green("Workspace created!"));
|
|
1060
1267
|
}
|
|
1061
1268
|
} catch (error) {
|
|
1062
|
-
spinner4.stop(
|
|
1269
|
+
spinner4.stop(pc5.red("Failed to create workspace"));
|
|
1063
1270
|
p4.log.error(error.message);
|
|
1064
1271
|
process.exit(1);
|
|
1065
1272
|
}
|
|
1066
1273
|
});
|
|
1067
1274
|
workspaceCmd.command("list").alias("ls").description("List all workspaces").action(async () => {
|
|
1068
1275
|
console.log();
|
|
1069
|
-
p4.intro(
|
|
1276
|
+
p4.intro(pc5.cyan(`@computesdk/cli v${VERSION}`));
|
|
1070
1277
|
await workspaceList();
|
|
1071
1278
|
});
|
|
1072
1279
|
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) => {
|
|
1073
1280
|
console.log();
|
|
1074
|
-
p4.intro(
|
|
1281
|
+
p4.intro(pc5.cyan(`@computesdk/cli v${VERSION}`));
|
|
1075
1282
|
if (!id) {
|
|
1076
1283
|
const state = loadWorkspaceState();
|
|
1077
1284
|
const workspaceIds = Object.keys(state.workspaces);
|
|
1078
1285
|
if (workspaceIds.length === 0) {
|
|
1079
1286
|
p4.log.info("No workspaces to destroy.");
|
|
1080
|
-
p4.outro(
|
|
1287
|
+
p4.outro(pc5.green("Done!"));
|
|
1081
1288
|
return;
|
|
1082
1289
|
}
|
|
1083
1290
|
const choices = workspaceIds.map((key) => {
|
|
@@ -1088,7 +1295,7 @@ workspaceCmd.command("destroy").alias("rm").description("Destroy a workspace").a
|
|
|
1088
1295
|
const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
|
|
1089
1296
|
return {
|
|
1090
1297
|
value: key,
|
|
1091
|
-
label: `${
|
|
1298
|
+
label: `${pc5.cyan(key)} (${ws.provider}, last used ${ageStr})`
|
|
1092
1299
|
};
|
|
1093
1300
|
});
|
|
1094
1301
|
const selected = await p4.multiselect({
|
|
@@ -1108,14 +1315,14 @@ workspaceCmd.command("destroy").alias("rm").description("Destroy a workspace").a
|
|
|
1108
1315
|
});
|
|
1109
1316
|
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) => {
|
|
1110
1317
|
console.log();
|
|
1111
|
-
p4.intro(
|
|
1318
|
+
p4.intro(pc5.cyan(`@computesdk/cli v${VERSION}`));
|
|
1112
1319
|
if (!id) {
|
|
1113
1320
|
const state = loadWorkspaceState();
|
|
1114
1321
|
const workspaceIds = Object.keys(state.workspaces);
|
|
1115
1322
|
if (workspaceIds.length === 0) {
|
|
1116
1323
|
p4.log.info("No workspaces found.");
|
|
1117
1324
|
p4.log.info("Create one with: compute create owner/repo");
|
|
1118
|
-
p4.outro(
|
|
1325
|
+
p4.outro(pc5.green("Done!"));
|
|
1119
1326
|
return;
|
|
1120
1327
|
}
|
|
1121
1328
|
const choices = workspaceIds.map((key) => {
|
|
@@ -1126,7 +1333,7 @@ workspaceCmd.command("connect").alias("attach").description("Connect to an exist
|
|
|
1126
1333
|
const ageStr = age === 0 ? "today" : age === 1 ? "1 day ago" : `${age} days ago`;
|
|
1127
1334
|
return {
|
|
1128
1335
|
value: key,
|
|
1129
|
-
label: `${
|
|
1336
|
+
label: `${pc5.cyan(key)} (${ws.provider}, last used ${ageStr})`
|
|
1130
1337
|
};
|
|
1131
1338
|
});
|
|
1132
1339
|
const selected = await p4.select({
|
|
@@ -1147,11 +1354,11 @@ program.command("run").description("Create a sandbox, run a command, then option
|
|
|
1147
1354
|
configureCompute(provider);
|
|
1148
1355
|
let sandbox;
|
|
1149
1356
|
try {
|
|
1150
|
-
sandbox = await
|
|
1357
|
+
sandbox = await compute4.sandbox.create(
|
|
1151
1358
|
buildCreateOptions(opts)
|
|
1152
1359
|
);
|
|
1153
1360
|
} catch (error) {
|
|
1154
|
-
console.error(
|
|
1361
|
+
console.error(pc5.red(`Failed to create sandbox: ${error.message}`));
|
|
1155
1362
|
process.exit(1);
|
|
1156
1363
|
}
|
|
1157
1364
|
try {
|
|
@@ -1174,35 +1381,35 @@ program.command("run").description("Create a sandbox, run a command, then option
|
|
|
1174
1381
|
} catch {
|
|
1175
1382
|
}
|
|
1176
1383
|
}
|
|
1177
|
-
console.error(
|
|
1384
|
+
console.error(pc5.red(error.message));
|
|
1178
1385
|
process.exit(1);
|
|
1179
1386
|
}
|
|
1180
1387
|
});
|
|
1181
1388
|
program.command("providers").description("List all providers and their configuration status").action(async () => {
|
|
1182
1389
|
console.log();
|
|
1183
|
-
p4.intro(
|
|
1390
|
+
p4.intro(pc5.cyan(`@computesdk/cli v${VERSION}`));
|
|
1184
1391
|
const statuses = getProviderStatus();
|
|
1185
1392
|
console.log();
|
|
1186
1393
|
for (const status of statuses) {
|
|
1187
|
-
const icon = status.ready ?
|
|
1188
|
-
const name = status.ready ?
|
|
1189
|
-
const detail = status.ready ?
|
|
1394
|
+
const icon = status.ready ? pc5.green("\u25CF") : pc5.gray("\u25CB");
|
|
1395
|
+
const name = status.ready ? pc5.white(status.name) : pc5.gray(status.name);
|
|
1396
|
+
const detail = status.ready ? pc5.green("ready") : pc5.gray(`missing: ${status.missing.join(", ")}`);
|
|
1190
1397
|
console.log(` ${icon} ${name} ${detail}`);
|
|
1191
1398
|
}
|
|
1192
1399
|
console.log();
|
|
1193
1400
|
});
|
|
1194
1401
|
program.command("login").description("Authenticate with ComputeSDK").action(async () => {
|
|
1195
1402
|
console.log();
|
|
1196
|
-
p4.intro(
|
|
1403
|
+
p4.intro(pc5.cyan(`@computesdk/cli v${VERSION}`));
|
|
1197
1404
|
await ensureAuth({ forceLogin: true });
|
|
1198
|
-
p4.outro(
|
|
1405
|
+
p4.outro(pc5.green("Authenticated!"));
|
|
1199
1406
|
});
|
|
1200
1407
|
program.command("logout").description("Clear stored credentials").action(() => {
|
|
1201
1408
|
console.log();
|
|
1202
|
-
p4.intro(
|
|
1409
|
+
p4.intro(pc5.cyan(`@computesdk/cli v${VERSION}`));
|
|
1203
1410
|
clearStoredCredentials();
|
|
1204
1411
|
p4.log.success("Logged out. Stored credentials removed.");
|
|
1205
|
-
p4.outro(
|
|
1412
|
+
p4.outro(pc5.green("Done!"));
|
|
1206
1413
|
});
|
|
1207
1414
|
function buildCreateOptions(opts) {
|
|
1208
1415
|
const options = {};
|
|
@@ -1271,7 +1478,7 @@ function parseDuration(input) {
|
|
|
1271
1478
|
return total;
|
|
1272
1479
|
}
|
|
1273
1480
|
program.parseAsync(process.argv).catch((error) => {
|
|
1274
|
-
console.error(
|
|
1481
|
+
console.error(pc5.red(`
|
|
1275
1482
|
Error: ${error.message}`));
|
|
1276
1483
|
process.exit(1);
|
|
1277
1484
|
});
|