@neta-art/cohub-cli 3.12.0 → 5.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 +23 -51
- package/dist/board-export.js +5 -5
- package/dist/commands/board-domain.js +4 -0
- package/dist/commands/boards/animation.js +65 -142
- package/dist/commands/boards/appearance.js +32 -29
- package/dist/commands/boards/context.d.ts +17 -1
- package/dist/commands/boards/context.js +38 -1
- package/dist/commands/boards/examples.d.ts +4 -0
- package/dist/commands/boards/examples.js +224 -0
- package/dist/commands/boards/items.d.ts +2 -0
- package/dist/commands/boards/items.js +140 -0
- package/dist/commands/boards/nodes.d.ts +1 -0
- package/dist/commands/boards/nodes.js +8 -68
- package/dist/commands/boards.d.ts +6 -11
- package/dist/commands/boards.js +46 -148
- package/dist/commands/cron-jobs.js +1 -1
- package/dist/commands/tasks.js +1 -1
- package/dist/commands/works.js +2 -2
- package/package.json +2 -2
package/dist/commands/boards.js
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import { BOARD_CREATE_INPUT_MAX_BYTES,
|
|
2
|
+
import { BOARD_CREATE_INPUT_MAX_BYTES, parseBoardJsonObject, readBoardJsonObject, resolveBoardId, writeBoardOutput, } from "../board-command-support.js";
|
|
3
3
|
import { BOARD_EXPORT_FORMATS, formatFromPath, runBoardExport } from "../board-export.js";
|
|
4
4
|
import { registerBoardDomainCommands } from "./board-domain.js";
|
|
5
5
|
import { createClient, createRealtimeClient } from "../client.js";
|
|
6
6
|
import { error, handleHttp, json as outJson, jsonRequested, ok, table } from "../output.js";
|
|
7
7
|
import { resolveSpace } from "../space.js";
|
|
8
|
-
const INSPECT_SECTIONS = ["nodes", "connections", "effects", "sequences", "clips", "playback"];
|
|
9
8
|
export const parseJsonObject = parseBoardJsonObject;
|
|
10
|
-
export async function readJsonObject(source, maxBytes = BOARD_TRANSACTION_INPUT_MAX_BYTES) {
|
|
11
|
-
return readBoardJsonObject(source, maxBytes);
|
|
12
|
-
}
|
|
13
9
|
function parseNumber(value, name, options = {}) {
|
|
14
10
|
if (!value.trim())
|
|
15
11
|
throw new Error(`${name} must be a finite number`);
|
|
@@ -24,15 +20,6 @@ function parseNumber(value, name, options = {}) {
|
|
|
24
20
|
throw new Error(`${name} must be at most ${options.max}`);
|
|
25
21
|
return parsed;
|
|
26
22
|
}
|
|
27
|
-
export function parseInspectSections(value) {
|
|
28
|
-
if (!value)
|
|
29
|
-
return undefined;
|
|
30
|
-
const sections = value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
31
|
-
const unknown = sections.filter((section) => !INSPECT_SECTIONS.includes(section));
|
|
32
|
-
if (unknown.length > 0)
|
|
33
|
-
throw new Error(`Unknown Board section: ${unknown.join(", ")}`);
|
|
34
|
-
return [...new Set(sections)];
|
|
35
|
-
}
|
|
36
23
|
export function parseViewport(value) {
|
|
37
24
|
if (!value)
|
|
38
25
|
return undefined;
|
|
@@ -51,47 +38,11 @@ export function parseViewport(value) {
|
|
|
51
38
|
throw new Error("viewport width and height must be greater than zero");
|
|
52
39
|
return { x, y, width, height };
|
|
53
40
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
throw new Error("transaction input must not contain boardId");
|
|
57
|
-
if (!Array.isArray(input.operations))
|
|
58
|
-
throw new Error("transaction input must contain an operations array");
|
|
59
|
-
const rawBaseVersion = options.baseVersion ?? input.baseVersion;
|
|
60
|
-
if (rawBaseVersion === undefined)
|
|
61
|
-
throw new Error("baseVersion is required in input or --base-version");
|
|
62
|
-
const baseVersion = parseNumber(String(rawBaseVersion), "baseVersion", { min: 0, integer: true });
|
|
63
|
-
const rawTxId = options.txId ?? input.txId;
|
|
64
|
-
if (rawTxId !== undefined && (typeof rawTxId !== "string" || !rawTxId.trim())) {
|
|
65
|
-
throw new Error("txId must be a non-empty string");
|
|
66
|
-
}
|
|
67
|
-
return {
|
|
68
|
-
...input,
|
|
69
|
-
txId: typeof rawTxId === "string" ? rawTxId : randomUUID(),
|
|
70
|
-
baseVersion,
|
|
71
|
-
operations: input.operations,
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
function showBoard(result) {
|
|
75
|
-
table([
|
|
76
|
-
{
|
|
77
|
-
id: result.board.id,
|
|
78
|
-
title: result.board.title,
|
|
79
|
-
version: result.board.version,
|
|
80
|
-
nodes: result.nodes.length,
|
|
81
|
-
connections: result.connections.length,
|
|
82
|
-
effects: result.effects.length,
|
|
83
|
-
sequences: result.sequences.length,
|
|
84
|
-
clips: result.clips.length,
|
|
85
|
-
},
|
|
86
|
-
], [
|
|
41
|
+
function showCreated(result) {
|
|
42
|
+
table([result.board], [
|
|
87
43
|
{ key: "id", label: "ID" },
|
|
88
44
|
{ key: "title", label: "Title" },
|
|
89
45
|
{ key: "version", label: "Version" },
|
|
90
|
-
{ key: "nodes", label: "Nodes" },
|
|
91
|
-
{ key: "connections", label: "Connections" },
|
|
92
|
-
{ key: "effects", label: "Effects" },
|
|
93
|
-
{ key: "sequences", label: "Sequences" },
|
|
94
|
-
{ key: "clips", label: "Clips" },
|
|
95
46
|
]);
|
|
96
47
|
}
|
|
97
48
|
function showSummary(result) {
|
|
@@ -107,35 +58,18 @@ function showSummary(result) {
|
|
|
107
58
|
{ key: "id", label: "ID" },
|
|
108
59
|
{ key: "title", label: "TITLE" },
|
|
109
60
|
{ key: "version", label: "VERSION" },
|
|
110
|
-
{ key: "
|
|
61
|
+
{ key: "items", label: "ITEMS" },
|
|
111
62
|
{ key: "connections", label: "CONNECTIONS" },
|
|
112
63
|
{ key: "effects", label: "EFFECTS" },
|
|
113
|
-
{ key: "
|
|
64
|
+
{ key: "compositions", label: "COMPOSITIONS" },
|
|
114
65
|
{ key: "background", label: "BACKGROUND" },
|
|
115
66
|
{ key: "updatedAt", label: "UPDATED" },
|
|
116
67
|
]);
|
|
117
68
|
}
|
|
118
|
-
function showValidation(result) {
|
|
119
|
-
table([{ valid: result.valid, diagnostics: result.diagnostics.length }], [
|
|
120
|
-
{ key: "valid", label: "Valid" },
|
|
121
|
-
{ key: "diagnostics", label: "Diagnostics" },
|
|
122
|
-
]);
|
|
123
|
-
if (result.diagnostics.length > 0) {
|
|
124
|
-
console.log();
|
|
125
|
-
table(result.diagnostics, [
|
|
126
|
-
{ key: "severity", label: "Severity" },
|
|
127
|
-
{ key: "code", label: "Code" },
|
|
128
|
-
{ key: "path", label: "Path" },
|
|
129
|
-
{ key: "message", label: "Message" },
|
|
130
|
-
]);
|
|
131
|
-
}
|
|
132
|
-
console.log();
|
|
133
|
-
table([result.peakCost], Object.keys(result.peakCost).map((key) => ({ key, label: key })));
|
|
134
|
-
}
|
|
135
69
|
function showPlayback(result) {
|
|
136
70
|
table([result], [
|
|
137
71
|
{ key: "playbackId", label: "Playback ID" },
|
|
138
|
-
{ key: "
|
|
72
|
+
{ key: "compositionId", label: "Composition" },
|
|
139
73
|
{ key: "status", label: "Status" },
|
|
140
74
|
{ key: "position", label: "Position" },
|
|
141
75
|
{ key: "timeScale", label: "Time Scale" },
|
|
@@ -158,38 +92,6 @@ function capabilityUnits(schema) {
|
|
|
158
92
|
return detail ? [`${field}:${detail}`] : [];
|
|
159
93
|
}).join(", ");
|
|
160
94
|
}
|
|
161
|
-
function registerTransactionCommand(boards, name) {
|
|
162
|
-
withJson(boards.command(`${name} <board>`)
|
|
163
|
-
.description(name === "validate" ? "Validate a transaction" : "Apply a transaction")
|
|
164
|
-
.requiredOption("-i, --input <file>", "Transaction JSON file; use - for stdin")
|
|
165
|
-
.option("--tx-id <id>", "Override txId; generated when omitted")
|
|
166
|
-
.option("--base-version <version>", "Override baseVersion")
|
|
167
|
-
.addHelpText("after", `
|
|
168
|
-
Input example:
|
|
169
|
-
{"baseVersion":12,"operations":[{"type":"board.patch","payload":{"patch":{"title":"Launch plan"}}}]}
|
|
170
|
-
|
|
171
|
-
Prefer semantic commands such as boards background, nodes, effects, or sequences for common edits.`))
|
|
172
|
-
.action(async (target, options) => {
|
|
173
|
-
try {
|
|
174
|
-
const transaction = createTransactionInput(await readJsonObject(options.input, BOARD_TRANSACTION_INPUT_MAX_BYTES), options);
|
|
175
|
-
const spaceId = resolveSpace(boards);
|
|
176
|
-
const boardId = await resolveBoardId(spaceId, target);
|
|
177
|
-
const board = createClient().space(spaceId).board(boardId);
|
|
178
|
-
const result = await board[name](transaction);
|
|
179
|
-
if (jsonRequested(options))
|
|
180
|
-
return outJson(result);
|
|
181
|
-
if (name === "validate")
|
|
182
|
-
showValidation(result);
|
|
183
|
-
else {
|
|
184
|
-
ok(`Board updated to version ${result.board.version}`);
|
|
185
|
-
showBoard(result);
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
catch (cause) {
|
|
189
|
-
handleHttp(cause);
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
95
|
function commandId(options) {
|
|
194
96
|
return options.commandId?.trim() || randomUUID();
|
|
195
97
|
}
|
|
@@ -315,14 +217,16 @@ export function registerBoards(program) {
|
|
|
315
217
|
.description("Create a Board")
|
|
316
218
|
.option("--title <title>", "Board title")
|
|
317
219
|
.option("--mutation-id <id>", "Stable id for safe retries")
|
|
318
|
-
.option("-i, --input <file>", "
|
|
220
|
+
.option("-i, --input <file>", "Semantic Board seed JSON; use - for stdin")
|
|
319
221
|
.addHelpText("after", `
|
|
320
|
-
|
|
321
|
-
|
|
222
|
+
Create an empty Board, or provide items, connections, effects, compositions, and metadata in --input.
|
|
223
|
+
Generate an editable seed:
|
|
224
|
+
cohub boards examples create > board.json
|
|
225
|
+
cohub boards create plan.board -i board.json`))
|
|
322
226
|
.action(async (path, options) => {
|
|
323
227
|
try {
|
|
324
228
|
const content = options.input
|
|
325
|
-
? await
|
|
229
|
+
? await readBoardJsonObject(options.input, BOARD_CREATE_INPUT_MAX_BYTES)
|
|
326
230
|
: {};
|
|
327
231
|
if ("path" in content || "title" in content) {
|
|
328
232
|
throw new Error("create input must not contain path or title; use the command argument and --title");
|
|
@@ -338,7 +242,7 @@ For normal use, create an empty Board and add content with boards nodes, effects
|
|
|
338
242
|
if (jsonRequested(options))
|
|
339
243
|
return outJson(result);
|
|
340
244
|
ok(`Board created: ${result.board.id}`);
|
|
341
|
-
|
|
245
|
+
showCreated(result);
|
|
342
246
|
}
|
|
343
247
|
catch (cause) {
|
|
344
248
|
handleHttp(cause);
|
|
@@ -346,31 +250,30 @@ For normal use, create an empty Board and add content with boards nodes, effects
|
|
|
346
250
|
});
|
|
347
251
|
withJson(boards.command("inspect <board>")
|
|
348
252
|
.alias("get")
|
|
349
|
-
.description("
|
|
350
|
-
.
|
|
351
|
-
|
|
253
|
+
.description("Show Board metadata and semantic resource counts")
|
|
254
|
+
.addHelpText("after", `
|
|
255
|
+
Inspect content with:
|
|
256
|
+
cohub boards items list <board> --json
|
|
257
|
+
cohub boards effects list <board> --json
|
|
258
|
+
cohub boards compositions list <board> --json`))
|
|
352
259
|
.action(async (target, options) => {
|
|
353
260
|
try {
|
|
354
261
|
const spaceId = resolveSpace(boards);
|
|
355
262
|
const boardId = await resolveBoardId(spaceId, target);
|
|
356
|
-
const
|
|
357
|
-
if (!jsonRequested(options) && !options.include && !options.viewport) {
|
|
358
|
-
return showSummary(await board.summary());
|
|
359
|
-
}
|
|
360
|
-
const result = await board.inspect({
|
|
361
|
-
include: parseInspectSections(options.include),
|
|
362
|
-
viewport: parseViewport(options.viewport),
|
|
363
|
-
});
|
|
263
|
+
const result = await createClient().space(spaceId).board(boardId).summary();
|
|
364
264
|
if (jsonRequested(options))
|
|
365
265
|
return outJson(result);
|
|
366
|
-
|
|
266
|
+
showSummary(result);
|
|
367
267
|
}
|
|
368
268
|
catch (cause) {
|
|
369
269
|
handleHttp(cause);
|
|
370
270
|
}
|
|
371
271
|
});
|
|
372
272
|
withJson(boards.command("capabilities <board>")
|
|
373
|
-
.description("Show
|
|
273
|
+
.description("Show authoring schemas and runtime capabilities")
|
|
274
|
+
.addHelpText("after", `
|
|
275
|
+
Use --json for complete Item, patch, mutation, effect, Composition, and create JSON Schemas.
|
|
276
|
+
Use boards examples for editable starter JSON.`))
|
|
374
277
|
.action(async (target, options) => {
|
|
375
278
|
try {
|
|
376
279
|
const spaceId = resolveSpace(boards);
|
|
@@ -390,46 +293,41 @@ For normal use, create an empty Board and add content with boards nodes, effects
|
|
|
390
293
|
{ key: "coordinates", label: "Coordinates / units" },
|
|
391
294
|
{ key: "digest", label: "Digest" },
|
|
392
295
|
]);
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
{ key: "arrowEndpoints", label: "Arrow endpoints" },
|
|
408
|
-
]);
|
|
409
|
-
}
|
|
296
|
+
console.log();
|
|
297
|
+
table([{
|
|
298
|
+
types: result.items.types.join(", "),
|
|
299
|
+
colors: result.items.colors.join(", "),
|
|
300
|
+
shapes: result.items.shapes.join(", "),
|
|
301
|
+
drawPoints: result.items.coordinates.drawPoints,
|
|
302
|
+
arrowEndpoints: result.items.coordinates.arrowEndpoints,
|
|
303
|
+
}], [
|
|
304
|
+
{ key: "types", label: "Item types" },
|
|
305
|
+
{ key: "colors", label: "Colors" },
|
|
306
|
+
{ key: "shapes", label: "Shapes" },
|
|
307
|
+
{ key: "drawPoints", label: "Draw points" },
|
|
308
|
+
{ key: "arrowEndpoints", label: "Arrow endpoints" },
|
|
309
|
+
]);
|
|
410
310
|
}
|
|
411
311
|
catch (cause) {
|
|
412
312
|
handleHttp(cause);
|
|
413
313
|
}
|
|
414
314
|
});
|
|
415
|
-
registerTransactionCommand(boards, "validate");
|
|
416
|
-
registerTransactionCommand(boards, "apply");
|
|
417
315
|
registerBoardDomainCommands(boards);
|
|
418
316
|
registerExportCommand(boards);
|
|
419
|
-
withJson(boards.command("play <board> <
|
|
317
|
+
withJson(boards.command("play <board> <composition-id>")
|
|
420
318
|
.description("Start shared playback")
|
|
421
319
|
.option("--position <time>", "Initial position in milliseconds")
|
|
422
320
|
.option("--time-scale <scale>", "Playback speed from 0 to 4")
|
|
423
321
|
.option("--seed <seed>", "Deterministic playback seed")
|
|
424
322
|
.option("--command-id <id>", "Idempotency command ID"))
|
|
425
|
-
.action(async (target,
|
|
323
|
+
.action(async (target, compositionId, options) => {
|
|
426
324
|
try {
|
|
427
325
|
const spaceId = resolveSpace(boards);
|
|
428
326
|
const boardId = await resolveBoardId(spaceId, target);
|
|
429
327
|
const result = await createClient().space(spaceId).board(boardId).play({
|
|
430
328
|
commandId: commandId(options),
|
|
431
329
|
type: "play",
|
|
432
|
-
|
|
330
|
+
compositionId,
|
|
433
331
|
shared: true,
|
|
434
332
|
...(options.position === undefined ? {} : { position: parseNumber(options.position, "position", { min: 0 }) }),
|
|
435
333
|
...(options.timeScale === undefined ? {} : { timeScale: parseNumber(options.timeScale, "timeScale", { min: Number.EPSILON, max: 4 }) }),
|
|
@@ -516,13 +414,13 @@ For normal use, create an empty Board and add content with boards nodes, effects
|
|
|
516
414
|
process.stdout.write(`${JSON.stringify(event)}\n`);
|
|
517
415
|
return;
|
|
518
416
|
}
|
|
519
|
-
if (event.type === "board.
|
|
520
|
-
process.stdout.write(`version ${event.payload.version}
|
|
417
|
+
if (event.type === "board.changed") {
|
|
418
|
+
process.stdout.write(`version ${event.payload.version} mutation ${event.payload.mutationId}\n`);
|
|
521
419
|
}
|
|
522
420
|
else if (event.type === "board.playback.changed") {
|
|
523
|
-
process.stdout.write(`${event.payload.status}
|
|
421
|
+
process.stdout.write(`${event.payload.status} composition ${event.payload.compositionId} position ${event.payload.position}\n`);
|
|
524
422
|
}
|
|
525
|
-
else {
|
|
423
|
+
else if (event.type === "board.awareness.updated") {
|
|
526
424
|
process.stdout.write(`awareness ${event.payload.actorName} ${event.payload.update.type}\n`);
|
|
527
425
|
}
|
|
528
426
|
},
|
|
@@ -9,7 +9,7 @@ function readJsonObject(pathOrJson) {
|
|
|
9
9
|
return parsed;
|
|
10
10
|
}
|
|
11
11
|
export function registerCronJobs(program) {
|
|
12
|
-
const cmd = program.command("cron-jobs"
|
|
12
|
+
const cmd = program.command("cron-jobs").description("Scheduled jobs");
|
|
13
13
|
cmd
|
|
14
14
|
.command("ls [spaceId]")
|
|
15
15
|
.alias("list")
|
package/dist/commands/tasks.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createClient } from "../client.js";
|
|
|
2
2
|
import { table, json as outJson, jsonRequested, handleHttp } from "../output.js";
|
|
3
3
|
export function registerTasks(program, dependencies = {}) {
|
|
4
4
|
const getClient = dependencies.createClient ?? createClient;
|
|
5
|
-
const cmd = program.command("tasks"
|
|
5
|
+
const cmd = program.command("tasks").description("Task runs");
|
|
6
6
|
cmd
|
|
7
7
|
.command("ls")
|
|
8
8
|
.alias("list")
|
package/dist/commands/works.js
CHANGED
|
@@ -259,7 +259,7 @@ export function registerWorks(program) {
|
|
|
259
259
|
.option("--status <status>", "Work status: published, disabled")
|
|
260
260
|
.option("--visibility <visibility>", "Work visibility: public, space")
|
|
261
261
|
.option("--work-scope <scope>", "Scope granted to the work runtime (space.view, session.view, file.view, taskrun.view)", collectOption, [])
|
|
262
|
-
.option("--viewer-scope <scope>", "Scope viewers may request (session.prompt.readonly, session.prompt.fullaccess, generation.create, user.space.list, user.session.list, user.usage.read)", collectOption, [])
|
|
262
|
+
.option("--viewer-scope <scope>", "Scope viewers may request (taskrun.view, session.prompt.readonly, session.prompt.fullaccess, generation.create, user.space.list, user.session.list, user.usage.read)", collectOption, [])
|
|
263
263
|
.option("--meta <json>", "Work metadata as a JSON object")
|
|
264
264
|
.option("--hide-cohub-bar", "Hide the Cohub footer bar on the public work page")
|
|
265
265
|
.option("--show-cohub-bar", "Show the Cohub footer bar on the public work page")
|
|
@@ -338,7 +338,7 @@ export function registerWorks(program) {
|
|
|
338
338
|
.option("--status <status>", "Work status: published, disabled")
|
|
339
339
|
.option("--visibility <visibility>", "Work visibility: public, space")
|
|
340
340
|
.option("--work-scope <scope>", "Scope granted to the work runtime (space.view, session.view, file.view, taskrun.view)", collectOption, [])
|
|
341
|
-
.option("--viewer-scope <scope>", "Scope viewers may request (session.prompt.readonly, session.prompt.fullaccess, generation.create, user.space.list, user.session.list, user.usage.read)", collectOption, [])
|
|
341
|
+
.option("--viewer-scope <scope>", "Scope viewers may request (taskrun.view, session.prompt.readonly, session.prompt.fullaccess, generation.create, user.space.list, user.session.list, user.usage.read)", collectOption, [])
|
|
342
342
|
.option("--clear-work-scopes", "Clear work runtime scopes")
|
|
343
343
|
.option("--clear-viewer-scopes", "Clear viewer-requestable scopes")
|
|
344
344
|
.option("--meta <json>", "Work metadata as a JSON object")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neta-art/cohub-cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "CLI for Cohub — spaces, sessions, and agent collaboration.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"commander": "^15.0.0",
|
|
20
20
|
"pixi.js": "^8.19.0",
|
|
21
21
|
"sharp": "^0.35.3",
|
|
22
|
-
"@neta-art/cohub": "
|
|
22
|
+
"@neta-art/cohub": "7.0.0"
|
|
23
23
|
},
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"access": "public"
|