@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.
- package/README.md +183 -13
- package/dist/auth.d.ts +41 -12
- package/dist/auth.js +249 -33
- package/dist/client.d.ts +1 -1
- package/dist/client.js +4 -2
- package/dist/commands/auth.js +93 -32
- package/dist/commands/channels.js +4 -14
- package/dist/commands/cron-jobs.js +6 -19
- package/dist/commands/generations.js +45 -50
- package/dist/commands/models.js +26 -6
- package/dist/commands/prompts.js +2 -6
- package/dist/commands/search.d.ts +2 -0
- package/dist/commands/search.js +71 -0
- package/dist/commands/session-access.js +4 -14
- package/dist/commands/spaces.d.ts +1 -0
- package/dist/commands/spaces.js +264 -150
- package/dist/commands/tasks.js +4 -11
- package/dist/index.js +40 -10
- package/dist/output.js +3 -0
- package/dist/self-update.d.ts +1 -0
- package/dist/self-update.js +136 -0
- package/package.json +2 -2
package/dist/commands/spaces.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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("
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
// ──
|
|
479
|
-
function
|
|
480
|
-
const
|
|
481
|
-
|
|
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
|
|
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(
|
|
489
|
+
const client = createClient();
|
|
491
490
|
try {
|
|
492
|
-
const result = await client.space(spaceId).session(sessionId).
|
|
493
|
-
|
|
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.
|
|
498
|
-
console.log("
|
|
499
|
-
|
|
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: "
|
|
504
|
-
{ key: "
|
|
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
|
|
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
|
-
|
|
515
|
-
.command("
|
|
516
|
-
.description("
|
|
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,
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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
|
-
|
|
530
|
-
|
|
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
|
-
|
|
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).
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
}
|