@neta-art/cohub-cli 10.4.3 → 10.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -282,6 +282,20 @@ cohub -s <spaceId> spaces activity 7
|
|
|
282
282
|
cohub -s <spaceId> spaces activity 365 --json
|
|
283
283
|
```
|
|
284
284
|
|
|
285
|
+
## Space wallet
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
cohub -s <spaceId> spaces wallet balance
|
|
289
|
+
cohub -s <spaceId> spaces wallet catalog
|
|
290
|
+
cohub -s <spaceId> spaces wallet recharge <productKey> --request-id <uuid>
|
|
291
|
+
cohub -s <spaceId> spaces wallet confirm <orderId>
|
|
292
|
+
cohub -s <spaceId> spaces wallet activity --page 1 --json
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Recharge prints a payment URL. Reuse the request UUID when retrying the same
|
|
296
|
+
recharge. Any signed-in user with access to the Space may contribute; payment
|
|
297
|
+
does not grant additional permissions. Transfers and withdrawals are not supported.
|
|
298
|
+
|
|
285
299
|
## Boards
|
|
286
300
|
|
|
287
301
|
A Board is one JSON document with three parts — `board` settings, an `items` map,
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createClient } from "../client.js";
|
|
2
|
+
import { handleHttp, json as outJson, jsonRequested, table } from "../output.js";
|
|
3
|
+
import { resolveSpace } from "../space.js";
|
|
4
|
+
export function registerSpaceWallet(spaces) {
|
|
5
|
+
const wallet = spaces.command("wallet").description("Space wallet balance, recharge, and activity");
|
|
6
|
+
wallet.command("balance").option("--json", "Output as JSON").action(async (opts) => {
|
|
7
|
+
try {
|
|
8
|
+
const value = await createClient().space(await resolveSpace(spaces)).wallet.get();
|
|
9
|
+
if (jsonRequested(opts))
|
|
10
|
+
return outJson(value);
|
|
11
|
+
table([{ balance: `$${value.balanceUsd.toFixed(8)}`, usable: value.usable }], [{ key: "balance", label: "Balance" }, { key: "usable", label: "Usable" }]);
|
|
12
|
+
}
|
|
13
|
+
catch (cause) {
|
|
14
|
+
handleHttp(cause);
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
wallet.command("catalog").option("--json", "Output as JSON").action(async (opts) => {
|
|
18
|
+
try {
|
|
19
|
+
const value = await createClient().space(await resolveSpace(spaces)).wallet.catalog();
|
|
20
|
+
if (jsonRequested(opts))
|
|
21
|
+
return outJson(value);
|
|
22
|
+
table(value.products.map((product) => ({ key: product.key, name: product.name, price: product.pricing.amountUsd })), [{ key: "key", label: "Product" }, { key: "name", label: "Name" }, { key: "price", label: "Price (USD)" }]);
|
|
23
|
+
}
|
|
24
|
+
catch (cause) {
|
|
25
|
+
handleHttp(cause);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
wallet.command("activity").option("--page <page>", "Page number", "1").option("--json", "Output as JSON").action(async (opts) => {
|
|
29
|
+
try {
|
|
30
|
+
const page = Number(opts.page);
|
|
31
|
+
if (!Number.isInteger(page) || page < 1 || page > 10_000)
|
|
32
|
+
throw new Error("Page must be an integer between 1 and 10000");
|
|
33
|
+
const value = await createClient().space(await resolveSpace(spaces)).wallet.activities(page);
|
|
34
|
+
if (jsonRequested(opts))
|
|
35
|
+
return outJson(value);
|
|
36
|
+
table(value.items, [{ key: "kind", label: "Activity" }, { key: "amountUsd", label: "Amount (USD)" }, { key: "actorUserId", label: "User" }, { key: "createdAt", label: "Date" }]);
|
|
37
|
+
}
|
|
38
|
+
catch (cause) {
|
|
39
|
+
handleHttp(cause);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
wallet.command("recharge <product>").requiredOption("--request-id <uuid>", "Stable UUID; reuse when retrying this recharge").option("--json", "Output as JSON").action(async (productKey, opts) => {
|
|
43
|
+
try {
|
|
44
|
+
const value = await createClient().space(await resolveSpace(spaces)).wallet.createOrder({ id: opts.requestId, productKey });
|
|
45
|
+
if (jsonRequested(opts))
|
|
46
|
+
return outJson(value);
|
|
47
|
+
if (!value.checkout.checkoutUsable || !value.checkout.checkoutUrl)
|
|
48
|
+
throw new Error(value.checkout.message ?? "Checkout is unavailable");
|
|
49
|
+
console.log(value.checkout.checkoutUrl);
|
|
50
|
+
console.log(`Order: ${value.orderId}`);
|
|
51
|
+
}
|
|
52
|
+
catch (cause) {
|
|
53
|
+
handleHttp(cause);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
wallet.command("confirm <order>").action(async (orderId) => {
|
|
57
|
+
try {
|
|
58
|
+
outJson(await createClient().space(await resolveSpace(spaces)).wallet.confirmOrder(orderId));
|
|
59
|
+
}
|
|
60
|
+
catch (cause) {
|
|
61
|
+
handleHttp(cause);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { CohubHttpClient, CreateSpaceInput } from "@neta-art/cohub";
|
|
2
2
|
import type { Command } from "commander";
|
|
3
|
+
import { createClient } from "../client.js";
|
|
3
4
|
import { type LocalUploadEntry } from "../space-files/upload.js";
|
|
4
5
|
export type SpaceCreateOptions = {
|
|
5
6
|
name: string;
|
|
6
7
|
description?: string;
|
|
7
8
|
checkpoint?: string;
|
|
9
|
+
image?: string;
|
|
8
10
|
autoDestroy?: string;
|
|
9
11
|
idleTtl?: string;
|
|
10
12
|
spec?: string;
|
|
@@ -17,4 +19,7 @@ export declare function registerPrompt(program: Command): void;
|
|
|
17
19
|
export declare function registerSpaceCreate(spacesCmd: Command, dependencies?: {
|
|
18
20
|
createClient?: () => CohubHttpClient;
|
|
19
21
|
}): Command;
|
|
22
|
+
export declare function registerSpaceSetup(spacesCmd: Command, deps?: {
|
|
23
|
+
createClient?: typeof createClient;
|
|
24
|
+
}): void;
|
|
20
25
|
export declare function registerSpaces(program: Command): void;
|
package/dist/commands/spaces.js
CHANGED
|
@@ -11,13 +11,14 @@ import { uploadLocalFiles } from "../space-files/upload.js";
|
|
|
11
11
|
import { registerDecisionCommand } from "./decision.js";
|
|
12
12
|
import { registerSpaceCommerce } from "./space-commerce.js";
|
|
13
13
|
import { registerSpaceFileTransfer } from "./space-files.js";
|
|
14
|
+
import { registerSpaceWallet } from "./space-wallet.js";
|
|
14
15
|
import { registerSpaceActivity } from "./space-activity.js";
|
|
15
16
|
import { registerSpaceDisplays } from "./space-displays.js";
|
|
16
17
|
import { registerSpaceInvitations } from "./space-invitations.js";
|
|
17
18
|
import { registerSpaceTurns } from "./space-turns.js";
|
|
18
19
|
import { registerSpaceWebhooks } from "./space-webhooks.js";
|
|
19
20
|
const cliEnv = resolveCohubEnvironment();
|
|
20
|
-
const defaultIdleTtlSeconds = cliEnv === "prod" ?
|
|
21
|
+
const defaultIdleTtlSeconds = cliEnv === "prod" ? 60 * 60 : 5 * 60;
|
|
21
22
|
const SPACE_ROLES = ["host", "builder", "guest"];
|
|
22
23
|
const SANDBOX_SPEC_IDS = ["standard", "boost", "ultra"];
|
|
23
24
|
const LABEL_RESOURCE_TYPES = ["session", "checkpoint", "file"];
|
|
@@ -75,6 +76,7 @@ const parseSandboxSpec = (value) => value ? parseChoice(value, "sandbox spec", S
|
|
|
75
76
|
export function buildSpaceCreateInput(opts) {
|
|
76
77
|
const autoDestroy = parseAutoDestroy(opts);
|
|
77
78
|
const spec = parseSandboxSpec(opts.spec);
|
|
79
|
+
const image = opts.image ? parseChoice(opts.image, "sandbox image", ["default", "game"]) : undefined;
|
|
78
80
|
const checkpointId = opts.checkpoint?.trim();
|
|
79
81
|
if (opts.checkpoint !== undefined && !checkpointId) {
|
|
80
82
|
return error("Invalid checkpoint", "Checkpoint ID is required");
|
|
@@ -85,12 +87,13 @@ export function buildSpaceCreateInput(opts) {
|
|
|
85
87
|
...(checkpointId
|
|
86
88
|
? { bootstrapSource: { type: "checkpoint", checkpointId } }
|
|
87
89
|
: {}),
|
|
88
|
-
...((autoDestroy || spec)
|
|
90
|
+
...((autoDestroy || spec || image)
|
|
89
91
|
? {
|
|
90
92
|
config: {
|
|
91
93
|
sandbox: {
|
|
92
94
|
...(autoDestroy ? { autoDestroy } : {}),
|
|
93
95
|
...(spec ? { spec } : {}),
|
|
96
|
+
...(image ? { image } : {}),
|
|
94
97
|
},
|
|
95
98
|
},
|
|
96
99
|
}
|
|
@@ -376,6 +379,7 @@ export function registerSpaceCreate(spacesCmd, dependencies = {}) {
|
|
|
376
379
|
.requiredOption("-n, --name <name>", "Space name")
|
|
377
380
|
.option("-d, --description <desc>", "Space description")
|
|
378
381
|
.option("--checkpoint <id>", "Create from a checkpoint")
|
|
382
|
+
.option("--image <image>", "Sandbox image: default or game")
|
|
379
383
|
.option("--auto-destroy <mode>", "Sandbox auto destroy mode: idle or never")
|
|
380
384
|
.option("--idle-ttl <seconds>", "Idle auto destroy TTL in seconds, max 2592000 (30d)")
|
|
381
385
|
.option("--spec <spec>", "Sandbox spec: standard, boost, or ultra")
|
|
@@ -454,6 +458,40 @@ function registerUserLabels(program) {
|
|
|
454
458
|
}
|
|
455
459
|
});
|
|
456
460
|
}
|
|
461
|
+
export function registerSpaceSetup(spacesCmd, deps = {}) {
|
|
462
|
+
spacesCmd
|
|
463
|
+
.command("setup [id]")
|
|
464
|
+
.description("Show cloud sandbox startup scripts, results, and log paths")
|
|
465
|
+
.option("--json", "Output as JSON")
|
|
466
|
+
.action(async (id, opts) => {
|
|
467
|
+
try {
|
|
468
|
+
const spaceId = id ?? await resolveSpace(spacesCmd);
|
|
469
|
+
const { sandbox } = await (deps.createClient ?? createClient)().space(spaceId).sandbox.get();
|
|
470
|
+
const setup = sandbox?.meta?.setup ?? null;
|
|
471
|
+
if (jsonRequested(opts))
|
|
472
|
+
return outJson({ provider: sandbox?.provider ?? null, setup });
|
|
473
|
+
if (sandbox?.provider === "local")
|
|
474
|
+
return ok("Setup is disabled for local spaces.");
|
|
475
|
+
if (!setup)
|
|
476
|
+
return ok("No setup report is available.");
|
|
477
|
+
table([
|
|
478
|
+
{ key: "Status", value: setup.status ?? (setup.error || setup.exitCode ? "failed" : "succeeded") },
|
|
479
|
+
{ key: "Duration", value: setup.duration || "—" },
|
|
480
|
+
...(setup.error ? [{ key: "Error", value: setup.error }] : []),
|
|
481
|
+
], [{ key: "key", label: "Key" }, { key: "value", label: "Value" }]);
|
|
482
|
+
if (setup.steps?.length)
|
|
483
|
+
table(setup.steps.map((step) => ({
|
|
484
|
+
script: step.path, status: step.status, duration: step.duration || "—", logs: step.logDir,
|
|
485
|
+
})), [
|
|
486
|
+
{ key: "script", label: "Script" }, { key: "status", label: "Status" },
|
|
487
|
+
{ key: "duration", label: "Duration" }, { key: "logs", label: "Logs" },
|
|
488
|
+
]);
|
|
489
|
+
}
|
|
490
|
+
catch (e) {
|
|
491
|
+
handleHttp(e);
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
}
|
|
457
495
|
export function registerSpaces(program) {
|
|
458
496
|
const spacesCmd = program.command("spaces").description("Space management");
|
|
459
497
|
registerSpaceInvitations(spacesCmd);
|
|
@@ -615,21 +653,24 @@ export function registerSpaces(program) {
|
|
|
615
653
|
handleHttp(e);
|
|
616
654
|
}
|
|
617
655
|
});
|
|
656
|
+
registerSpaceSetup(spacesCmd);
|
|
618
657
|
// ── spaces config ──
|
|
619
658
|
spacesCmd
|
|
620
659
|
.command("config <id>")
|
|
621
660
|
.description("Show or update space configuration")
|
|
622
661
|
.option("--auto-destroy <mode>", "Sandbox auto destroy mode: idle or never")
|
|
623
662
|
.option("--idle-ttl <seconds>", "Idle auto destroy TTL in seconds, max 2592000 (30d)")
|
|
624
|
-
.option("--spec <spec>", "Sandbox spec: standard, boost, or
|
|
663
|
+
.option("--spec <spec>", "Sandbox spec: standard, boost, ultra, or auto")
|
|
664
|
+
.option("--image <image>", "Sandbox image: default, game, or auto")
|
|
625
665
|
.option("--json", "Output as JSON")
|
|
626
666
|
.action(async (id, opts) => {
|
|
627
667
|
const client = createClient();
|
|
628
668
|
try {
|
|
629
669
|
const autoDestroy = parseAutoDestroy(opts);
|
|
630
|
-
const spec = parseSandboxSpec(opts.spec);
|
|
631
|
-
|
|
632
|
-
|
|
670
|
+
const spec = opts.spec === "auto" ? null : parseSandboxSpec(opts.spec);
|
|
671
|
+
const image = opts.image === "auto" ? null : opts.image ? parseChoice(opts.image, "sandbox image", ["default", "game"]) : undefined;
|
|
672
|
+
if (autoDestroy || spec !== undefined || image !== undefined) {
|
|
673
|
+
const result = await client.space(id).updateConfig({ sandbox: { ...(autoDestroy ? { autoDestroy } : {}), ...(spec !== undefined ? { spec } : {}), ...(image !== undefined ? { image } : {}) } });
|
|
633
674
|
if (jsonRequested(opts))
|
|
634
675
|
return outJson(result);
|
|
635
676
|
ok(`Space config updated${autoDestroy ? ` — sandbox auto destroy: ${formatAutoDestroy(autoDestroy)}` : ""}${spec ? ` — sandbox spec: ${spec}` : ""}${result.sandbox?.pendingRestart ? " — restart the sandbox to apply" : ""}`);
|
|
@@ -640,6 +681,11 @@ export function registerSpaces(program) {
|
|
|
640
681
|
return outJson(result);
|
|
641
682
|
table([
|
|
642
683
|
{ key: "sandbox.autoDestroy", value: formatAutoDestroy(result.config.sandbox.autoDestroy) },
|
|
684
|
+
{ key: "sandbox.image", value: result.config.sandbox.image ?? "default" },
|
|
685
|
+
{ key: "sandbox.imageSource", value: result.config.sandbox.preferences?.origins.image ?? "platform" },
|
|
686
|
+
{ key: "sandbox.specSource", value: result.config.sandbox.preferences?.origins.spec ?? "platform" },
|
|
687
|
+
{ key: "sandbox.warnings", value: result.config.sandbox.preferences?.warnings.join(", ") || "—" },
|
|
688
|
+
{ key: "sandbox.pendingRestart", value: String(result.config.sandbox.preferences?.pendingRestart ?? false) },
|
|
643
689
|
{ key: "sandbox.spec", value: result.config.sandbox.spec ?? "standard" },
|
|
644
690
|
{ key: "sandbox.appliedSpec", value: result.config.sandbox.appliedSpec ?? "—" },
|
|
645
691
|
{ key: "sandbox.allowedSpec", value: result.config.sandbox.allowedSpec ?? "standard" },
|
|
@@ -737,6 +783,7 @@ export function registerSpaces(program) {
|
|
|
737
783
|
// ── spaces commerce ──
|
|
738
784
|
registerSpaceCommerce(spacesCmd);
|
|
739
785
|
// ── spaces activity ──
|
|
786
|
+
registerSpaceWallet(spacesCmd);
|
|
740
787
|
registerSpaceActivity(spacesCmd);
|
|
741
788
|
// ── spaces usage ──
|
|
742
789
|
spacesCmd
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neta-art/cohub-cli",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.5.0",
|
|
4
4
|
"description": "CLI for Cohub — spaces, sessions, and agent collaboration.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"NOTICE"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@neta-art/cohub": "11.
|
|
21
|
+
"@neta-art/cohub": "11.4.0",
|
|
22
22
|
"@neta-art/generation": "^0.1.39",
|
|
23
23
|
"commander": "^15.0.0",
|
|
24
24
|
"sharp": "^0.35.4",
|