@neta-art/cohub-cli 1.0.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 +15 -0
- package/bin/cohub.js +2 -0
- package/dist/auth.d.ts +9 -0
- package/dist/auth.js +35 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.js +7 -0
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +51 -0
- package/dist/commands/channels.d.ts +2 -0
- package/dist/commands/channels.js +84 -0
- package/dist/commands/cron-jobs.d.ts +2 -0
- package/dist/commands/cron-jobs.js +136 -0
- package/dist/commands/models.d.ts +2 -0
- package/dist/commands/models.js +34 -0
- package/dist/commands/prompts.d.ts +2 -0
- package/dist/commands/prompts.js +33 -0
- package/dist/commands/session-access.d.ts +2 -0
- package/dist/commands/session-access.js +72 -0
- package/dist/commands/spaces.d.ts +2 -0
- package/dist/commands/spaces.js +675 -0
- package/dist/commands/tasks.d.ts +2 -0
- package/dist/commands/tasks.js +104 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +37 -0
- package/dist/output.d.ts +14 -0
- package/dist/output.js +91 -0
- package/package.json +29 -0
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
import { resolveToken } from "../auth.js";
|
|
2
|
+
import { createClient } from "../client.js";
|
|
3
|
+
import { table, json as outJson, ok, error, handleHttp } from "../output.js";
|
|
4
|
+
function requireSpace(program) {
|
|
5
|
+
let current = program;
|
|
6
|
+
while (current) {
|
|
7
|
+
const opts = current.opts();
|
|
8
|
+
if (opts.space)
|
|
9
|
+
return String(opts.space);
|
|
10
|
+
current = current.parent ?? null;
|
|
11
|
+
}
|
|
12
|
+
return error("Missing required option", "Add -s, --space <id> to target a space");
|
|
13
|
+
}
|
|
14
|
+
export function registerSpaces(program) {
|
|
15
|
+
const spacesCmd = program.command("spaces").description("Space management");
|
|
16
|
+
// ── spaces ls ──
|
|
17
|
+
spacesCmd
|
|
18
|
+
.command("ls")
|
|
19
|
+
.alias("list")
|
|
20
|
+
.description("List all spaces")
|
|
21
|
+
.option("--json", "Output as JSON")
|
|
22
|
+
.action(async (opts) => {
|
|
23
|
+
const token = resolveToken() ?? missingAuth();
|
|
24
|
+
const client = createClient(token);
|
|
25
|
+
try {
|
|
26
|
+
const items = await client.spaces.list();
|
|
27
|
+
if (opts.json)
|
|
28
|
+
return outJson(items);
|
|
29
|
+
table(items, [
|
|
30
|
+
{ key: "id", label: "ID" },
|
|
31
|
+
{ key: "name", label: "Name" },
|
|
32
|
+
{ key: "createdAt", label: "Created" },
|
|
33
|
+
]);
|
|
34
|
+
}
|
|
35
|
+
catch (e) {
|
|
36
|
+
handleHttp(e);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
// ── spaces get ──
|
|
40
|
+
spacesCmd
|
|
41
|
+
.command("get <id>")
|
|
42
|
+
.description("Show space details")
|
|
43
|
+
.option("--json", "Output as JSON")
|
|
44
|
+
.action(async (id, opts) => {
|
|
45
|
+
const token = resolveToken() ?? missingAuth();
|
|
46
|
+
const client = createClient(token);
|
|
47
|
+
try {
|
|
48
|
+
const space = await client.spaces.get(id);
|
|
49
|
+
if (opts.json)
|
|
50
|
+
return outJson(space);
|
|
51
|
+
table([space], [
|
|
52
|
+
{ key: "id", label: "ID" },
|
|
53
|
+
{ key: "name", label: "Name" },
|
|
54
|
+
{ key: "description", label: "Description" },
|
|
55
|
+
{ key: "status", label: "Status" },
|
|
56
|
+
{ key: "createdAt", label: "Created" },
|
|
57
|
+
]);
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
handleHttp(e);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
// ── spaces create ──
|
|
64
|
+
spacesCmd
|
|
65
|
+
.command("create")
|
|
66
|
+
.description("Create a new space")
|
|
67
|
+
.option("-n, --name <name>", "Space name")
|
|
68
|
+
.option("-d, --description <desc>", "Space description")
|
|
69
|
+
.option("--json", "Output as JSON")
|
|
70
|
+
.action(async (opts) => {
|
|
71
|
+
const token = resolveToken() ?? missingAuth();
|
|
72
|
+
const client = createClient(token);
|
|
73
|
+
try {
|
|
74
|
+
const result = await client.spaces.create({
|
|
75
|
+
name: opts.name,
|
|
76
|
+
description: opts.description,
|
|
77
|
+
});
|
|
78
|
+
if (opts.json)
|
|
79
|
+
return outJson(result);
|
|
80
|
+
ok(`Space created: ${result.space.id}`);
|
|
81
|
+
table([result.space], [
|
|
82
|
+
{ key: "id", label: "ID" },
|
|
83
|
+
{ key: "name", label: "Name" },
|
|
84
|
+
{ key: "taskRunId", label: "Task" },
|
|
85
|
+
]);
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
handleHttp(e);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
// ── spaces rename ──
|
|
92
|
+
spacesCmd
|
|
93
|
+
.command("rename <id> <name>")
|
|
94
|
+
.description("Rename a space")
|
|
95
|
+
.action(async (id, name) => {
|
|
96
|
+
const token = resolveToken() ?? missingAuth();
|
|
97
|
+
const client = createClient(token);
|
|
98
|
+
try {
|
|
99
|
+
await client.space(id).rename(name);
|
|
100
|
+
ok(`Space renamed to "${name}"`);
|
|
101
|
+
}
|
|
102
|
+
catch (e) {
|
|
103
|
+
handleHttp(e);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
// ── spaces files ──
|
|
107
|
+
registerFiles(spacesCmd);
|
|
108
|
+
// ── spaces sessions ──
|
|
109
|
+
registerSessions(spacesCmd);
|
|
110
|
+
// ── spaces members ──
|
|
111
|
+
registerMembers(spacesCmd);
|
|
112
|
+
// ── spaces access ──
|
|
113
|
+
registerAccess(spacesCmd);
|
|
114
|
+
// ── spaces checkpoints ──
|
|
115
|
+
registerCheckpoints(spacesCmd);
|
|
116
|
+
// ── spaces usage ──
|
|
117
|
+
spacesCmd
|
|
118
|
+
.command("usage [days]")
|
|
119
|
+
.description("Space usage statistics (default: 30 days)")
|
|
120
|
+
.option("--json", "Output as JSON")
|
|
121
|
+
.action(async (days, opts) => {
|
|
122
|
+
const token = resolveToken() ?? missingAuth();
|
|
123
|
+
const spaceId = requireSpace(spacesCmd);
|
|
124
|
+
const client = createClient(token);
|
|
125
|
+
try {
|
|
126
|
+
const usage = await client.space(spaceId).usage.get(Number.parseInt(days ?? "30", 10));
|
|
127
|
+
if (opts.json)
|
|
128
|
+
return outJson(usage);
|
|
129
|
+
console.log("\n Summary:");
|
|
130
|
+
table([usage.summary], [
|
|
131
|
+
{ key: "totalTokens", label: "Tokens" },
|
|
132
|
+
{ key: "costTotal", label: "Cost ($)" },
|
|
133
|
+
{ key: "requestCount", label: "Requests" },
|
|
134
|
+
{ key: "successCount", label: "Success" },
|
|
135
|
+
{ key: "errorCount", label: "Errors" },
|
|
136
|
+
]);
|
|
137
|
+
}
|
|
138
|
+
catch (e) {
|
|
139
|
+
handleHttp(e);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
// ── File operations ──
|
|
144
|
+
function registerFiles(spacesCmd) {
|
|
145
|
+
const filesCmd = spacesCmd
|
|
146
|
+
.command("files")
|
|
147
|
+
.description("File operations")
|
|
148
|
+
.hook("preAction", () => { requireSpace(spacesCmd); });
|
|
149
|
+
filesCmd
|
|
150
|
+
.command("ls [path]")
|
|
151
|
+
.alias("list")
|
|
152
|
+
.description("List directory tree")
|
|
153
|
+
.option("--json", "Output as JSON")
|
|
154
|
+
.action(async (path, opts) => {
|
|
155
|
+
const token = resolveToken() ?? missingAuth();
|
|
156
|
+
const spaceId = requireSpace(spacesCmd);
|
|
157
|
+
const client = createClient(token);
|
|
158
|
+
try {
|
|
159
|
+
const tree = await client.space(spaceId).files.list(path ?? "");
|
|
160
|
+
if (opts.json)
|
|
161
|
+
return outJson(tree);
|
|
162
|
+
if (tree.entries.length === 0) {
|
|
163
|
+
console.log(" (empty)");
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
table(tree.entries, [
|
|
167
|
+
{ key: "name", label: "Name" },
|
|
168
|
+
{ key: "type", label: "Type" },
|
|
169
|
+
{ key: "size", label: "Size" },
|
|
170
|
+
{ key: "mtimeMs", label: "Modified" },
|
|
171
|
+
]);
|
|
172
|
+
}
|
|
173
|
+
catch (e) {
|
|
174
|
+
handleHttp(e);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
filesCmd
|
|
178
|
+
.command("cat <path>")
|
|
179
|
+
.description("Read file content")
|
|
180
|
+
.action(async (path) => {
|
|
181
|
+
const token = resolveToken() ?? missingAuth();
|
|
182
|
+
const spaceId = requireSpace(spacesCmd);
|
|
183
|
+
const client = createClient(token);
|
|
184
|
+
try {
|
|
185
|
+
const file = await client.space(spaceId).files.read(path);
|
|
186
|
+
console.log(file.content);
|
|
187
|
+
}
|
|
188
|
+
catch (e) {
|
|
189
|
+
handleHttp(e);
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
filesCmd
|
|
193
|
+
.command("write <path>")
|
|
194
|
+
.description("Write file content")
|
|
195
|
+
.option("-c, --content <text>", "File content")
|
|
196
|
+
.option("-e, --encoding <enc>", "Encoding (utf-8 or base64)", "utf-8")
|
|
197
|
+
.action(async (path, opts) => {
|
|
198
|
+
const token = resolveToken() ?? missingAuth();
|
|
199
|
+
let content = opts.content ?? "";
|
|
200
|
+
if (!content && !process.stdin.isTTY) {
|
|
201
|
+
const chunks = [];
|
|
202
|
+
for await (const chunk of process.stdin)
|
|
203
|
+
chunks.push(chunk);
|
|
204
|
+
content = Buffer.concat(chunks).toString();
|
|
205
|
+
}
|
|
206
|
+
if (!content)
|
|
207
|
+
return error("No content provided", "Use -c or pipe via stdin");
|
|
208
|
+
const spaceId = requireSpace(spacesCmd);
|
|
209
|
+
const client = createClient(token);
|
|
210
|
+
try {
|
|
211
|
+
const result = await client.space(spaceId).files.write({
|
|
212
|
+
path,
|
|
213
|
+
content,
|
|
214
|
+
encoding: opts.encoding,
|
|
215
|
+
});
|
|
216
|
+
ok(`Written ${result.size} bytes to ${result.path}`);
|
|
217
|
+
}
|
|
218
|
+
catch (e) {
|
|
219
|
+
handleHttp(e);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
filesCmd
|
|
223
|
+
.command("mkdir <path>")
|
|
224
|
+
.description("Create a directory")
|
|
225
|
+
.action(async (path) => {
|
|
226
|
+
const token = resolveToken() ?? missingAuth();
|
|
227
|
+
const spaceId = requireSpace(spacesCmd);
|
|
228
|
+
const client = createClient(token);
|
|
229
|
+
try {
|
|
230
|
+
await client.space(spaceId).files.createDir(path);
|
|
231
|
+
ok(`Directory created: ${path}`);
|
|
232
|
+
}
|
|
233
|
+
catch (e) {
|
|
234
|
+
handleHttp(e);
|
|
235
|
+
}
|
|
236
|
+
});
|
|
237
|
+
filesCmd
|
|
238
|
+
.command("rm <path>")
|
|
239
|
+
.description("Delete a file or directory")
|
|
240
|
+
.option("-r, --recursive", "Delete recursively")
|
|
241
|
+
.action(async (path, opts) => {
|
|
242
|
+
const token = resolveToken() ?? missingAuth();
|
|
243
|
+
const spaceId = requireSpace(spacesCmd);
|
|
244
|
+
const client = createClient(token);
|
|
245
|
+
try {
|
|
246
|
+
await client.space(spaceId).files.delete(path, opts.recursive ?? false);
|
|
247
|
+
ok(`Deleted: ${path}`);
|
|
248
|
+
}
|
|
249
|
+
catch (e) {
|
|
250
|
+
handleHttp(e);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
filesCmd
|
|
254
|
+
.command("mv <from> <to>")
|
|
255
|
+
.description("Move or rename")
|
|
256
|
+
.action(async (from, to) => {
|
|
257
|
+
const token = resolveToken() ?? missingAuth();
|
|
258
|
+
const spaceId = requireSpace(spacesCmd);
|
|
259
|
+
const client = createClient(token);
|
|
260
|
+
try {
|
|
261
|
+
await client.space(spaceId).files.move({ fromPath: from, toPath: to });
|
|
262
|
+
ok(`Moved: ${from} → ${to}`);
|
|
263
|
+
}
|
|
264
|
+
catch (e) {
|
|
265
|
+
handleHttp(e);
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
filesCmd
|
|
269
|
+
.command("upload <files...>")
|
|
270
|
+
.description("Upload files to a directory")
|
|
271
|
+
.option("--dir <dir>", "Target directory", "")
|
|
272
|
+
.action(async (_files) => {
|
|
273
|
+
error("Upload requires browser File API", "Use the web interface for now");
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
// ── Session operations ──
|
|
277
|
+
function registerSessions(spacesCmd) {
|
|
278
|
+
const sessionsCmd = spacesCmd
|
|
279
|
+
.command("sessions")
|
|
280
|
+
.description("Session operations")
|
|
281
|
+
.hook("preAction", () => { requireSpace(spacesCmd); });
|
|
282
|
+
sessionsCmd
|
|
283
|
+
.command("ls")
|
|
284
|
+
.alias("list")
|
|
285
|
+
.description("List sessions")
|
|
286
|
+
.option("--json", "Output as JSON")
|
|
287
|
+
.action(async (opts) => {
|
|
288
|
+
const token = resolveToken() ?? missingAuth();
|
|
289
|
+
const spaceId = requireSpace(spacesCmd);
|
|
290
|
+
const client = createClient(token);
|
|
291
|
+
try {
|
|
292
|
+
const result = await client.space(spaceId).sessions.list();
|
|
293
|
+
if (opts.json)
|
|
294
|
+
return outJson(result);
|
|
295
|
+
if (result.sessions.length === 0) {
|
|
296
|
+
console.log(" (empty)");
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
table(result.sessions, [
|
|
300
|
+
{ key: "id", label: "ID" },
|
|
301
|
+
{ key: "title", label: "Title" },
|
|
302
|
+
{ key: "totalMessages", label: "Messages" },
|
|
303
|
+
{ key: "createdAt", label: "Created" },
|
|
304
|
+
]);
|
|
305
|
+
}
|
|
306
|
+
catch (e) {
|
|
307
|
+
handleHttp(e);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
sessionsCmd
|
|
311
|
+
.command("create [title]")
|
|
312
|
+
.description("Create a session")
|
|
313
|
+
.option("--json", "Output as JSON")
|
|
314
|
+
.action(async (title, opts) => {
|
|
315
|
+
const token = resolveToken() ?? missingAuth();
|
|
316
|
+
const spaceId = requireSpace(spacesCmd);
|
|
317
|
+
const client = createClient(token);
|
|
318
|
+
try {
|
|
319
|
+
const result = await client.space(spaceId).sessions.create({ title });
|
|
320
|
+
if (opts.json)
|
|
321
|
+
return outJson(result);
|
|
322
|
+
ok(`Session created: ${result.session.id}`);
|
|
323
|
+
table([result.session], [
|
|
324
|
+
{ key: "id", label: "ID" },
|
|
325
|
+
{ key: "title", label: "Title" },
|
|
326
|
+
]);
|
|
327
|
+
}
|
|
328
|
+
catch (e) {
|
|
329
|
+
handleHttp(e);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
sessionsCmd
|
|
333
|
+
.command("get <id>")
|
|
334
|
+
.description("Session details")
|
|
335
|
+
.option("--json", "Output as JSON")
|
|
336
|
+
.action(async (id, opts) => {
|
|
337
|
+
const token = resolveToken() ?? missingAuth();
|
|
338
|
+
const spaceId = requireSpace(spacesCmd);
|
|
339
|
+
const client = createClient(token);
|
|
340
|
+
try {
|
|
341
|
+
const result = await client.space(spaceId).session(id).get();
|
|
342
|
+
if (opts.json)
|
|
343
|
+
return outJson(result);
|
|
344
|
+
table([result.session], [
|
|
345
|
+
{ key: "id", label: "ID" },
|
|
346
|
+
{ key: "title", label: "Title" },
|
|
347
|
+
{ key: "totalMessages", label: "Messages" },
|
|
348
|
+
{ key: "totalToolCalls", label: "Tool Calls" },
|
|
349
|
+
{ key: "createdAt", label: "Created" },
|
|
350
|
+
]);
|
|
351
|
+
}
|
|
352
|
+
catch (e) {
|
|
353
|
+
handleHttp(e);
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
sessionsCmd
|
|
357
|
+
.command("rename <id> <name>")
|
|
358
|
+
.description("Rename a session")
|
|
359
|
+
.action(async (id, name) => {
|
|
360
|
+
const token = resolveToken() ?? missingAuth();
|
|
361
|
+
const spaceId = requireSpace(spacesCmd);
|
|
362
|
+
const client = createClient(token);
|
|
363
|
+
try {
|
|
364
|
+
await client.space(spaceId).session(id).rename(name);
|
|
365
|
+
ok(`Session renamed to "${name}"`);
|
|
366
|
+
}
|
|
367
|
+
catch (e) {
|
|
368
|
+
handleHttp(e);
|
|
369
|
+
}
|
|
370
|
+
});
|
|
371
|
+
// ── sessions messages ──
|
|
372
|
+
registerMessages(sessionsCmd);
|
|
373
|
+
// ── sessions tail ──
|
|
374
|
+
sessionsCmd
|
|
375
|
+
.command("tail <id>")
|
|
376
|
+
.description("Stream realtime session events")
|
|
377
|
+
.option("--json", "Output as JSON")
|
|
378
|
+
.action(async (id, opts) => {
|
|
379
|
+
const token = resolveToken() ?? missingAuth();
|
|
380
|
+
const spaceId = requireSpace(spacesCmd);
|
|
381
|
+
const client = createClient(token);
|
|
382
|
+
const session = client.space(spaceId).session(id);
|
|
383
|
+
process.stdout.write(" Listening for events...\n\n");
|
|
384
|
+
session.on("turn.progress", (e) => {
|
|
385
|
+
if (opts.json) {
|
|
386
|
+
console.log(JSON.stringify(e));
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
const blocks = e.payload?.content;
|
|
390
|
+
const text = blocks?.find((b) => b.type === "text")?.text;
|
|
391
|
+
if (text)
|
|
392
|
+
process.stdout.write(text);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
session.on("turn.final", () => {
|
|
396
|
+
process.stdout.write("\n\n ✓ Done\n");
|
|
397
|
+
process.exit(0);
|
|
398
|
+
});
|
|
399
|
+
session.on("turn.error", (e) => {
|
|
400
|
+
process.stderr.write(`\n ✗ Error\n`);
|
|
401
|
+
if (opts.json)
|
|
402
|
+
process.stderr.write(JSON.stringify(e) + "\n");
|
|
403
|
+
process.exit(1);
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
}
|
|
407
|
+
// ── Message operations ──
|
|
408
|
+
function registerMessages(sessionsCmd) {
|
|
409
|
+
const msgsCmd = sessionsCmd.command("messages").description("Message operations");
|
|
410
|
+
msgsCmd
|
|
411
|
+
.command("ls <sessionId>")
|
|
412
|
+
.alias("list")
|
|
413
|
+
.description("List session messages")
|
|
414
|
+
.option("--json", "Output as JSON")
|
|
415
|
+
.option("--limit <n>", "Page size", "50")
|
|
416
|
+
.action(async (sessionId, opts) => {
|
|
417
|
+
const token = resolveToken() ?? missingAuth();
|
|
418
|
+
const spaceId = requireSpace(sessionsCmd);
|
|
419
|
+
const client = createClient(token);
|
|
420
|
+
try {
|
|
421
|
+
const result = await client.space(spaceId).session(sessionId).messages.listPaginated({
|
|
422
|
+
limit: Number.parseInt(opts.limit ?? "50", 10),
|
|
423
|
+
});
|
|
424
|
+
if (opts.json)
|
|
425
|
+
return outJson(result);
|
|
426
|
+
if (result.messages.length === 0) {
|
|
427
|
+
console.log(" (empty)");
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
table(result.messages, [
|
|
431
|
+
{ key: "id", label: "ID" },
|
|
432
|
+
{ key: "role", label: "Role" },
|
|
433
|
+
{ key: "createdAt", label: "Created" },
|
|
434
|
+
]);
|
|
435
|
+
if (result.hasMore) {
|
|
436
|
+
console.log(`\n (more — use --cursor ${result.nextCursor} for next page)`);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
catch (e) {
|
|
440
|
+
handleHttp(e);
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
msgsCmd
|
|
444
|
+
.command("send <sessionId> [content...]")
|
|
445
|
+
.description("Send a message to a session")
|
|
446
|
+
.option("-m, --model <model>", "Model name")
|
|
447
|
+
.option("-p, --provider <provider>", "Provider name")
|
|
448
|
+
.option("--json", "Output as JSON")
|
|
449
|
+
.action(async (sessionId, words, opts) => {
|
|
450
|
+
const token = resolveToken() ?? missingAuth();
|
|
451
|
+
let content = words.join(" ");
|
|
452
|
+
if (!content && !process.stdin.isTTY) {
|
|
453
|
+
const chunks = [];
|
|
454
|
+
for await (const chunk of process.stdin)
|
|
455
|
+
chunks.push(chunk);
|
|
456
|
+
content = Buffer.concat(chunks).toString().trim();
|
|
457
|
+
}
|
|
458
|
+
if (!content)
|
|
459
|
+
return error("No content", "Pass as argument or pipe via stdin");
|
|
460
|
+
const spaceId = requireSpace(sessionsCmd);
|
|
461
|
+
const client = createClient(token);
|
|
462
|
+
try {
|
|
463
|
+
const result = await client.space(spaceId).session(sessionId).messages.send({
|
|
464
|
+
content: [{ type: "text", text: content }],
|
|
465
|
+
model: opts.model,
|
|
466
|
+
provider: opts.provider,
|
|
467
|
+
});
|
|
468
|
+
if (opts.json)
|
|
469
|
+
return outJson(result);
|
|
470
|
+
ok(`Message sent — userMessageId: ${result.userMessageId}`);
|
|
471
|
+
}
|
|
472
|
+
catch (e) {
|
|
473
|
+
handleHttp(e);
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
// ── Member operations ──
|
|
478
|
+
function registerMembers(spacesCmd) {
|
|
479
|
+
const memCmd = spacesCmd
|
|
480
|
+
.command("members")
|
|
481
|
+
.description("Member management")
|
|
482
|
+
.hook("preAction", () => { requireSpace(spacesCmd); });
|
|
483
|
+
memCmd
|
|
484
|
+
.command("ls")
|
|
485
|
+
.alias("list")
|
|
486
|
+
.description("List space members")
|
|
487
|
+
.option("--json", "Output as JSON")
|
|
488
|
+
.action(async (opts) => {
|
|
489
|
+
const token = resolveToken() ?? missingAuth();
|
|
490
|
+
const spaceId = requireSpace(spacesCmd);
|
|
491
|
+
const client = createClient(token);
|
|
492
|
+
try {
|
|
493
|
+
const result = await client.space(spaceId).members.list();
|
|
494
|
+
if (opts.json)
|
|
495
|
+
return outJson(result);
|
|
496
|
+
if (result.items.length === 0) {
|
|
497
|
+
console.log(" (empty)");
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
table(result.items, [
|
|
501
|
+
{ key: "userId", label: "User ID" },
|
|
502
|
+
{ key: "role", label: "Role" },
|
|
503
|
+
{ key: "createdAt", label: "Since" },
|
|
504
|
+
]);
|
|
505
|
+
}
|
|
506
|
+
catch (e) {
|
|
507
|
+
handleHttp(e);
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
memCmd
|
|
511
|
+
.command("update <userId> <role>")
|
|
512
|
+
.description("Change member role (host | builder | guest)")
|
|
513
|
+
.action(async (userId, role) => {
|
|
514
|
+
const token = resolveToken() ?? missingAuth();
|
|
515
|
+
const spaceId = requireSpace(spacesCmd);
|
|
516
|
+
const client = createClient(token);
|
|
517
|
+
try {
|
|
518
|
+
await client.space(spaceId).members.update(userId, role);
|
|
519
|
+
ok(`${userId} → ${role}`);
|
|
520
|
+
}
|
|
521
|
+
catch (e) {
|
|
522
|
+
handleHttp(e);
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
memCmd
|
|
526
|
+
.command("remove <userId>")
|
|
527
|
+
.description("Remove a member")
|
|
528
|
+
.action(async (userId) => {
|
|
529
|
+
const token = resolveToken() ?? missingAuth();
|
|
530
|
+
const spaceId = requireSpace(spacesCmd);
|
|
531
|
+
const client = createClient(token);
|
|
532
|
+
try {
|
|
533
|
+
await client.space(spaceId).members.remove(userId);
|
|
534
|
+
ok(`${userId} removed`);
|
|
535
|
+
}
|
|
536
|
+
catch (e) {
|
|
537
|
+
handleHttp(e);
|
|
538
|
+
}
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
// ── Access control ──
|
|
542
|
+
function registerAccess(spacesCmd) {
|
|
543
|
+
const accCmd = spacesCmd
|
|
544
|
+
.command("access")
|
|
545
|
+
.description("Access control")
|
|
546
|
+
.hook("preAction", () => { requireSpace(spacesCmd); });
|
|
547
|
+
accCmd
|
|
548
|
+
.command("get")
|
|
549
|
+
.description("Get access policy")
|
|
550
|
+
.option("--json", "Output as JSON")
|
|
551
|
+
.action(async (opts) => {
|
|
552
|
+
const token = resolveToken() ?? missingAuth();
|
|
553
|
+
const spaceId = requireSpace(spacesCmd);
|
|
554
|
+
const client = createClient(token);
|
|
555
|
+
try {
|
|
556
|
+
const policy = await client.space(spaceId).access.get();
|
|
557
|
+
if (opts.json)
|
|
558
|
+
return outJson(policy);
|
|
559
|
+
table([policy], [
|
|
560
|
+
{ key: "signed_in_user", label: "Signed-in" },
|
|
561
|
+
{ key: "anonymous_user", label: "Anonymous" },
|
|
562
|
+
]);
|
|
563
|
+
}
|
|
564
|
+
catch (e) {
|
|
565
|
+
handleHttp(e);
|
|
566
|
+
}
|
|
567
|
+
});
|
|
568
|
+
accCmd
|
|
569
|
+
.command("set")
|
|
570
|
+
.description("Set access policy")
|
|
571
|
+
.option("--signed-in <role>", "Role for signed-in users (host|builder|guest|null)")
|
|
572
|
+
.option("--anonymous <role>", "Role for anonymous users (host|builder|guest|null)")
|
|
573
|
+
.option("--json", "Output as JSON")
|
|
574
|
+
.action(async (opts) => {
|
|
575
|
+
const token = resolveToken() ?? missingAuth();
|
|
576
|
+
const spaceId = requireSpace(spacesCmd);
|
|
577
|
+
const client = createClient(token);
|
|
578
|
+
try {
|
|
579
|
+
const policy = await client.space(spaceId).access.set({
|
|
580
|
+
signed_in_user: (opts.signedIn ?? null),
|
|
581
|
+
anonymous_user: (opts.anonymous ?? null),
|
|
582
|
+
});
|
|
583
|
+
if (opts.json)
|
|
584
|
+
return outJson(policy);
|
|
585
|
+
ok("Access policy updated");
|
|
586
|
+
table([policy], [
|
|
587
|
+
{ key: "signed_in_user", label: "Signed-in" },
|
|
588
|
+
{ key: "anonymous_user", label: "Anonymous" },
|
|
589
|
+
]);
|
|
590
|
+
}
|
|
591
|
+
catch (e) {
|
|
592
|
+
handleHttp(e);
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
// ── Checkpoint operations ──
|
|
597
|
+
function registerCheckpoints(spacesCmd) {
|
|
598
|
+
const cpCmd = spacesCmd
|
|
599
|
+
.command("checkpoints")
|
|
600
|
+
.description("Checkpoint management")
|
|
601
|
+
.hook("preAction", () => { requireSpace(spacesCmd); });
|
|
602
|
+
cpCmd
|
|
603
|
+
.command("ls")
|
|
604
|
+
.alias("list")
|
|
605
|
+
.description("List checkpoints")
|
|
606
|
+
.option("--json", "Output as JSON")
|
|
607
|
+
.action(async (opts) => {
|
|
608
|
+
const token = resolveToken() ?? missingAuth();
|
|
609
|
+
const spaceId = requireSpace(spacesCmd);
|
|
610
|
+
const client = createClient(token);
|
|
611
|
+
try {
|
|
612
|
+
const result = await client.space(spaceId).checkpoints.list();
|
|
613
|
+
if (opts.json)
|
|
614
|
+
return outJson(result);
|
|
615
|
+
if (result.checkpoints.length === 0) {
|
|
616
|
+
console.log(" (empty)");
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
table(result.checkpoints, [
|
|
620
|
+
{ key: "id", label: "ID" },
|
|
621
|
+
{ key: "commitHash", label: "Commit" },
|
|
622
|
+
{ key: "description", label: "Description" },
|
|
623
|
+
{ key: "createdAt", label: "Created" },
|
|
624
|
+
]);
|
|
625
|
+
}
|
|
626
|
+
catch (e) {
|
|
627
|
+
handleHttp(e);
|
|
628
|
+
}
|
|
629
|
+
});
|
|
630
|
+
cpCmd
|
|
631
|
+
.command("get <id>")
|
|
632
|
+
.description("Checkpoint details")
|
|
633
|
+
.option("--json", "Output as JSON")
|
|
634
|
+
.action(async (id, opts) => {
|
|
635
|
+
const token = resolveToken() ?? missingAuth();
|
|
636
|
+
const spaceId = requireSpace(spacesCmd);
|
|
637
|
+
const client = createClient(token);
|
|
638
|
+
try {
|
|
639
|
+
const result = await client.space(spaceId).checkpoints.get(id);
|
|
640
|
+
if (opts.json)
|
|
641
|
+
return outJson(result);
|
|
642
|
+
table([result.checkpoint], [
|
|
643
|
+
{ key: "id", label: "ID" },
|
|
644
|
+
{ key: "commitHash", label: "Commit" },
|
|
645
|
+
{ key: "description", label: "Description" },
|
|
646
|
+
{ key: "forkCount", label: "Forks" },
|
|
647
|
+
{ key: "createdAt", label: "Created" },
|
|
648
|
+
]);
|
|
649
|
+
}
|
|
650
|
+
catch (e) {
|
|
651
|
+
handleHttp(e);
|
|
652
|
+
}
|
|
653
|
+
});
|
|
654
|
+
cpCmd
|
|
655
|
+
.command("create [description]")
|
|
656
|
+
.description("Create a checkpoint")
|
|
657
|
+
.option("--json", "Output as JSON")
|
|
658
|
+
.action(async (description, opts) => {
|
|
659
|
+
const token = resolveToken() ?? missingAuth();
|
|
660
|
+
const spaceId = requireSpace(spacesCmd);
|
|
661
|
+
const client = createClient(token);
|
|
662
|
+
try {
|
|
663
|
+
const result = await client.space(spaceId).checkpoints.create(description ?? null);
|
|
664
|
+
if (opts.json)
|
|
665
|
+
return outJson(result);
|
|
666
|
+
ok(`Checkpoint created — taskRunId: ${result.taskRunId}`);
|
|
667
|
+
}
|
|
668
|
+
catch (e) {
|
|
669
|
+
handleHttp(e);
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
function missingAuth() {
|
|
674
|
+
return error("Not authenticated", "Run 'cohub auth login <token>'");
|
|
675
|
+
}
|