@neta-art/cohub-cli 1.7.0 → 1.8.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 +2 -0
- package/dist/auth.d.ts +3 -1
- package/dist/auth.js +2 -2
- package/dist/commands/auth.js +68 -13
- package/dist/commands/channels.js +3 -3
- package/dist/commands/cron-jobs.js +5 -3
- package/dist/commands/generations.js +40 -10
- package/dist/commands/models.js +93 -5
- package/dist/commands/profile.js +2 -2
- package/dist/commands/prompts.js +2 -2
- package/dist/commands/search.js +2 -2
- package/dist/commands/session-access.js +12 -4
- package/dist/commands/spaces.js +144 -107
- package/dist/commands/tasks.js +3 -3
- package/dist/output.d.ts +3 -0
- package/dist/output.js +3 -0
- package/dist/space.d.ts +2 -0
- package/dist/space.js +14 -0
- package/package.json +2 -2
package/dist/commands/spaces.js
CHANGED
|
@@ -2,21 +2,36 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import { createReadStream } from "node:fs";
|
|
3
3
|
import { readdir, stat } from "node:fs/promises";
|
|
4
4
|
import { basename, dirname, relative, resolve, sep } from "node:path";
|
|
5
|
-
import {
|
|
5
|
+
import { resolveCohubEnvironment } from "@neta-art/cohub";
|
|
6
6
|
import { uploadAvatarAsset } from "../avatar.js";
|
|
7
|
-
import {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const opts = current.opts();
|
|
12
|
-
if (opts.space)
|
|
13
|
-
return String(opts.space);
|
|
14
|
-
current = current.parent ?? null;
|
|
15
|
-
}
|
|
16
|
-
return error("Missing required option", "Add -s, --space <id> to target a space");
|
|
17
|
-
}
|
|
18
|
-
const cliEnv = (process.env.ENV === "prod" ? "prod" : "dev");
|
|
7
|
+
import { createClient } from "../client.js";
|
|
8
|
+
import { table, json as outJson, jsonRequested, ok, error, handleHttp } from "../output.js";
|
|
9
|
+
import { resolveSpace } from "../space.js";
|
|
10
|
+
const cliEnv = resolveCohubEnvironment();
|
|
19
11
|
const defaultIdleTtlSeconds = cliEnv === "prod" ? 12 * 60 * 60 : 10 * 60;
|
|
12
|
+
const SPACE_ROLES = ["host", "builder", "guest"];
|
|
13
|
+
function parseInteger(value, name, options = {}) {
|
|
14
|
+
if (!/^-?\d+$/.test(value.trim()))
|
|
15
|
+
return error(`Invalid ${name}`, `${name} must be an integer`);
|
|
16
|
+
const parsed = Number.parseInt(value, 10);
|
|
17
|
+
if (!Number.isSafeInteger(parsed))
|
|
18
|
+
return error(`Invalid ${name}`, `${name} must be a safe integer`);
|
|
19
|
+
if (options.min !== undefined && parsed < options.min)
|
|
20
|
+
return error(`Invalid ${name}`, `${name} must be at least ${options.min}`);
|
|
21
|
+
if (options.max !== undefined && parsed > options.max)
|
|
22
|
+
return error(`Invalid ${name}`, `${name} must be at most ${options.max}`);
|
|
23
|
+
return parsed;
|
|
24
|
+
}
|
|
25
|
+
function parseChoice(value, name, choices) {
|
|
26
|
+
if (choices.includes(value))
|
|
27
|
+
return value;
|
|
28
|
+
return error(`Invalid ${name}`, `Use one of: ${choices.join(", ")}`);
|
|
29
|
+
}
|
|
30
|
+
function parseNullableRole(value, name) {
|
|
31
|
+
if (value === undefined || value === "null")
|
|
32
|
+
return null;
|
|
33
|
+
return parseChoice(value, name, SPACE_ROLES);
|
|
34
|
+
}
|
|
20
35
|
const parseAutoDestroy = (opts) => {
|
|
21
36
|
const mode = opts.autoDestroy ?? (opts.idleTtl ? "idle" : undefined);
|
|
22
37
|
if (!mode)
|
|
@@ -25,10 +40,7 @@ const parseAutoDestroy = (opts) => {
|
|
|
25
40
|
return { mode: "never" };
|
|
26
41
|
if (mode !== "idle")
|
|
27
42
|
return error("Invalid auto destroy mode", "Use --auto-destroy idle or --auto-destroy never");
|
|
28
|
-
const ttlSeconds =
|
|
29
|
-
if (!Number.isSafeInteger(ttlSeconds) || ttlSeconds < 60 || ttlSeconds > 30 * 24 * 60 * 60) {
|
|
30
|
-
return error("Invalid idle TTL", "--idle-ttl must be an integer between 60 and 2592000 seconds");
|
|
31
|
-
}
|
|
43
|
+
const ttlSeconds = parseInteger(opts.idleTtl ?? String(defaultIdleTtlSeconds), "idle TTL", { min: 60, max: 30 * 24 * 60 * 60 });
|
|
32
44
|
return { mode: "idle", ttlSeconds };
|
|
33
45
|
};
|
|
34
46
|
const formatAutoDestroy = (policy) => {
|
|
@@ -93,7 +105,7 @@ async function putUploadEntry(entry, uploadUrl, headers) {
|
|
|
93
105
|
}
|
|
94
106
|
}
|
|
95
107
|
async function uploadFiles(command, paths, opts) {
|
|
96
|
-
const spaceId =
|
|
108
|
+
const spaceId = resolveSpace(command);
|
|
97
109
|
const client = createClient();
|
|
98
110
|
try {
|
|
99
111
|
const files = await collectUploadFiles(paths);
|
|
@@ -117,7 +129,7 @@ async function uploadFiles(command, paths, opts) {
|
|
|
117
129
|
const result = await client.space(spaceId).files.completeUpload(plan.uploadId, {
|
|
118
130
|
entries: plan.entries.map((entry) => ({ id: entry.id })),
|
|
119
131
|
});
|
|
120
|
-
if (opts
|
|
132
|
+
if (jsonRequested(opts))
|
|
121
133
|
return outJson({ ...result, uploadId: plan.uploadId, files: files.length });
|
|
122
134
|
ok(`Uploaded ${files.length} file${files.length === 1 ? "" : "s"}`);
|
|
123
135
|
}
|
|
@@ -125,6 +137,21 @@ async function uploadFiles(command, paths, opts) {
|
|
|
125
137
|
handleHttp(e);
|
|
126
138
|
}
|
|
127
139
|
}
|
|
140
|
+
async function confirmRestart(opts) {
|
|
141
|
+
if (opts.yes)
|
|
142
|
+
return;
|
|
143
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY)
|
|
144
|
+
return error("Confirmation required", "Pass --yes to restart the sandbox automatically.");
|
|
145
|
+
process.stdout.write("Changing mods restarts the sandbox and may interrupt running work. Continue? [y/N] ");
|
|
146
|
+
const chunks = [];
|
|
147
|
+
for await (const chunk of process.stdin) {
|
|
148
|
+
chunks.push(chunk);
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
const answer = Buffer.concat(chunks).toString().trim().toLowerCase();
|
|
152
|
+
if (answer !== "y" && answer !== "yes")
|
|
153
|
+
return error("Cancelled");
|
|
154
|
+
}
|
|
128
155
|
async function readPromptContent(words) {
|
|
129
156
|
let content = words.join(" ");
|
|
130
157
|
if (!content && !process.stdin.isTTY) {
|
|
@@ -144,11 +171,11 @@ async function sendPrompt(command, words, opts) {
|
|
|
144
171
|
return error("Conflicting schedule", "Use only one of --delay-ms, --at, or --cron");
|
|
145
172
|
if (opts.cron && !opts.timezone)
|
|
146
173
|
return error("Missing timezone", "--timezone is required with --cron");
|
|
147
|
-
const spaceId =
|
|
174
|
+
const spaceId = resolveSpace(command);
|
|
148
175
|
const client = createClient();
|
|
149
176
|
try {
|
|
150
177
|
const schedule = opts.delayMs
|
|
151
|
-
? { mode: "delay", delayMs:
|
|
178
|
+
? { mode: "delay", delayMs: parseInteger(opts.delayMs, "delay", { min: 1 }) }
|
|
152
179
|
: opts.at
|
|
153
180
|
? { mode: "at", sendAt: opts.at }
|
|
154
181
|
: opts.cron
|
|
@@ -162,7 +189,7 @@ async function sendPrompt(command, words, opts) {
|
|
|
162
189
|
provider: opts.provider,
|
|
163
190
|
schedule,
|
|
164
191
|
});
|
|
165
|
-
if (opts
|
|
192
|
+
if (jsonRequested(opts))
|
|
166
193
|
return outJson(result);
|
|
167
194
|
if (result.mode === "immediate")
|
|
168
195
|
return ok(`Prompt sent — sessionId: ${result.sessionId}, turnId: ${result.turnId}`);
|
|
@@ -201,7 +228,7 @@ export function registerSpaces(program) {
|
|
|
201
228
|
const client = createClient();
|
|
202
229
|
try {
|
|
203
230
|
const items = await client.spaces.list();
|
|
204
|
-
if (opts
|
|
231
|
+
if (jsonRequested(opts))
|
|
205
232
|
return outJson(items);
|
|
206
233
|
table(items, [
|
|
207
234
|
{ key: "id", label: "ID" },
|
|
@@ -222,7 +249,7 @@ export function registerSpaces(program) {
|
|
|
222
249
|
const client = createClient();
|
|
223
250
|
try {
|
|
224
251
|
const space = await client.spaces.get(id);
|
|
225
|
-
if (opts
|
|
252
|
+
if (jsonRequested(opts))
|
|
226
253
|
return outJson(space);
|
|
227
254
|
table([space], [
|
|
228
255
|
{ key: "id", label: "ID" },
|
|
@@ -254,7 +281,7 @@ export function registerSpaces(program) {
|
|
|
254
281
|
description: opts.description,
|
|
255
282
|
...(autoDestroy ? { config: { sandbox: { autoDestroy } } } : {}),
|
|
256
283
|
});
|
|
257
|
-
if (opts
|
|
284
|
+
if (jsonRequested(opts))
|
|
258
285
|
return outJson(result);
|
|
259
286
|
ok(`Space created: ${result.space.id}`);
|
|
260
287
|
table([result.space], [
|
|
@@ -287,12 +314,12 @@ export function registerSpaces(program) {
|
|
|
287
314
|
.description("Upload the space avatar")
|
|
288
315
|
.option("--json", "Output as JSON")
|
|
289
316
|
.action(async (path, opts) => {
|
|
290
|
-
const spaceId =
|
|
317
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
291
318
|
const client = createClient();
|
|
292
319
|
try {
|
|
293
320
|
const asset = await uploadAvatarAsset({ client, purpose: "space_avatar", spaceId, path });
|
|
294
321
|
const result = await client.space(spaceId).profile({ avatarUrl: asset.publicUrl });
|
|
295
|
-
if (opts
|
|
322
|
+
if (jsonRequested(opts))
|
|
296
323
|
return outJson({ ...result, asset });
|
|
297
324
|
ok("Space avatar updated");
|
|
298
325
|
}
|
|
@@ -313,13 +340,13 @@ export function registerSpaces(program) {
|
|
|
313
340
|
const autoDestroy = parseAutoDestroy(opts);
|
|
314
341
|
if (autoDestroy) {
|
|
315
342
|
const result = await client.space(id).updateConfig({ sandbox: { autoDestroy } });
|
|
316
|
-
if (opts
|
|
343
|
+
if (jsonRequested(opts))
|
|
317
344
|
return outJson(result);
|
|
318
345
|
ok(`Space config updated — sandbox auto destroy: ${formatAutoDestroy(autoDestroy)}`);
|
|
319
346
|
return;
|
|
320
347
|
}
|
|
321
348
|
const result = await client.space(id).getConfig();
|
|
322
|
-
if (opts
|
|
349
|
+
if (jsonRequested(opts))
|
|
323
350
|
return outJson(result);
|
|
324
351
|
table([{ key: "sandbox.autoDestroy", value: formatAutoDestroy(result.config.sandbox.autoDestroy) }], [
|
|
325
352
|
{ key: "key", label: "Key" },
|
|
@@ -363,11 +390,11 @@ export function registerSpaces(program) {
|
|
|
363
390
|
.description("Space usage statistics (default: 30 days)")
|
|
364
391
|
.option("--json", "Output as JSON")
|
|
365
392
|
.action(async (days, opts) => {
|
|
366
|
-
const spaceId =
|
|
393
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
367
394
|
const client = createClient();
|
|
368
395
|
try {
|
|
369
|
-
const usage = await client.space(spaceId).usage.get(
|
|
370
|
-
if (opts
|
|
396
|
+
const usage = await client.space(spaceId).usage.get(parseInteger(days ?? "30", "days", { min: 1 }));
|
|
397
|
+
if (jsonRequested(opts))
|
|
371
398
|
return outJson(usage);
|
|
372
399
|
console.log("\n Summary:");
|
|
373
400
|
table([usage.summary], [
|
|
@@ -387,23 +414,23 @@ function registerMods(spacesCmd) {
|
|
|
387
414
|
const modsCmd = spacesCmd
|
|
388
415
|
.command("mods")
|
|
389
416
|
.description("Manage space mods")
|
|
390
|
-
.hook("preAction", () => {
|
|
417
|
+
.hook("preAction", () => { resolveSpace(spacesCmd); });
|
|
391
418
|
modsCmd
|
|
392
419
|
.command("ls")
|
|
393
420
|
.alias("list")
|
|
394
421
|
.description("List mods")
|
|
395
422
|
.option("--json", "Output as JSON")
|
|
396
423
|
.action(async (opts) => {
|
|
397
|
-
const spaceId =
|
|
424
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
398
425
|
const client = createClient();
|
|
399
426
|
try {
|
|
400
427
|
const result = await client.space(spaceId).mods.list();
|
|
401
|
-
if (opts
|
|
428
|
+
if (jsonRequested(opts))
|
|
402
429
|
return outJson(result.items);
|
|
403
430
|
table(result.items, [
|
|
404
431
|
{ key: "id", label: "ID" },
|
|
405
432
|
{ key: "modSpaceName", label: "Name" },
|
|
406
|
-
{ key: "
|
|
433
|
+
{ key: "mountPath", label: "Mount" },
|
|
407
434
|
{ key: "enabled", label: "On" },
|
|
408
435
|
]);
|
|
409
436
|
}
|
|
@@ -414,15 +441,19 @@ function registerMods(spacesCmd) {
|
|
|
414
441
|
modsCmd
|
|
415
442
|
.command("add <modSpaceId>")
|
|
416
443
|
.description("Add a mod")
|
|
444
|
+
.option("--name <name>", "Display name")
|
|
445
|
+
.option("--slug <slug>", "Mount slug")
|
|
446
|
+
.option("-y, --yes", "Confirm sandbox restart")
|
|
417
447
|
.option("--json", "Output as JSON")
|
|
418
448
|
.action(async (modSpaceId, opts) => {
|
|
419
|
-
|
|
449
|
+
await confirmRestart(opts);
|
|
450
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
420
451
|
const client = createClient();
|
|
421
452
|
try {
|
|
422
|
-
const result = await client.space(spaceId).mods.create({ modSpaceId });
|
|
423
|
-
if (opts
|
|
453
|
+
const result = await client.space(spaceId).mods.create({ modSpaceId, name: opts.name, mountSlug: opts.slug });
|
|
454
|
+
if (jsonRequested(opts))
|
|
424
455
|
return outJson(result);
|
|
425
|
-
ok(
|
|
456
|
+
ok(`Mod added — ${result.item.mountPath}; sandbox restarting`);
|
|
426
457
|
}
|
|
427
458
|
catch (e) {
|
|
428
459
|
handleHttp(e);
|
|
@@ -431,15 +462,17 @@ function registerMods(spacesCmd) {
|
|
|
431
462
|
modsCmd
|
|
432
463
|
.command("enable <modId>")
|
|
433
464
|
.description("Enable a mod")
|
|
465
|
+
.option("-y, --yes", "Confirm sandbox restart")
|
|
434
466
|
.option("--json", "Output as JSON")
|
|
435
467
|
.action(async (modId, opts) => {
|
|
436
|
-
|
|
468
|
+
await confirmRestart(opts);
|
|
469
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
437
470
|
const client = createClient();
|
|
438
471
|
try {
|
|
439
472
|
const result = await client.space(spaceId).mods.update(modId, { enabled: true });
|
|
440
|
-
if (opts
|
|
473
|
+
if (jsonRequested(opts))
|
|
441
474
|
return outJson(result);
|
|
442
|
-
ok("Mod enabled");
|
|
475
|
+
ok("Mod enabled; sandbox restarting");
|
|
443
476
|
}
|
|
444
477
|
catch (e) {
|
|
445
478
|
handleHttp(e);
|
|
@@ -448,15 +481,17 @@ function registerMods(spacesCmd) {
|
|
|
448
481
|
modsCmd
|
|
449
482
|
.command("disable <modId>")
|
|
450
483
|
.description("Disable a mod")
|
|
484
|
+
.option("-y, --yes", "Confirm sandbox restart")
|
|
451
485
|
.option("--json", "Output as JSON")
|
|
452
486
|
.action(async (modId, opts) => {
|
|
453
|
-
|
|
487
|
+
await confirmRestart(opts);
|
|
488
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
454
489
|
const client = createClient();
|
|
455
490
|
try {
|
|
456
491
|
const result = await client.space(spaceId).mods.update(modId, { enabled: false });
|
|
457
|
-
if (opts
|
|
492
|
+
if (jsonRequested(opts))
|
|
458
493
|
return outJson(result);
|
|
459
|
-
ok("Mod disabled");
|
|
494
|
+
ok("Mod disabled; sandbox restarting");
|
|
460
495
|
}
|
|
461
496
|
catch (e) {
|
|
462
497
|
handleHttp(e);
|
|
@@ -466,15 +501,17 @@ function registerMods(spacesCmd) {
|
|
|
466
501
|
.command("rm <modId>")
|
|
467
502
|
.alias("remove")
|
|
468
503
|
.description("Remove a mod")
|
|
504
|
+
.option("-y, --yes", "Confirm sandbox restart")
|
|
469
505
|
.option("--json", "Output as JSON")
|
|
470
506
|
.action(async (modId, opts) => {
|
|
471
|
-
|
|
507
|
+
await confirmRestart(opts);
|
|
508
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
472
509
|
const client = createClient();
|
|
473
510
|
try {
|
|
474
511
|
const result = await client.space(spaceId).mods.remove(modId);
|
|
475
|
-
if (opts
|
|
512
|
+
if (jsonRequested(opts))
|
|
476
513
|
return outJson(result);
|
|
477
|
-
ok("Mod removed");
|
|
514
|
+
ok("Mod removed; sandbox restarting");
|
|
478
515
|
}
|
|
479
516
|
catch (e) {
|
|
480
517
|
handleHttp(e);
|
|
@@ -486,18 +523,18 @@ function registerFiles(spacesCmd) {
|
|
|
486
523
|
const filesCmd = spacesCmd
|
|
487
524
|
.command("files")
|
|
488
525
|
.description("File operations")
|
|
489
|
-
.hook("preAction", () => {
|
|
526
|
+
.hook("preAction", () => { resolveSpace(spacesCmd); });
|
|
490
527
|
filesCmd
|
|
491
528
|
.command("ls [path]")
|
|
492
529
|
.alias("list")
|
|
493
530
|
.description("List directory tree")
|
|
494
531
|
.option("--json", "Output as JSON")
|
|
495
532
|
.action(async (path, opts) => {
|
|
496
|
-
const spaceId =
|
|
533
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
497
534
|
const client = createClient();
|
|
498
535
|
try {
|
|
499
536
|
const tree = await client.space(spaceId).files.list(path ?? "");
|
|
500
|
-
if (opts
|
|
537
|
+
if (jsonRequested(opts))
|
|
501
538
|
return outJson(tree);
|
|
502
539
|
if (tree.entries.length === 0) {
|
|
503
540
|
console.log(" (empty)");
|
|
@@ -518,7 +555,7 @@ function registerFiles(spacesCmd) {
|
|
|
518
555
|
.command("cat <path>")
|
|
519
556
|
.description("Read file content")
|
|
520
557
|
.action(async (path) => {
|
|
521
|
-
const spaceId =
|
|
558
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
522
559
|
const client = createClient();
|
|
523
560
|
try {
|
|
524
561
|
const file = await client.space(spaceId).files.read(path);
|
|
@@ -548,7 +585,7 @@ function registerFiles(spacesCmd) {
|
|
|
548
585
|
}
|
|
549
586
|
if (!content)
|
|
550
587
|
return error("No content provided", "Use -c or pipe via stdin");
|
|
551
|
-
const spaceId =
|
|
588
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
552
589
|
const client = createClient();
|
|
553
590
|
try {
|
|
554
591
|
const result = await client.space(spaceId).files.write({
|
|
@@ -572,7 +609,7 @@ function registerFiles(spacesCmd) {
|
|
|
572
609
|
.command("mkdir <path>")
|
|
573
610
|
.description("Create a directory")
|
|
574
611
|
.action(async (path) => {
|
|
575
|
-
const spaceId =
|
|
612
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
576
613
|
const client = createClient();
|
|
577
614
|
try {
|
|
578
615
|
await client.space(spaceId).files.createDir(path);
|
|
@@ -587,7 +624,7 @@ function registerFiles(spacesCmd) {
|
|
|
587
624
|
.description("Delete a file or directory")
|
|
588
625
|
.option("-r, --recursive", "Delete recursively")
|
|
589
626
|
.action(async (path, opts) => {
|
|
590
|
-
const spaceId =
|
|
627
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
591
628
|
const client = createClient();
|
|
592
629
|
try {
|
|
593
630
|
await client.space(spaceId).files.delete(path, opts.recursive ?? false);
|
|
@@ -601,7 +638,7 @@ function registerFiles(spacesCmd) {
|
|
|
601
638
|
.command("mv <from> <to>")
|
|
602
639
|
.description("Move or rename")
|
|
603
640
|
.action(async (from, to) => {
|
|
604
|
-
const spaceId =
|
|
641
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
605
642
|
const client = createClient();
|
|
606
643
|
try {
|
|
607
644
|
await client.space(spaceId).files.move({ fromPath: from, toPath: to });
|
|
@@ -617,18 +654,18 @@ function registerSessions(spacesCmd) {
|
|
|
617
654
|
const sessionsCmd = spacesCmd
|
|
618
655
|
.command("sessions")
|
|
619
656
|
.description("Browse sessions and turns")
|
|
620
|
-
.hook("preAction", () => {
|
|
657
|
+
.hook("preAction", () => { resolveSpace(spacesCmd); });
|
|
621
658
|
sessionsCmd
|
|
622
659
|
.command("ls")
|
|
623
660
|
.alias("list")
|
|
624
661
|
.description("List sessions")
|
|
625
662
|
.option("--json", "Output as JSON")
|
|
626
663
|
.action(async (opts) => {
|
|
627
|
-
const spaceId =
|
|
664
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
628
665
|
const client = createClient();
|
|
629
666
|
try {
|
|
630
667
|
const result = await client.space(spaceId).sessions.list();
|
|
631
|
-
if (opts
|
|
668
|
+
if (jsonRequested(opts))
|
|
632
669
|
return outJson(result);
|
|
633
670
|
if (result.sessions.length === 0) {
|
|
634
671
|
console.log(" (empty)");
|
|
@@ -650,11 +687,11 @@ function registerSessions(spacesCmd) {
|
|
|
650
687
|
.description("Create a session")
|
|
651
688
|
.option("--json", "Output as JSON")
|
|
652
689
|
.action(async (title, opts) => {
|
|
653
|
-
const spaceId =
|
|
690
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
654
691
|
const client = createClient();
|
|
655
692
|
try {
|
|
656
693
|
const result = await client.space(spaceId).sessions.create({ title });
|
|
657
|
-
if (opts
|
|
694
|
+
if (jsonRequested(opts))
|
|
658
695
|
return outJson(result);
|
|
659
696
|
ok(`Session created: ${result.session.id}`);
|
|
660
697
|
table([result.session], [
|
|
@@ -671,11 +708,11 @@ function registerSessions(spacesCmd) {
|
|
|
671
708
|
.description("Session details")
|
|
672
709
|
.option("--json", "Output as JSON")
|
|
673
710
|
.action(async (id, opts) => {
|
|
674
|
-
const spaceId =
|
|
711
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
675
712
|
const client = createClient();
|
|
676
713
|
try {
|
|
677
714
|
const result = await client.space(spaceId).session(id).get();
|
|
678
|
-
if (opts
|
|
715
|
+
if (jsonRequested(opts))
|
|
679
716
|
return outJson(result);
|
|
680
717
|
table([result.session], [
|
|
681
718
|
{ key: "id", label: "ID" },
|
|
@@ -693,7 +730,7 @@ function registerSessions(spacesCmd) {
|
|
|
693
730
|
.command("rename <id> <name>")
|
|
694
731
|
.description("Rename a session")
|
|
695
732
|
.action(async (id, name) => {
|
|
696
|
-
const spaceId =
|
|
733
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
697
734
|
const client = createClient();
|
|
698
735
|
try {
|
|
699
736
|
await client.space(spaceId).session(id).rename(name);
|
|
@@ -709,13 +746,13 @@ function registerSessions(spacesCmd) {
|
|
|
709
746
|
.description("Stream realtime session events")
|
|
710
747
|
.option("--json", "Output as JSON")
|
|
711
748
|
.action(async (id, opts) => {
|
|
712
|
-
const spaceId =
|
|
749
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
713
750
|
const client = createClient();
|
|
714
751
|
const session = client.space(spaceId).session(id);
|
|
715
752
|
process.stdout.write(" Listening for events...\n\n");
|
|
716
753
|
let lastAppendPath = null;
|
|
717
754
|
session.on("turn.patch", (e) => {
|
|
718
|
-
if (opts
|
|
755
|
+
if (jsonRequested(opts)) {
|
|
719
756
|
console.log(JSON.stringify(e));
|
|
720
757
|
}
|
|
721
758
|
else {
|
|
@@ -742,7 +779,7 @@ function registerSessions(spacesCmd) {
|
|
|
742
779
|
});
|
|
743
780
|
session.on("turn.error", (e) => {
|
|
744
781
|
process.stderr.write(`\n ✗ Error\n`);
|
|
745
|
-
if (opts
|
|
782
|
+
if (jsonRequested(opts))
|
|
746
783
|
process.stderr.write(`${JSON.stringify(e)}\n`);
|
|
747
784
|
process.exit(1);
|
|
748
785
|
});
|
|
@@ -764,15 +801,15 @@ function registerTurns(sessionsCmd) {
|
|
|
764
801
|
.option("--limit <n>", "Page size", "30")
|
|
765
802
|
.option("--json", "Output as JSON")
|
|
766
803
|
.action(async (sessionId, opts) => {
|
|
767
|
-
const spaceId =
|
|
804
|
+
const spaceId = resolveSpace(sessionsCmd);
|
|
768
805
|
const client = createClient();
|
|
769
806
|
try {
|
|
770
807
|
const result = await client.space(spaceId).session(sessionId).turns.listPaginated({
|
|
771
|
-
cursor: opts.cursor === undefined ? undefined :
|
|
772
|
-
direction: opts.direction,
|
|
773
|
-
limit:
|
|
808
|
+
cursor: opts.cursor === undefined ? undefined : parseInteger(opts.cursor, "cursor", { min: 0 }),
|
|
809
|
+
direction: parseChoice(opts.direction ?? "older", "direction", ["older", "newer"]),
|
|
810
|
+
limit: parseInteger(opts.limit ?? "30", "limit", { min: 1, max: 100 }),
|
|
774
811
|
});
|
|
775
|
-
if (opts
|
|
812
|
+
if (jsonRequested(opts))
|
|
776
813
|
return outJson(result);
|
|
777
814
|
if (result.turns.length === 0)
|
|
778
815
|
return console.log(" No turns found");
|
|
@@ -796,11 +833,11 @@ function registerTurns(sessionsCmd) {
|
|
|
796
833
|
.description("Show turn details")
|
|
797
834
|
.option("--json", "Output as JSON")
|
|
798
835
|
.action(async (sessionId, turnId, opts) => {
|
|
799
|
-
const spaceId =
|
|
836
|
+
const spaceId = resolveSpace(sessionsCmd);
|
|
800
837
|
const client = createClient();
|
|
801
838
|
try {
|
|
802
839
|
const result = await client.space(spaceId).session(sessionId).turns.get(turnId);
|
|
803
|
-
if (opts
|
|
840
|
+
if (jsonRequested(opts))
|
|
804
841
|
return outJson(result);
|
|
805
842
|
table([result.turn], [
|
|
806
843
|
{ key: "sequence", label: "Seq" },
|
|
@@ -827,14 +864,14 @@ function registerTurns(sessionsCmd) {
|
|
|
827
864
|
.option("--limit <n>", "Page size", "100")
|
|
828
865
|
.option("--json", "Output as JSON")
|
|
829
866
|
.action(async (sessionId, opts) => {
|
|
830
|
-
const spaceId =
|
|
867
|
+
const spaceId = resolveSpace(sessionsCmd);
|
|
831
868
|
const client = createClient();
|
|
832
869
|
try {
|
|
833
870
|
const result = await client.space(spaceId).session(sessionId).turns.index({
|
|
834
|
-
cursor: opts.cursor === undefined ? undefined :
|
|
835
|
-
limit:
|
|
871
|
+
cursor: opts.cursor === undefined ? undefined : parseInteger(opts.cursor, "cursor", { min: 0 }),
|
|
872
|
+
limit: parseInteger(opts.limit ?? "100", "limit", { min: 1, max: 500 }),
|
|
836
873
|
});
|
|
837
|
-
if (opts
|
|
874
|
+
if (jsonRequested(opts))
|
|
838
875
|
return outJson(result);
|
|
839
876
|
if (result.turns.length === 0)
|
|
840
877
|
return console.log(" No turns found");
|
|
@@ -861,18 +898,18 @@ function registerTurns(sessionsCmd) {
|
|
|
861
898
|
.option("--after <n>", "Turns after anchor", "20")
|
|
862
899
|
.option("--json", "Output as JSON")
|
|
863
900
|
.action(async (sessionId, opts) => {
|
|
864
|
-
const spaceId =
|
|
901
|
+
const spaceId = resolveSpace(sessionsCmd);
|
|
865
902
|
if (!opts.sequence && !opts.turn)
|
|
866
903
|
return error("Missing anchor", "Use --sequence <n> or --turn <id>");
|
|
867
904
|
const client = createClient();
|
|
868
905
|
try {
|
|
869
906
|
const result = await client.space(spaceId).session(sessionId).turns.window({
|
|
870
|
-
sequence: opts.sequence === undefined ? undefined :
|
|
907
|
+
sequence: opts.sequence === undefined ? undefined : parseInteger(opts.sequence, "sequence", { min: 0 }),
|
|
871
908
|
turnId: opts.turn,
|
|
872
|
-
before:
|
|
873
|
-
after:
|
|
909
|
+
before: parseInteger(opts.before ?? "10", "before", { min: 0, max: 200 }),
|
|
910
|
+
after: parseInteger(opts.after ?? "20", "after", { min: 0, max: 200 }),
|
|
874
911
|
});
|
|
875
|
-
if (opts
|
|
912
|
+
if (jsonRequested(opts))
|
|
876
913
|
return outJson(result);
|
|
877
914
|
if (result.turns.length === 0)
|
|
878
915
|
return console.log(" No turns found");
|
|
@@ -901,7 +938,7 @@ function registerSessionAccess(sessionsCmd) {
|
|
|
901
938
|
const client = createClient();
|
|
902
939
|
try {
|
|
903
940
|
const policy = await client.sessionAccess.get(id);
|
|
904
|
-
if (opts
|
|
941
|
+
if (jsonRequested(opts))
|
|
905
942
|
return outJson(policy);
|
|
906
943
|
table([policy], [
|
|
907
944
|
{ key: "signed_in_user", label: "Signed-in" },
|
|
@@ -921,9 +958,9 @@ function registerSessionAccess(sessionsCmd) {
|
|
|
921
958
|
const client = createClient();
|
|
922
959
|
try {
|
|
923
960
|
const policy = await client.sessionAccess.set(id, {
|
|
924
|
-
anonymous_user: (opts.anonymous
|
|
961
|
+
anonymous_user: parseNullableRole(opts.anonymous, "anonymous role"),
|
|
925
962
|
});
|
|
926
|
-
if (opts
|
|
963
|
+
if (jsonRequested(opts))
|
|
927
964
|
return outJson(policy);
|
|
928
965
|
ok("Session access updated");
|
|
929
966
|
table([policy], [
|
|
@@ -954,18 +991,18 @@ function registerMembers(spacesCmd) {
|
|
|
954
991
|
const memCmd = spacesCmd
|
|
955
992
|
.command("members")
|
|
956
993
|
.description("Member management")
|
|
957
|
-
.hook("preAction", () => {
|
|
994
|
+
.hook("preAction", () => { resolveSpace(spacesCmd); });
|
|
958
995
|
memCmd
|
|
959
996
|
.command("ls")
|
|
960
997
|
.alias("list")
|
|
961
998
|
.description("List space members")
|
|
962
999
|
.option("--json", "Output as JSON")
|
|
963
1000
|
.action(async (opts) => {
|
|
964
|
-
const spaceId =
|
|
1001
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
965
1002
|
const client = createClient();
|
|
966
1003
|
try {
|
|
967
1004
|
const result = await client.space(spaceId).members.list();
|
|
968
|
-
if (opts
|
|
1005
|
+
if (jsonRequested(opts))
|
|
969
1006
|
return outJson(result);
|
|
970
1007
|
if (result.items.length === 0) {
|
|
971
1008
|
console.log(" (empty)");
|
|
@@ -985,10 +1022,10 @@ function registerMembers(spacesCmd) {
|
|
|
985
1022
|
.command("update <userId> <role>")
|
|
986
1023
|
.description("Change member role (host | builder | guest)")
|
|
987
1024
|
.action(async (userId, role) => {
|
|
988
|
-
const spaceId =
|
|
1025
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
989
1026
|
const client = createClient();
|
|
990
1027
|
try {
|
|
991
|
-
await client.space(spaceId).members.update(userId, role);
|
|
1028
|
+
await client.space(spaceId).members.update(userId, parseChoice(role, "role", SPACE_ROLES));
|
|
992
1029
|
ok(`${userId} → ${role}`);
|
|
993
1030
|
}
|
|
994
1031
|
catch (e) {
|
|
@@ -999,7 +1036,7 @@ function registerMembers(spacesCmd) {
|
|
|
999
1036
|
.command("remove <userId>")
|
|
1000
1037
|
.description("Remove a member")
|
|
1001
1038
|
.action(async (userId) => {
|
|
1002
|
-
const spaceId =
|
|
1039
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
1003
1040
|
const client = createClient();
|
|
1004
1041
|
try {
|
|
1005
1042
|
await client.space(spaceId).members.remove(userId);
|
|
@@ -1015,17 +1052,17 @@ function registerAccess(spacesCmd) {
|
|
|
1015
1052
|
const accCmd = spacesCmd
|
|
1016
1053
|
.command("access")
|
|
1017
1054
|
.description("Access control")
|
|
1018
|
-
.hook("preAction", () => {
|
|
1055
|
+
.hook("preAction", () => { resolveSpace(spacesCmd); });
|
|
1019
1056
|
accCmd
|
|
1020
1057
|
.command("get")
|
|
1021
1058
|
.description("Get access policy")
|
|
1022
1059
|
.option("--json", "Output as JSON")
|
|
1023
1060
|
.action(async (opts) => {
|
|
1024
|
-
const spaceId =
|
|
1061
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
1025
1062
|
const client = createClient();
|
|
1026
1063
|
try {
|
|
1027
1064
|
const policy = await client.space(spaceId).access.get();
|
|
1028
|
-
if (opts
|
|
1065
|
+
if (jsonRequested(opts))
|
|
1029
1066
|
return outJson(policy);
|
|
1030
1067
|
table([policy], [
|
|
1031
1068
|
{ key: "signed_in_user", label: "Signed-in" },
|
|
@@ -1043,14 +1080,14 @@ function registerAccess(spacesCmd) {
|
|
|
1043
1080
|
.option("--anonymous <role>", "Role for anonymous users (host|builder|guest|null)")
|
|
1044
1081
|
.option("--json", "Output as JSON")
|
|
1045
1082
|
.action(async (opts) => {
|
|
1046
|
-
const spaceId =
|
|
1083
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
1047
1084
|
const client = createClient();
|
|
1048
1085
|
try {
|
|
1049
1086
|
const policy = await client.space(spaceId).access.set({
|
|
1050
|
-
signed_in_user: (opts.signedIn
|
|
1051
|
-
anonymous_user: (opts.anonymous
|
|
1087
|
+
signed_in_user: parseNullableRole(opts.signedIn, "signed-in role"),
|
|
1088
|
+
anonymous_user: parseNullableRole(opts.anonymous, "anonymous role"),
|
|
1052
1089
|
});
|
|
1053
|
-
if (opts
|
|
1090
|
+
if (jsonRequested(opts))
|
|
1054
1091
|
return outJson(policy);
|
|
1055
1092
|
ok("Access policy updated");
|
|
1056
1093
|
table([policy], [
|
|
@@ -1068,18 +1105,18 @@ function registerCheckpoints(spacesCmd) {
|
|
|
1068
1105
|
const cpCmd = spacesCmd
|
|
1069
1106
|
.command("checkpoints")
|
|
1070
1107
|
.description("Checkpoint management")
|
|
1071
|
-
.hook("preAction", () => {
|
|
1108
|
+
.hook("preAction", () => { resolveSpace(spacesCmd); });
|
|
1072
1109
|
cpCmd
|
|
1073
1110
|
.command("ls")
|
|
1074
1111
|
.alias("list")
|
|
1075
1112
|
.description("List checkpoints")
|
|
1076
1113
|
.option("--json", "Output as JSON")
|
|
1077
1114
|
.action(async (opts) => {
|
|
1078
|
-
const spaceId =
|
|
1115
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
1079
1116
|
const client = createClient();
|
|
1080
1117
|
try {
|
|
1081
1118
|
const result = await client.space(spaceId).checkpoints.list();
|
|
1082
|
-
if (opts
|
|
1119
|
+
if (jsonRequested(opts))
|
|
1083
1120
|
return outJson(result);
|
|
1084
1121
|
if (result.checkpoints.length === 0) {
|
|
1085
1122
|
console.log(" (empty)");
|
|
@@ -1101,11 +1138,11 @@ function registerCheckpoints(spacesCmd) {
|
|
|
1101
1138
|
.description("Checkpoint details")
|
|
1102
1139
|
.option("--json", "Output as JSON")
|
|
1103
1140
|
.action(async (id, opts) => {
|
|
1104
|
-
const spaceId =
|
|
1141
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
1105
1142
|
const client = createClient();
|
|
1106
1143
|
try {
|
|
1107
1144
|
const result = await client.space(spaceId).checkpoints.get(id);
|
|
1108
|
-
if (opts
|
|
1145
|
+
if (jsonRequested(opts))
|
|
1109
1146
|
return outJson(result);
|
|
1110
1147
|
table([result.checkpoint], [
|
|
1111
1148
|
{ key: "id", label: "ID" },
|
|
@@ -1124,11 +1161,11 @@ function registerCheckpoints(spacesCmd) {
|
|
|
1124
1161
|
.description("Create a checkpoint")
|
|
1125
1162
|
.option("--json", "Output as JSON")
|
|
1126
1163
|
.action(async (description, opts) => {
|
|
1127
|
-
const spaceId =
|
|
1164
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
1128
1165
|
const client = createClient();
|
|
1129
1166
|
try {
|
|
1130
1167
|
const result = await client.space(spaceId).checkpoints.create(description ?? null);
|
|
1131
|
-
if (opts
|
|
1168
|
+
if (jsonRequested(opts))
|
|
1132
1169
|
return outJson(result);
|
|
1133
1170
|
ok(`Checkpoint created — taskRunId: ${result.taskRunId}`);
|
|
1134
1171
|
}
|