@miosa/cli 1.0.95 → 1.1.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/README.md +83 -17
- package/dist/bin/miosa.js +5 -1
- package/dist/bin/miosa.js.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +6 -5
- package/dist/client.js.map +1 -1
- package/dist/commands/cloud.d.ts +3 -0
- package/dist/commands/cloud.d.ts.map +1 -0
- package/dist/commands/cloud.js +192 -0
- package/dist/commands/cloud.js.map +1 -0
- package/dist/commands/connect.d.ts +4 -1
- package/dist/commands/connect.d.ts.map +1 -1
- package/dist/commands/connect.js +46 -15
- package/dist/commands/connect.js.map +1 -1
- package/dist/commands/databases.d.ts.map +1 -1
- package/dist/commands/databases.js +3 -2
- package/dist/commands/databases.js.map +1 -1
- package/dist/commands/db.js +1 -1
- package/dist/commands/desktop.d.ts.map +1 -1
- package/dist/commands/desktop.js +2 -1
- package/dist/commands/desktop.js.map +1 -1
- package/dist/commands/hosts.d.ts +5 -1
- package/dist/commands/hosts.d.ts.map +1 -1
- package/dist/commands/hosts.js +5 -4
- package/dist/commands/hosts.js.map +1 -1
- package/dist/commands/mcp.d.ts +25 -0
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +26 -11
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/opencomputers.d.ts +10 -0
- package/dist/commands/opencomputers.d.ts.map +1 -0
- package/dist/commands/opencomputers.js +25 -0
- package/dist/commands/opencomputers.js.map +1 -0
- package/dist/commands/sandbox.d.ts.map +1 -1
- package/dist/commands/sandbox.js +84 -22
- package/dist/commands/sandbox.js.map +1 -1
- package/dist/commands/shell.d.ts.map +1 -1
- package/dist/commands/shell.js +3 -2
- package/dist/commands/shell.js.map +1 -1
- package/dist/commands/storage.d.ts.map +1 -1
- package/dist/commands/storage.js +3 -2
- package/dist/commands/storage.js.map +1 -1
- package/dist/commands/templates.d.ts.map +1 -1
- package/dist/commands/templates.js +94 -0
- package/dist/commands/templates.js.map +1 -1
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +5 -0
- package/dist/version.js.map +1 -0
- package/package.json +4 -3
package/dist/commands/sandbox.js
CHANGED
|
@@ -23,6 +23,13 @@ import { registerSandboxDevCommands, runFullSandboxDoctor, } from "./sandbox-dev
|
|
|
23
23
|
const DEFAULT_INTERACTIVE_SANDBOX_TIMEOUT_SEC = 3_600;
|
|
24
24
|
const DEFAULT_CREATE_WAIT_TIMEOUT_SEC = 120;
|
|
25
25
|
const EXPIRING_SANDBOX_THRESHOLD_SEC = 5 * 60;
|
|
26
|
+
const SANDBOX_SIZE_CONTRACTS = {
|
|
27
|
+
xs: { cpu: 1, memory: 2_048, disk: 10_240 },
|
|
28
|
+
small: { cpu: 2, memory: 4_096, disk: 10_240 },
|
|
29
|
+
medium: { cpu: 4, memory: 8_192, disk: 20_480 },
|
|
30
|
+
large: { cpu: 8, memory: 16_384, disk: 40_960 },
|
|
31
|
+
xl: { cpu: 16, memory: 32_768, disk: 81_920 },
|
|
32
|
+
};
|
|
26
33
|
export function register(program) {
|
|
27
34
|
// -------------------------------------------------------------------------
|
|
28
35
|
// sandbox / sandboxes command group — built manually to avoid subcommand
|
|
@@ -67,7 +74,7 @@ export function register(program) {
|
|
|
67
74
|
{ header: "NAME", key: "name" },
|
|
68
75
|
{
|
|
69
76
|
header: "STATUS",
|
|
70
|
-
key: "
|
|
77
|
+
key: "state",
|
|
71
78
|
color: (val) => statusColor(val.trim()),
|
|
72
79
|
},
|
|
73
80
|
{
|
|
@@ -116,7 +123,7 @@ export function register(program) {
|
|
|
116
123
|
{ label: "Name", value: str(sb["name"]) },
|
|
117
124
|
{
|
|
118
125
|
label: "Status",
|
|
119
|
-
value: statusColor(str(sb["
|
|
126
|
+
value: statusColor(str(sb["state"])),
|
|
120
127
|
},
|
|
121
128
|
];
|
|
122
129
|
if (sb["template_id"]) {
|
|
@@ -174,10 +181,13 @@ export function register(program) {
|
|
|
174
181
|
.description("Create a new Sandbox")
|
|
175
182
|
.option("--template <template>", "Template / image ID (default: miosa-sandbox)")
|
|
176
183
|
.option("--name <name>", "Human-readable name for the Sandbox")
|
|
184
|
+
.option("--size <size>", "Named size: xs, small, medium, large, or xl (default: small)", parseSandboxSize)
|
|
177
185
|
.option("--cpu <n>", "vCPU count", parseIntegerOption)
|
|
178
186
|
.option("--memory <size>", "Memory size, e.g. 4096mb or 4gb", parseSizeMb)
|
|
179
187
|
.option("--disk <size>", "Disk size, e.g. 10240mb or 10gb", parseSizeMb)
|
|
180
188
|
.option("--timeout <duration>", "Wall-clock timeout, e.g. 300s, 1h", parseDurationSec)
|
|
189
|
+
.option("--idle-timeout <duration>", "Idle timeout before pause; 0 disables it (default: 0)", parseDurationSec)
|
|
190
|
+
.option("--idempotency-key <key>", "Retry-safe create key retained by the service for 24 hours")
|
|
181
191
|
.option("--publish-port <port>", "Expose this port after create", parseIntegerOption)
|
|
182
192
|
.option("--wait", "Wait for sandbox running and published port readiness")
|
|
183
193
|
.option("--probe-path <path>", "HTTP path to probe when --publish-port is set", "/")
|
|
@@ -217,19 +227,21 @@ Note:
|
|
|
217
227
|
renderCreateSuccess(raw, Date.now() - t0);
|
|
218
228
|
return;
|
|
219
229
|
}
|
|
220
|
-
const body = {
|
|
221
|
-
|
|
222
|
-
|
|
230
|
+
const body = {
|
|
231
|
+
template_id: opts.template ?? "miosa-sandbox",
|
|
232
|
+
};
|
|
223
233
|
if (opts.name)
|
|
224
234
|
body["name"] = opts.name;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (
|
|
228
|
-
body["
|
|
229
|
-
|
|
230
|
-
body["disk_size_mb"] =
|
|
235
|
+
const resources = resolveSandboxResources(opts);
|
|
236
|
+
body["size"] = resources.size;
|
|
237
|
+
if (resources.legacy) {
|
|
238
|
+
body["cpu_count"] = resources.legacy.cpu;
|
|
239
|
+
body["memory_mb"] = resources.legacy.memory;
|
|
240
|
+
body["disk_size_mb"] = resources.legacy.disk;
|
|
241
|
+
}
|
|
231
242
|
const timeoutSec = opts.timeout ?? DEFAULT_INTERACTIVE_SANDBOX_TIMEOUT_SEC;
|
|
232
243
|
body["timeout_sec"] = timeoutSec;
|
|
244
|
+
body["idle_timeout_sec"] = opts.idleTimeout ?? 0;
|
|
233
245
|
if (opts.source)
|
|
234
246
|
body["source"] = opts.source;
|
|
235
247
|
if (opts.revision)
|
|
@@ -271,7 +283,9 @@ Note:
|
|
|
271
283
|
body["persistent"] = opts.nonPersistent ? false : true;
|
|
272
284
|
if (opts.autoStart)
|
|
273
285
|
body["auto_start"] = true;
|
|
274
|
-
const raw = unwrap(await client().apiPost(apiPath("/sandboxes"), body
|
|
286
|
+
const raw = unwrap(await client().apiPost(apiPath("/sandboxes"), body, opts.idempotencyKey
|
|
287
|
+
? { "Idempotency-Key": opts.idempotencyKey }
|
|
288
|
+
: undefined));
|
|
275
289
|
const sb = (raw ?? {});
|
|
276
290
|
const id = String(sb["id"] ?? "");
|
|
277
291
|
if (opts.publishPort != null && id) {
|
|
@@ -307,14 +321,16 @@ Note:
|
|
|
307
321
|
// delete
|
|
308
322
|
sandbox
|
|
309
323
|
.command("delete <sandbox-id>")
|
|
310
|
-
.description("
|
|
324
|
+
.description("Permanently delete a Sandbox (legacy API extension)")
|
|
325
|
+
.requiredOption("--force", "Confirm permanent filesystem deletion")
|
|
311
326
|
.option("--json", "Output as JSON")
|
|
312
327
|
.action((id, opts) => runAction(() => deleteAndPrint(`/sandboxes/${enc(id)}`, opts)));
|
|
313
328
|
// destroy / rm — aliases for delete
|
|
314
329
|
sandbox
|
|
315
330
|
.command("destroy <sandbox-id>")
|
|
316
331
|
.alias("rm")
|
|
317
|
-
.description("
|
|
332
|
+
.description("Permanently destroy a Sandbox (legacy API extension)")
|
|
333
|
+
.requiredOption("--force", "Confirm permanent filesystem deletion")
|
|
318
334
|
.option("--json", "Output as JSON")
|
|
319
335
|
.action((id, opts) => runAction(() => deleteAndPrint(`/sandboxes/${enc(id)}`, opts)));
|
|
320
336
|
// action — lifecycle operations: pause, resume
|
|
@@ -329,6 +345,11 @@ Note:
|
|
|
329
345
|
}
|
|
330
346
|
await postAndPrint(`/sandboxes/${enc(id)}/${enc(action)}`, opts, {});
|
|
331
347
|
}));
|
|
348
|
+
sandbox
|
|
349
|
+
.command("pause <sandbox-id>")
|
|
350
|
+
.description("Pause a running persistent Sandbox and preserve its workspace")
|
|
351
|
+
.option("--json", "Output as JSON")
|
|
352
|
+
.action((id, opts) => runAction(() => postAndPrint(`/sandboxes/${enc(id)}/pause`, opts, {})));
|
|
332
353
|
// stop — snapshot + pause (mirrors `box stop`)
|
|
333
354
|
sandbox
|
|
334
355
|
.command("stop <sandbox-id>")
|
|
@@ -336,7 +357,7 @@ Note:
|
|
|
336
357
|
.option("--no-snapshot", "Deprecated compatibility flag; stop still preserves state server-side")
|
|
337
358
|
.option("--json", "Output as JSON")
|
|
338
359
|
.action((id, opts) => runAction(async () => {
|
|
339
|
-
const stopped = unwrap(await client().apiPost(apiPath(`/sandboxes/${enc(id)}/
|
|
360
|
+
const stopped = unwrap(await client().apiPost(apiPath(`/sandboxes/${enc(id)}/pause`), {}));
|
|
340
361
|
if (isJsonMode(opts)) {
|
|
341
362
|
console.log(JSON.stringify(stopped, null, 2));
|
|
342
363
|
return;
|
|
@@ -357,19 +378,27 @@ Note:
|
|
|
357
378
|
sandbox
|
|
358
379
|
.command("resume <sandbox-id>")
|
|
359
380
|
.description("Resume a paused Sandbox")
|
|
381
|
+
.option("--idempotency-key <key>", "Retry-safe lifecycle key sent as Idempotency-Key")
|
|
360
382
|
.option("--json", "Output as JSON")
|
|
361
383
|
.action((id, opts) => runAction(() => resumeSandboxAndPrint(id, opts)));
|
|
362
384
|
// fork — clone from snapshot in one call (mirrors `box fork`)
|
|
363
385
|
sandbox
|
|
364
386
|
.command("fork <sandbox-id>")
|
|
365
|
-
.description("
|
|
366
|
-
.option("--
|
|
387
|
+
.description("Snapshot and fork a running Sandbox into a new Sandbox")
|
|
388
|
+
.option("--template <template>", "Template or immutable image override")
|
|
389
|
+
.option("--timeout <duration>", "Fork timeout", parseDurationSec)
|
|
390
|
+
.option("--idempotency-key <key>", "Retry-safe fork key sent as Idempotency-Key")
|
|
367
391
|
.option("--json", "Output as JSON")
|
|
368
392
|
.action((id, opts) => runAction(async () => {
|
|
369
393
|
const body = {};
|
|
370
|
-
if (opts.
|
|
371
|
-
body["
|
|
372
|
-
|
|
394
|
+
if (opts.template)
|
|
395
|
+
body["template_id"] = opts.template;
|
|
396
|
+
if (opts.timeout != null)
|
|
397
|
+
body["timeout_sec"] = opts.timeout;
|
|
398
|
+
const result = unwrap(await client().apiPost(apiPath(`/sandboxes/${enc(id)}/fork`), body, opts.idempotencyKey
|
|
399
|
+
? { "Idempotency-Key": opts.idempotencyKey }
|
|
400
|
+
: undefined));
|
|
401
|
+
printValue(result, opts);
|
|
373
402
|
}));
|
|
374
403
|
// desktop — open the Sandbox's web desktop URL (mirrors `box desktop`).
|
|
375
404
|
// Sandboxes are headless by default; only templates that expose a desktop
|
|
@@ -2293,7 +2322,7 @@ function parsePublishDatabase(value) {
|
|
|
2293
2322
|
normalized === "postgresql" ||
|
|
2294
2323
|
normalized === "create:postgres" ||
|
|
2295
2324
|
normalized === "create:postgresql") {
|
|
2296
|
-
return { engine: "postgresql", engine_version: "
|
|
2325
|
+
return { engine: "postgresql", engine_version: "16" };
|
|
2297
2326
|
}
|
|
2298
2327
|
if (normalized.startsWith("existing:")) {
|
|
2299
2328
|
return { existing_database_id: value.slice("existing:".length) };
|
|
@@ -2603,7 +2632,10 @@ async function waitForSandboxRunning(c, sandboxId, timeoutSec) {
|
|
|
2603
2632
|
}
|
|
2604
2633
|
async function resumeSandboxAndPrint(sandboxId, opts) {
|
|
2605
2634
|
try {
|
|
2606
|
-
await
|
|
2635
|
+
const result = unwrap(await client().apiPost(apiPath(`/sandboxes/${enc(sandboxId)}/resume`), {}, opts.idempotencyKey
|
|
2636
|
+
? { "Idempotency-Key": opts.idempotencyKey }
|
|
2637
|
+
: undefined));
|
|
2638
|
+
printValue(result, opts);
|
|
2607
2639
|
}
|
|
2608
2640
|
catch (err) {
|
|
2609
2641
|
if (err instanceof ApiResponseError && err.code === "SANDBOX_NOT_PAUSED") {
|
|
@@ -3387,6 +3419,36 @@ function parseIntegerOption(value) {
|
|
|
3387
3419
|
throw new UserError(`Invalid integer: ${value}`);
|
|
3388
3420
|
return n;
|
|
3389
3421
|
}
|
|
3422
|
+
function parseSandboxSize(value) {
|
|
3423
|
+
const size = String(value).trim().toLowerCase();
|
|
3424
|
+
if (size in SANDBOX_SIZE_CONTRACTS)
|
|
3425
|
+
return size;
|
|
3426
|
+
throw new UserError(`Invalid sandbox size: ${value}. Expected xs, small, medium, large, or xl.`);
|
|
3427
|
+
}
|
|
3428
|
+
function resolveSandboxResources(opts) {
|
|
3429
|
+
const legacyValues = [opts.cpu, opts.memory, opts.disk];
|
|
3430
|
+
const legacyCount = legacyValues.filter((value) => value != null).length;
|
|
3431
|
+
if (legacyCount === 0)
|
|
3432
|
+
return { size: opts.size ?? "small" };
|
|
3433
|
+
if (legacyCount !== legacyValues.length) {
|
|
3434
|
+
throw new UserError("Legacy resource overrides require --cpu, --memory, and --disk together. Prefer --size.");
|
|
3435
|
+
}
|
|
3436
|
+
const legacy = {
|
|
3437
|
+
cpu: opts.cpu,
|
|
3438
|
+
memory: opts.memory,
|
|
3439
|
+
disk: opts.disk,
|
|
3440
|
+
};
|
|
3441
|
+
const matchingSize = Object.entries(SANDBOX_SIZE_CONTRACTS).find(([, contract]) => contract.cpu === legacy.cpu &&
|
|
3442
|
+
contract.memory === legacy.memory &&
|
|
3443
|
+
contract.disk === legacy.disk)?.[0];
|
|
3444
|
+
if (!matchingSize) {
|
|
3445
|
+
throw new UserError("Legacy --cpu/--memory/--disk values must exactly match a named sandbox size. Prefer --size.");
|
|
3446
|
+
}
|
|
3447
|
+
if (opts.size && opts.size !== matchingSize) {
|
|
3448
|
+
throw new UserError(`Legacy resource overrides match ${matchingSize}, not requested size ${opts.size}.`);
|
|
3449
|
+
}
|
|
3450
|
+
return { size: matchingSize, legacy };
|
|
3451
|
+
}
|
|
3390
3452
|
function parseSizeMb(value) {
|
|
3391
3453
|
const match = String(value)
|
|
3392
3454
|
.trim()
|