@neta-art/cohub-cli 1.2.0 → 1.3.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.
@@ -1,4 +1,3 @@
1
- import { resolveToken } from "../auth.js";
2
1
  import { createClient } from "../client.js";
3
2
  import { table, json as outJson, ok, error, handleHttp } from "../output.js";
4
3
  function requireSpace(program) {
@@ -11,6 +10,70 @@ function requireSpace(program) {
11
10
  }
12
11
  return error("Missing required option", "Add -s, --space <id> to target a space");
13
12
  }
13
+ async function readPromptContent(words) {
14
+ let content = words.join(" ");
15
+ if (!content && !process.stdin.isTTY) {
16
+ const chunks = [];
17
+ for await (const chunk of process.stdin)
18
+ chunks.push(chunk);
19
+ content = Buffer.concat(chunks).toString().trim();
20
+ }
21
+ if (!content)
22
+ return error("No content", "Pass as argument or pipe via stdin");
23
+ return content;
24
+ }
25
+ async function sendPrompt(command, words, opts) {
26
+ const content = await readPromptContent(words);
27
+ const scheduleFlags = [opts.delayMs, opts.at, opts.cron].filter((value) => value !== undefined);
28
+ if (scheduleFlags.length > 1)
29
+ return error("Conflicting schedule", "Use only one of --delay-ms, --at, or --cron");
30
+ if (opts.cron && !opts.timezone)
31
+ return error("Missing timezone", "--timezone is required with --cron");
32
+ const spaceId = requireSpace(command);
33
+ const client = createClient();
34
+ try {
35
+ const schedule = opts.delayMs
36
+ ? { mode: "delay", delayMs: Number.parseInt(opts.delayMs, 10) }
37
+ : opts.at
38
+ ? { mode: "at", sendAt: opts.at }
39
+ : opts.cron
40
+ ? { mode: "repeat", cronExpression: opts.cron, timezone: opts.timezone }
41
+ : undefined;
42
+ const result = await client.space(spaceId).prompt({
43
+ sessionId: opts.session,
44
+ title: opts.title,
45
+ content: [{ type: "text", text: content }],
46
+ model: opts.model,
47
+ provider: opts.provider,
48
+ schedule,
49
+ });
50
+ if (opts.json)
51
+ return outJson(result);
52
+ if (result.mode === "immediate")
53
+ return ok(`Prompt sent — sessionId: ${result.sessionId}, turnId: ${result.turnId}`);
54
+ if (result.mode === "repeat")
55
+ return ok(`Prompt scheduled — cronJobId: ${result.cronJobId}, nextRunAt: ${result.nextRunAt}`);
56
+ return ok(`Prompt scheduled — taskRunId: ${result.taskRunId}, scheduledAt: ${result.scheduledAt}`);
57
+ }
58
+ catch (e) {
59
+ handleHttp(e);
60
+ }
61
+ }
62
+ export function registerPrompt(program) {
63
+ program
64
+ .command("prompt [content...]")
65
+ .description("Send or schedule a prompt in a space")
66
+ .option("--session <id>", "Target session ID")
67
+ .option("--title <title>", "Title for a newly created session or schedule")
68
+ .option("-m, --model <model>", "Model name")
69
+ .option("-p, --provider <provider>", "Provider name")
70
+ .option("--delay-ms <ms>", "Delay sending by milliseconds")
71
+ .option("--at <iso>", "Send once at an ISO 8601 time with timezone")
72
+ .option("--cron <expression>", "Repeat using a 5-field cron expression")
73
+ .option("--timezone <tz>", "IANA timezone for --cron, e.g. Asia/Shanghai")
74
+ .option("--json", "Output as JSON")
75
+ .action((words, opts) => sendPrompt(program, words, opts));
76
+ }
14
77
  export function registerSpaces(program) {
15
78
  const spacesCmd = program.command("spaces").description("Space management");
16
79
  // ── spaces ls ──
@@ -20,8 +83,7 @@ export function registerSpaces(program) {
20
83
  .description("List all spaces")
21
84
  .option("--json", "Output as JSON")
22
85
  .action(async (opts) => {
23
- const token = resolveToken() ?? missingAuth();
24
- const client = createClient(token);
86
+ const client = createClient();
25
87
  try {
26
88
  const items = await client.spaces.list();
27
89
  if (opts.json)
@@ -42,8 +104,7 @@ export function registerSpaces(program) {
42
104
  .description("Show space details")
43
105
  .option("--json", "Output as JSON")
44
106
  .action(async (id, opts) => {
45
- const token = resolveToken() ?? missingAuth();
46
- const client = createClient(token);
107
+ const client = createClient();
47
108
  try {
48
109
  const space = await client.spaces.get(id);
49
110
  if (opts.json)
@@ -68,8 +129,7 @@ export function registerSpaces(program) {
68
129
  .option("-d, --description <desc>", "Space description")
69
130
  .option("--json", "Output as JSON")
70
131
  .action(async (opts) => {
71
- const token = resolveToken() ?? missingAuth();
72
- const client = createClient(token);
132
+ const client = createClient();
73
133
  try {
74
134
  const result = await client.spaces.create({
75
135
  name: opts.name,
@@ -93,8 +153,7 @@ export function registerSpaces(program) {
93
153
  .command("rename <id> <name>")
94
154
  .description("Rename a space")
95
155
  .action(async (id, name) => {
96
- const token = resolveToken() ?? missingAuth();
97
- const client = createClient(token);
156
+ const client = createClient();
98
157
  try {
99
158
  await client.space(id).rename(name);
100
159
  ok(`Space renamed to "${name}"`);
@@ -105,7 +164,8 @@ export function registerSpaces(program) {
105
164
  });
106
165
  // ── spaces prompt ──
107
166
  spacesCmd
108
- .command("prompt [content...]")
167
+ .command("prompt [content...]", { hidden: true })
168
+ .alias("send")
109
169
  .description("Send or schedule a prompt in the target space")
110
170
  .option("--session <id>", "Target session ID")
111
171
  .option("--title <title>", "Title for a newly created session or schedule")
@@ -116,52 +176,7 @@ export function registerSpaces(program) {
116
176
  .option("--cron <expression>", "Repeat using a 5-field cron expression")
117
177
  .option("--timezone <tz>", "IANA timezone for --cron, e.g. Asia/Shanghai")
118
178
  .option("--json", "Output as JSON")
119
- .action(async (words, opts) => {
120
- const token = resolveToken() ?? missingAuth();
121
- let content = words.join(" ");
122
- if (!content && !process.stdin.isTTY) {
123
- const chunks = [];
124
- for await (const chunk of process.stdin)
125
- chunks.push(chunk);
126
- content = Buffer.concat(chunks).toString().trim();
127
- }
128
- if (!content)
129
- return error("No content", "Pass as argument or pipe via stdin");
130
- const scheduleFlags = [opts.delayMs, opts.at, opts.cron].filter((value) => value !== undefined);
131
- if (scheduleFlags.length > 1)
132
- return error("Conflicting schedule", "Use only one of --delay-ms, --at, or --cron");
133
- if (opts.cron && !opts.timezone)
134
- return error("Missing timezone", "--timezone is required with --cron");
135
- const spaceId = requireSpace(spacesCmd);
136
- const client = createClient(token);
137
- try {
138
- const schedule = opts.delayMs
139
- ? { mode: "delay", delayMs: Number.parseInt(opts.delayMs, 10) }
140
- : opts.at
141
- ? { mode: "at", sendAt: opts.at }
142
- : opts.cron
143
- ? { mode: "repeat", cronExpression: opts.cron, timezone: opts.timezone }
144
- : undefined;
145
- const result = await client.space(spaceId).prompt({
146
- sessionId: opts.session,
147
- title: opts.title,
148
- content: [{ type: "text", text: content }],
149
- model: opts.model,
150
- provider: opts.provider,
151
- schedule,
152
- });
153
- if (opts.json)
154
- return outJson(result);
155
- if (result.mode === "immediate")
156
- return ok(`Prompt sent — sessionId: ${result.sessionId}, turnId: ${result.turnId}`);
157
- if (result.mode === "repeat")
158
- return ok(`Prompt scheduled — cronJobId: ${result.cronJobId}, nextRunAt: ${result.nextRunAt}`);
159
- return ok(`Prompt scheduled — taskRunId: ${result.taskRunId}, scheduledAt: ${result.scheduledAt}`);
160
- }
161
- catch (e) {
162
- handleHttp(e);
163
- }
164
- });
179
+ .action((words, opts) => sendPrompt(spacesCmd, words, opts));
165
180
  // ── spaces files ──
166
181
  registerFiles(spacesCmd);
167
182
  // ── spaces sessions ──
@@ -178,9 +193,8 @@ export function registerSpaces(program) {
178
193
  .description("Space usage statistics (default: 30 days)")
179
194
  .option("--json", "Output as JSON")
180
195
  .action(async (days, opts) => {
181
- const token = resolveToken() ?? missingAuth();
182
196
  const spaceId = requireSpace(spacesCmd);
183
- const client = createClient(token);
197
+ const client = createClient();
184
198
  try {
185
199
  const usage = await client.space(spaceId).usage.get(Number.parseInt(days ?? "30", 10));
186
200
  if (opts.json)
@@ -211,9 +225,8 @@ function registerFiles(spacesCmd) {
211
225
  .description("List directory tree")
212
226
  .option("--json", "Output as JSON")
213
227
  .action(async (path, opts) => {
214
- const token = resolveToken() ?? missingAuth();
215
228
  const spaceId = requireSpace(spacesCmd);
216
- const client = createClient(token);
229
+ const client = createClient();
217
230
  try {
218
231
  const tree = await client.space(spaceId).files.list(path ?? "");
219
232
  if (opts.json)
@@ -237,9 +250,8 @@ function registerFiles(spacesCmd) {
237
250
  .command("cat <path>")
238
251
  .description("Read file content")
239
252
  .action(async (path) => {
240
- const token = resolveToken() ?? missingAuth();
241
253
  const spaceId = requireSpace(spacesCmd);
242
- const client = createClient(token);
254
+ const client = createClient();
243
255
  try {
244
256
  const file = await client.space(spaceId).files.read(path);
245
257
  console.log(file.content);
@@ -254,7 +266,6 @@ function registerFiles(spacesCmd) {
254
266
  .option("-c, --content <text>", "File content")
255
267
  .option("-e, --encoding <enc>", "Encoding (utf-8 or base64)", "utf-8")
256
268
  .action(async (path, opts) => {
257
- const token = resolveToken() ?? missingAuth();
258
269
  let content = opts.content ?? "";
259
270
  if (!content && !process.stdin.isTTY) {
260
271
  const chunks = [];
@@ -265,7 +276,7 @@ function registerFiles(spacesCmd) {
265
276
  if (!content)
266
277
  return error("No content provided", "Use -c or pipe via stdin");
267
278
  const spaceId = requireSpace(spacesCmd);
268
- const client = createClient(token);
279
+ const client = createClient();
269
280
  try {
270
281
  const result = await client.space(spaceId).files.write({
271
282
  path,
@@ -282,9 +293,8 @@ function registerFiles(spacesCmd) {
282
293
  .command("mkdir <path>")
283
294
  .description("Create a directory")
284
295
  .action(async (path) => {
285
- const token = resolveToken() ?? missingAuth();
286
296
  const spaceId = requireSpace(spacesCmd);
287
- const client = createClient(token);
297
+ const client = createClient();
288
298
  try {
289
299
  await client.space(spaceId).files.createDir(path);
290
300
  ok(`Directory created: ${path}`);
@@ -298,9 +308,8 @@ function registerFiles(spacesCmd) {
298
308
  .description("Delete a file or directory")
299
309
  .option("-r, --recursive", "Delete recursively")
300
310
  .action(async (path, opts) => {
301
- const token = resolveToken() ?? missingAuth();
302
311
  const spaceId = requireSpace(spacesCmd);
303
- const client = createClient(token);
312
+ const client = createClient();
304
313
  try {
305
314
  await client.space(spaceId).files.delete(path, opts.recursive ?? false);
306
315
  ok(`Deleted: ${path}`);
@@ -313,9 +322,8 @@ function registerFiles(spacesCmd) {
313
322
  .command("mv <from> <to>")
314
323
  .description("Move or rename")
315
324
  .action(async (from, to) => {
316
- const token = resolveToken() ?? missingAuth();
317
325
  const spaceId = requireSpace(spacesCmd);
318
- const client = createClient(token);
326
+ const client = createClient();
319
327
  try {
320
328
  await client.space(spaceId).files.move({ fromPath: from, toPath: to });
321
329
  ok(`Moved: ${from} → ${to}`);
@@ -324,19 +332,12 @@ function registerFiles(spacesCmd) {
324
332
  handleHttp(e);
325
333
  }
326
334
  });
327
- filesCmd
328
- .command("upload <files...>")
329
- .description("Upload files to a directory")
330
- .option("--dir <dir>", "Target directory", "")
331
- .action(async (_files) => {
332
- error("Upload requires browser File API", "Use the web interface for now");
333
- });
334
335
  }
335
336
  // ── Session operations ──
336
337
  function registerSessions(spacesCmd) {
337
338
  const sessionsCmd = spacesCmd
338
339
  .command("sessions")
339
- .description("Session operations")
340
+ .description("Browse sessions and turns")
340
341
  .hook("preAction", () => { requireSpace(spacesCmd); });
341
342
  sessionsCmd
342
343
  .command("ls")
@@ -344,9 +345,8 @@ function registerSessions(spacesCmd) {
344
345
  .description("List sessions")
345
346
  .option("--json", "Output as JSON")
346
347
  .action(async (opts) => {
347
- const token = resolveToken() ?? missingAuth();
348
348
  const spaceId = requireSpace(spacesCmd);
349
- const client = createClient(token);
349
+ const client = createClient();
350
350
  try {
351
351
  const result = await client.space(spaceId).sessions.list();
352
352
  if (opts.json)
@@ -371,9 +371,8 @@ function registerSessions(spacesCmd) {
371
371
  .description("Create a session")
372
372
  .option("--json", "Output as JSON")
373
373
  .action(async (title, opts) => {
374
- const token = resolveToken() ?? missingAuth();
375
374
  const spaceId = requireSpace(spacesCmd);
376
- const client = createClient(token);
375
+ const client = createClient();
377
376
  try {
378
377
  const result = await client.space(spaceId).sessions.create({ title });
379
378
  if (opts.json)
@@ -393,9 +392,8 @@ function registerSessions(spacesCmd) {
393
392
  .description("Session details")
394
393
  .option("--json", "Output as JSON")
395
394
  .action(async (id, opts) => {
396
- const token = resolveToken() ?? missingAuth();
397
395
  const spaceId = requireSpace(spacesCmd);
398
- const client = createClient(token);
396
+ const client = createClient();
399
397
  try {
400
398
  const result = await client.space(spaceId).session(id).get();
401
399
  if (opts.json)
@@ -416,9 +414,8 @@ function registerSessions(spacesCmd) {
416
414
  .command("rename <id> <name>")
417
415
  .description("Rename a session")
418
416
  .action(async (id, name) => {
419
- const token = resolveToken() ?? missingAuth();
420
417
  const spaceId = requireSpace(spacesCmd);
421
- const client = createClient(token);
418
+ const client = createClient();
422
419
  try {
423
420
  await client.space(spaceId).session(id).rename(name);
424
421
  ok(`Session renamed to "${name}"`);
@@ -427,17 +424,14 @@ function registerSessions(spacesCmd) {
427
424
  handleHttp(e);
428
425
  }
429
426
  });
430
- // ── sessions messages ──
431
- registerMessages(sessionsCmd);
432
427
  // ── sessions tail ──
433
428
  sessionsCmd
434
429
  .command("tail <id>")
435
430
  .description("Stream realtime session events")
436
431
  .option("--json", "Output as JSON")
437
432
  .action(async (id, opts) => {
438
- const token = resolveToken() ?? missingAuth();
439
433
  const spaceId = requireSpace(spacesCmd);
440
- const client = createClient(token);
434
+ const client = createClient();
441
435
  const session = client.space(spaceId).session(id);
442
436
  process.stdout.write(" Listening for events...\n\n");
443
437
  let lastAppendPath = null;
@@ -474,71 +468,202 @@ function registerSessions(spacesCmd) {
474
468
  process.exit(1);
475
469
  });
476
470
  });
471
+ // ── sessions turns ──
472
+ registerTurns(sessionsCmd);
473
+ // ── sessions access ──
474
+ registerSessionAccess(sessionsCmd);
477
475
  }
478
- // ── Message operations ──
479
- function registerMessages(sessionsCmd) {
480
- const msgsCmd = sessionsCmd.command("messages").description("Message operations");
481
- msgsCmd
476
+ // ── Turn operations ──
477
+ function registerTurns(sessionsCmd) {
478
+ const turnsCmd = sessionsCmd.command("turns").description("Inspect session turns");
479
+ turnsCmd
482
480
  .command("ls <sessionId>")
483
481
  .alias("list")
484
- .description("List session messages")
482
+ .description("List recent turns")
483
+ .option("--cursor <sequence>", "Turn sequence cursor")
484
+ .option("--direction <older|newer>", "Page direction", "older")
485
+ .option("--limit <n>", "Page size", "30")
485
486
  .option("--json", "Output as JSON")
486
- .option("--limit <n>", "Page size", "50")
487
487
  .action(async (sessionId, opts) => {
488
- const token = resolveToken() ?? missingAuth();
489
488
  const spaceId = requireSpace(sessionsCmd);
490
- const client = createClient(token);
489
+ const client = createClient();
491
490
  try {
492
- const result = await client.space(spaceId).session(sessionId).messages.listPaginated({
493
- limit: Number.parseInt(opts.limit ?? "50", 10),
491
+ const result = await client.space(spaceId).session(sessionId).turns.listPaginated({
492
+ cursor: opts.cursor === undefined ? undefined : Number.parseInt(opts.cursor, 10),
493
+ direction: opts.direction,
494
+ limit: Number.parseInt(opts.limit ?? "30", 10),
494
495
  });
495
496
  if (opts.json)
496
497
  return outJson(result);
497
- if (result.messages.length === 0) {
498
- console.log(" (empty)");
499
- return;
500
- }
501
- table(result.messages, [
498
+ if (result.turns.length === 0)
499
+ return console.log(" No turns found");
500
+ table(result.turns, [
501
+ { key: "sequence", label: "Seq" },
502
502
  { key: "id", label: "ID" },
503
- { key: "role", label: "Role" },
504
- { key: "createdAt", label: "Created" },
503
+ { key: "status", label: "Status" },
504
+ { key: "userText", label: "User" },
505
+ { key: "assistantText", label: "Assistant" },
506
+ { key: "updatedAt", label: "Updated" },
505
507
  ]);
506
- if (result.hasMore) {
507
- console.log(`\n (moreuse --cursor ${result.nextCursor} for next page)`);
508
- }
508
+ if (result.hasMore)
509
+ console.log(`\n More turns available next cursor: ${result.nextCursor}`);
509
510
  }
510
511
  catch (e) {
511
512
  handleHttp(e);
512
513
  }
513
514
  });
514
- msgsCmd
515
- .command("send <sessionId> [content...]")
516
- .description("Send a message to a session")
517
- .option("-m, --model <model>", "Model name")
518
- .option("-p, --provider <provider>", "Provider name")
515
+ turnsCmd
516
+ .command("get <sessionId> <turnId>")
517
+ .description("Show turn details")
519
518
  .option("--json", "Output as JSON")
520
- .action(async (sessionId, words, opts) => {
521
- const token = resolveToken() ?? missingAuth();
522
- let content = words.join(" ");
523
- if (!content && !process.stdin.isTTY) {
524
- const chunks = [];
525
- for await (const chunk of process.stdin)
526
- chunks.push(chunk);
527
- content = Buffer.concat(chunks).toString().trim();
519
+ .action(async (sessionId, turnId, opts) => {
520
+ const spaceId = requireSpace(sessionsCmd);
521
+ const client = createClient();
522
+ try {
523
+ const result = await client.space(spaceId).session(sessionId).turns.get(turnId);
524
+ if (opts.json)
525
+ return outJson(result);
526
+ table([result.turn], [
527
+ { key: "sequence", label: "Seq" },
528
+ { key: "id", label: "ID" },
529
+ { key: "status", label: "Status" },
530
+ { key: "provider", label: "Provider" },
531
+ { key: "model", label: "Model" },
532
+ { key: "stopReason", label: "Stop" },
533
+ { key: "errorMessage", label: "Error" },
534
+ ]);
535
+ if (result.turn.userText)
536
+ console.log(`\nUser:\n${result.turn.userText}`);
537
+ if (result.turn.assistantText)
538
+ console.log(`\nAssistant:\n${result.turn.assistantText}`);
528
539
  }
529
- if (!content)
530
- return error("No content", "Pass as argument or pipe via stdin");
540
+ catch (e) {
541
+ handleHttp(e);
542
+ }
543
+ });
544
+ turnsCmd
545
+ .command("index <sessionId>", { hidden: true })
546
+ .description("List lightweight turn index")
547
+ .option("--cursor <sequence>", "Turn sequence cursor")
548
+ .option("--limit <n>", "Page size", "100")
549
+ .option("--json", "Output as JSON")
550
+ .action(async (sessionId, opts) => {
551
+ const spaceId = requireSpace(sessionsCmd);
552
+ const client = createClient();
553
+ try {
554
+ const result = await client.space(spaceId).session(sessionId).turns.index({
555
+ cursor: opts.cursor === undefined ? undefined : Number.parseInt(opts.cursor, 10),
556
+ limit: Number.parseInt(opts.limit ?? "100", 10),
557
+ });
558
+ if (opts.json)
559
+ return outJson(result);
560
+ if (result.turns.length === 0)
561
+ return console.log(" No turns found");
562
+ table(result.turns, [
563
+ { key: "sequence", label: "Seq" },
564
+ { key: "id", label: "ID" },
565
+ { key: "status", label: "Status" },
566
+ { key: "userPreview", label: "User" },
567
+ { key: "assistantPreview", label: "Assistant" },
568
+ ]);
569
+ if (result.hasMore)
570
+ console.log(`\n More turns available — next cursor: ${result.nextCursor}`);
571
+ }
572
+ catch (e) {
573
+ handleHttp(e);
574
+ }
575
+ });
576
+ turnsCmd
577
+ .command("window <sessionId>", { hidden: true })
578
+ .description("Load turns around a sequence or turn ID")
579
+ .option("--sequence <n>", "Anchor turn sequence")
580
+ .option("--turn <id>", "Anchor turn ID")
581
+ .option("--before <n>", "Turns before anchor", "10")
582
+ .option("--after <n>", "Turns after anchor", "20")
583
+ .option("--json", "Output as JSON")
584
+ .action(async (sessionId, opts) => {
531
585
  const spaceId = requireSpace(sessionsCmd);
532
- const client = createClient(token);
586
+ if (!opts.sequence && !opts.turn)
587
+ return error("Missing anchor", "Use --sequence <n> or --turn <id>");
588
+ const client = createClient();
533
589
  try {
534
- const result = await client.space(spaceId).session(sessionId).messages.send({
535
- content: [{ type: "text", text: content }],
536
- model: opts.model,
537
- provider: opts.provider,
590
+ const result = await client.space(spaceId).session(sessionId).turns.window({
591
+ sequence: opts.sequence === undefined ? undefined : Number.parseInt(opts.sequence, 10),
592
+ turnId: opts.turn,
593
+ before: Number.parseInt(opts.before ?? "10", 10),
594
+ after: Number.parseInt(opts.after ?? "20", 10),
538
595
  });
539
596
  if (opts.json)
540
597
  return outJson(result);
541
- ok(`Message sent — userMessageId: ${result.userMessageId}`);
598
+ if (result.turns.length === 0)
599
+ return console.log(" No turns found");
600
+ table(result.turns, [
601
+ { key: "sequence", label: "Seq" },
602
+ { key: "id", label: "ID" },
603
+ { key: "status", label: "Status" },
604
+ { key: "userText", label: "User" },
605
+ { key: "assistantText", label: "Assistant" },
606
+ ]);
607
+ console.log(`\n Window — older: ${result.hasMoreOlder ? "yes" : "no"}, newer: ${result.hasMoreNewer ? "yes" : "no"}`);
608
+ }
609
+ catch (e) {
610
+ handleHttp(e);
611
+ }
612
+ });
613
+ }
614
+ // ── Session access operations ──
615
+ function registerSessionAccess(sessionsCmd) {
616
+ const accessCmd = sessionsCmd.command("access").description("Session access control");
617
+ accessCmd
618
+ .command("get <id>")
619
+ .description("Get session access policy")
620
+ .option("--json", "Output as JSON")
621
+ .action(async (id, opts) => {
622
+ const client = createClient();
623
+ try {
624
+ const policy = await client.sessionAccess.get(id);
625
+ if (opts.json)
626
+ return outJson(policy);
627
+ table([policy], [
628
+ { key: "signed_in_user", label: "Signed-in" },
629
+ { key: "anonymous_user", label: "Anonymous" },
630
+ ]);
631
+ }
632
+ catch (e) {
633
+ handleHttp(e);
634
+ }
635
+ });
636
+ accessCmd
637
+ .command("set <id>")
638
+ .description("Set session anonymous access")
639
+ .option("--anonymous <role>", "Anonymous role (host|builder|guest|null)")
640
+ .option("--json", "Output as JSON")
641
+ .action(async (id, opts) => {
642
+ const client = createClient();
643
+ try {
644
+ const policy = await client.sessionAccess.set(id, {
645
+ anonymous_user: (opts.anonymous ?? null),
646
+ });
647
+ if (opts.json)
648
+ return outJson(policy);
649
+ ok("Session access updated");
650
+ table([policy], [
651
+ { key: "signed_in_user", label: "Signed-in" },
652
+ { key: "anonymous_user", label: "Anonymous" },
653
+ ]);
654
+ }
655
+ catch (e) {
656
+ handleHttp(e);
657
+ }
658
+ });
659
+ accessCmd
660
+ .command("remove <id>")
661
+ .description("Remove session access override")
662
+ .action(async (id) => {
663
+ const client = createClient();
664
+ try {
665
+ await client.sessionAccess.remove(id);
666
+ ok(`Session access override removed: ${id}`);
542
667
  }
543
668
  catch (e) {
544
669
  handleHttp(e);
@@ -557,9 +682,8 @@ function registerMembers(spacesCmd) {
557
682
  .description("List space members")
558
683
  .option("--json", "Output as JSON")
559
684
  .action(async (opts) => {
560
- const token = resolveToken() ?? missingAuth();
561
685
  const spaceId = requireSpace(spacesCmd);
562
- const client = createClient(token);
686
+ const client = createClient();
563
687
  try {
564
688
  const result = await client.space(spaceId).members.list();
565
689
  if (opts.json)
@@ -582,9 +706,8 @@ function registerMembers(spacesCmd) {
582
706
  .command("update <userId> <role>")
583
707
  .description("Change member role (host | builder | guest)")
584
708
  .action(async (userId, role) => {
585
- const token = resolveToken() ?? missingAuth();
586
709
  const spaceId = requireSpace(spacesCmd);
587
- const client = createClient(token);
710
+ const client = createClient();
588
711
  try {
589
712
  await client.space(spaceId).members.update(userId, role);
590
713
  ok(`${userId} → ${role}`);
@@ -597,9 +720,8 @@ function registerMembers(spacesCmd) {
597
720
  .command("remove <userId>")
598
721
  .description("Remove a member")
599
722
  .action(async (userId) => {
600
- const token = resolveToken() ?? missingAuth();
601
723
  const spaceId = requireSpace(spacesCmd);
602
- const client = createClient(token);
724
+ const client = createClient();
603
725
  try {
604
726
  await client.space(spaceId).members.remove(userId);
605
727
  ok(`${userId} removed`);
@@ -620,9 +742,8 @@ function registerAccess(spacesCmd) {
620
742
  .description("Get access policy")
621
743
  .option("--json", "Output as JSON")
622
744
  .action(async (opts) => {
623
- const token = resolveToken() ?? missingAuth();
624
745
  const spaceId = requireSpace(spacesCmd);
625
- const client = createClient(token);
746
+ const client = createClient();
626
747
  try {
627
748
  const policy = await client.space(spaceId).access.get();
628
749
  if (opts.json)
@@ -643,9 +764,8 @@ function registerAccess(spacesCmd) {
643
764
  .option("--anonymous <role>", "Role for anonymous users (host|builder|guest|null)")
644
765
  .option("--json", "Output as JSON")
645
766
  .action(async (opts) => {
646
- const token = resolveToken() ?? missingAuth();
647
767
  const spaceId = requireSpace(spacesCmd);
648
- const client = createClient(token);
768
+ const client = createClient();
649
769
  try {
650
770
  const policy = await client.space(spaceId).access.set({
651
771
  signed_in_user: (opts.signedIn ?? null),
@@ -676,9 +796,8 @@ function registerCheckpoints(spacesCmd) {
676
796
  .description("List checkpoints")
677
797
  .option("--json", "Output as JSON")
678
798
  .action(async (opts) => {
679
- const token = resolveToken() ?? missingAuth();
680
799
  const spaceId = requireSpace(spacesCmd);
681
- const client = createClient(token);
800
+ const client = createClient();
682
801
  try {
683
802
  const result = await client.space(spaceId).checkpoints.list();
684
803
  if (opts.json)
@@ -703,9 +822,8 @@ function registerCheckpoints(spacesCmd) {
703
822
  .description("Checkpoint details")
704
823
  .option("--json", "Output as JSON")
705
824
  .action(async (id, opts) => {
706
- const token = resolveToken() ?? missingAuth();
707
825
  const spaceId = requireSpace(spacesCmd);
708
- const client = createClient(token);
826
+ const client = createClient();
709
827
  try {
710
828
  const result = await client.space(spaceId).checkpoints.get(id);
711
829
  if (opts.json)
@@ -727,9 +845,8 @@ function registerCheckpoints(spacesCmd) {
727
845
  .description("Create a checkpoint")
728
846
  .option("--json", "Output as JSON")
729
847
  .action(async (description, opts) => {
730
- const token = resolveToken() ?? missingAuth();
731
848
  const spaceId = requireSpace(spacesCmd);
732
- const client = createClient(token);
849
+ const client = createClient();
733
850
  try {
734
851
  const result = await client.space(spaceId).checkpoints.create(description ?? null);
735
852
  if (opts.json)
@@ -741,6 +858,3 @@ function registerCheckpoints(spacesCmd) {
741
858
  }
742
859
  });
743
860
  }
744
- function missingAuth() {
745
- return error("Not authenticated", "Run 'cohub auth login <token>'");
746
- }