@neta-art/cohub-cli 4.0.0 → 6.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.
@@ -1,15 +1,11 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import { BOARD_CREATE_INPUT_MAX_BYTES, BOARD_TRANSACTION_INPUT_MAX_BYTES, parseBoardJsonObject, readBoardJsonObject, resolveBoardId, writeBoardOutput, } from "../board-command-support.js";
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", "compositions", "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,45 +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
- export function createTransactionInput(input, options) {
55
- if ("boardId" in input)
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
- compositions: result.compositions.length,
84
- },
85
- ], [
41
+ function showCreated(result) {
42
+ table([result.board], [
86
43
  { key: "id", label: "ID" },
87
44
  { key: "title", label: "Title" },
88
45
  { key: "version", label: "Version" },
89
- { key: "nodes", label: "Nodes" },
90
- { key: "connections", label: "Connections" },
91
- { key: "effects", label: "Effects" },
92
- { key: "compositions", label: "Compositions" },
93
46
  ]);
94
47
  }
95
48
  function showSummary(result) {
@@ -105,7 +58,7 @@ function showSummary(result) {
105
58
  { key: "id", label: "ID" },
106
59
  { key: "title", label: "TITLE" },
107
60
  { key: "version", label: "VERSION" },
108
- { key: "nodes", label: "NODES" },
61
+ { key: "items", label: "ITEMS" },
109
62
  { key: "connections", label: "CONNECTIONS" },
110
63
  { key: "effects", label: "EFFECTS" },
111
64
  { key: "compositions", label: "COMPOSITIONS" },
@@ -113,23 +66,6 @@ function showSummary(result) {
113
66
  { key: "updatedAt", label: "UPDATED" },
114
67
  ]);
115
68
  }
116
- function showValidation(result) {
117
- table([{ valid: result.valid, diagnostics: result.diagnostics.length }], [
118
- { key: "valid", label: "Valid" },
119
- { key: "diagnostics", label: "Diagnostics" },
120
- ]);
121
- if (result.diagnostics.length > 0) {
122
- console.log();
123
- table(result.diagnostics, [
124
- { key: "severity", label: "Severity" },
125
- { key: "code", label: "Code" },
126
- { key: "path", label: "Path" },
127
- { key: "message", label: "Message" },
128
- ]);
129
- }
130
- console.log();
131
- table([result.peakCost], Object.keys(result.peakCost).map((key) => ({ key, label: key })));
132
- }
133
69
  function showPlayback(result) {
134
70
  table([result], [
135
71
  { key: "playbackId", label: "Playback ID" },
@@ -156,38 +92,6 @@ function capabilityUnits(schema) {
156
92
  return detail ? [`${field}:${detail}`] : [];
157
93
  }).join(", ");
158
94
  }
159
- function registerTransactionCommand(boards, name) {
160
- withJson(boards.command(`${name} <board>`)
161
- .description(name === "validate" ? "Validate a transaction" : "Apply a transaction")
162
- .requiredOption("-i, --input <file>", "Transaction JSON file; use - for stdin")
163
- .option("--tx-id <id>", "Override txId; generated when omitted")
164
- .option("--base-version <version>", "Override baseVersion")
165
- .addHelpText("after", `
166
- Input example:
167
- {"baseVersion":12,"operations":[{"type":"board.patch","payload":{"patch":{"title":"Launch plan"}}}]}
168
-
169
- Prefer semantic commands such as boards background, nodes, effects, or compositions for common edits.`))
170
- .action(async (target, options) => {
171
- try {
172
- const transaction = createTransactionInput(await readJsonObject(options.input, BOARD_TRANSACTION_INPUT_MAX_BYTES), options);
173
- const spaceId = resolveSpace(boards);
174
- const boardId = await resolveBoardId(spaceId, target);
175
- const board = createClient().space(spaceId).board(boardId);
176
- const result = await board[name](transaction);
177
- if (jsonRequested(options))
178
- return outJson(result);
179
- if (name === "validate")
180
- showValidation(result);
181
- else {
182
- const receipt = result;
183
- ok(`Board updated to version ${receipt.board.version}`);
184
- }
185
- }
186
- catch (cause) {
187
- handleHttp(cause);
188
- }
189
- });
190
- }
191
95
  function commandId(options) {
192
96
  return options.commandId?.trim() || randomUUID();
193
97
  }
@@ -313,14 +217,16 @@ export function registerBoards(program) {
313
217
  .description("Create a Board")
314
218
  .option("--title <title>", "Board title")
315
219
  .option("--mutation-id <id>", "Stable id for safe retries")
316
- .option("-i, --input <file>", "BoardCreateInput fields; use - for stdin")
220
+ .option("-i, --input <file>", "Semantic Board seed JSON; use - for stdin")
317
221
  .addHelpText("after", `
318
- For normal use, create an empty Board and add content with boards nodes, effects, and compositions.
319
- --input is intended for bulk creation and accepts BoardCreateInput fields except path and title.`))
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`))
320
226
  .action(async (path, options) => {
321
227
  try {
322
228
  const content = options.input
323
- ? await readJsonObject(options.input, BOARD_CREATE_INPUT_MAX_BYTES)
229
+ ? await readBoardJsonObject(options.input, BOARD_CREATE_INPUT_MAX_BYTES)
324
230
  : {};
325
231
  if ("path" in content || "title" in content) {
326
232
  throw new Error("create input must not contain path or title; use the command argument and --title");
@@ -336,7 +242,7 @@ For normal use, create an empty Board and add content with boards nodes, effects
336
242
  if (jsonRequested(options))
337
243
  return outJson(result);
338
244
  ok(`Board created: ${result.board.id}`);
339
- showBoard(result);
245
+ showCreated(result);
340
246
  }
341
247
  catch (cause) {
342
248
  handleHttp(cause);
@@ -344,31 +250,30 @@ For normal use, create an empty Board and add content with boards nodes, effects
344
250
  });
345
251
  withJson(boards.command("inspect <board>")
346
252
  .alias("get")
347
- .description("Inspect a Board")
348
- .option("--include <sections>", "Comma-separated nodes,connections,effects,compositions,playback")
349
- .option("--viewport <rect>", "Viewport as x,y,width,height"))
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`))
350
259
  .action(async (target, options) => {
351
260
  try {
352
261
  const spaceId = resolveSpace(boards);
353
262
  const boardId = await resolveBoardId(spaceId, target);
354
- const board = createClient().space(spaceId).board(boardId);
355
- if (!jsonRequested(options) && !options.include && !options.viewport) {
356
- return showSummary(await board.summary());
357
- }
358
- const result = await board.inspect({
359
- include: parseInspectSections(options.include),
360
- viewport: parseViewport(options.viewport),
361
- });
263
+ const result = await createClient().space(spaceId).board(boardId).summary();
362
264
  if (jsonRequested(options))
363
265
  return outJson(result);
364
- showBoard(result);
266
+ showSummary(result);
365
267
  }
366
268
  catch (cause) {
367
269
  handleHttp(cause);
368
270
  }
369
271
  });
370
272
  withJson(boards.command("capabilities <board>")
371
- .description("Show supported capabilities"))
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.`))
372
277
  .action(async (target, options) => {
373
278
  try {
374
279
  const spaceId = resolveSpace(boards);
@@ -388,30 +293,25 @@ For normal use, create an empty Board and add content with boards nodes, effects
388
293
  { key: "coordinates", label: "Coordinates / units" },
389
294
  { key: "digest", label: "Digest" },
390
295
  ]);
391
- const nodes = result.nodes;
392
- if (nodes) {
393
- console.log();
394
- table([{
395
- types: nodes.types.join(", "),
396
- colors: nodes.colors.join(", "),
397
- geos: nodes.geos.join(", "),
398
- drawPoints: nodes.coordinates.drawPoints,
399
- arrowEndpoints: nodes.coordinates.arrowEndpoints,
400
- }], [
401
- { key: "types", label: "Node types" },
402
- { key: "colors", label: "Colors" },
403
- { key: "geos", label: "Geo kinds" },
404
- { key: "drawPoints", label: "Draw points" },
405
- { key: "arrowEndpoints", label: "Arrow endpoints" },
406
- ]);
407
- }
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
+ ]);
408
310
  }
409
311
  catch (cause) {
410
312
  handleHttp(cause);
411
313
  }
412
314
  });
413
- registerTransactionCommand(boards, "validate");
414
- registerTransactionCommand(boards, "apply");
415
315
  registerBoardDomainCommands(boards);
416
316
  registerExportCommand(boards);
417
317
  withJson(boards.command("play <board> <composition-id>")
@@ -514,13 +414,13 @@ For normal use, create an empty Board and add content with boards nodes, effects
514
414
  process.stdout.write(`${JSON.stringify(event)}\n`);
515
415
  return;
516
416
  }
517
- if (event.type === "board.transaction.applied") {
518
- process.stdout.write(`version ${event.payload.version} transaction ${event.payload.txId} operations ${event.payload.operations.length}\n`);
417
+ if (event.type === "board.changed") {
418
+ process.stdout.write(`version ${event.payload.version} mutation ${event.payload.mutationId}\n`);
519
419
  }
520
420
  else if (event.type === "board.playback.changed") {
521
421
  process.stdout.write(`${event.payload.status} composition ${event.payload.compositionId} position ${event.payload.position}\n`);
522
422
  }
523
- else {
423
+ else if (event.type === "board.awareness.updated") {
524
424
  process.stdout.write(`awareness ${event.payload.actorName} ${event.payload.update.type}\n`);
525
425
  }
526
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", { hidden: true }).description("Scheduled jobs");
12
+ const cmd = program.command("cron-jobs").description("Scheduled jobs");
13
13
  cmd
14
14
  .command("ls [spaceId]")
15
15
  .alias("list")
@@ -0,0 +1,3 @@
1
+ import type { Command } from "commander";
2
+ export declare function registerDesktop(program: Command): void;
3
+ export declare function registerLegacyUi(program: Command): void;
@@ -0,0 +1,219 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { HttpError, } from "@neta-art/cohub";
3
+ import { parseAppRef, DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS, DESKTOP_COMMAND_MAX_TIMEOUT_MS, } from "@neta-art/cohub";
4
+ import { createClient } from "../client.js";
5
+ import { error, handleHttp, json as outJson, jsonRequested, ok } from "../output.js";
6
+ import { getAppByRef } from "../app-ref.js";
7
+ const FILE_SCHEME = "file://";
8
+ const APP_SCHEME = "app://";
9
+ const LEGACY_WORK_SCHEME = "work://";
10
+ function optionalSpaceId(command) {
11
+ let current = command;
12
+ while (current) {
13
+ const opts = current.opts();
14
+ if (typeof opts.space === "string" && opts.space.trim())
15
+ return opts.space.trim();
16
+ current = current.parent ?? null;
17
+ }
18
+ return process.env.COHUB_SPACE_ID?.trim() || undefined;
19
+ }
20
+ function parseFilePath(value) {
21
+ const path = value.slice(FILE_SCHEME.length).trim();
22
+ if (!path ||
23
+ path.startsWith("/") ||
24
+ path.includes("\0") ||
25
+ path.split("/").some((segment) => segment === "..") ||
26
+ path.includes("\\")) {
27
+ return error("Invalid file path", "Use a relative Space path after file://.");
28
+ }
29
+ return path;
30
+ }
31
+ function hasFileScheme(value) {
32
+ return value.toLowerCase().startsWith(FILE_SCHEME);
33
+ }
34
+ function hasAppScheme(value) {
35
+ const lowered = value.toLowerCase();
36
+ return lowered.startsWith(APP_SCHEME) || lowered.startsWith(LEGACY_WORK_SCHEME);
37
+ }
38
+ function readCallInput(opts) {
39
+ if (opts.data !== undefined && opts.input !== undefined) {
40
+ return error("Conflicting input", "Use either --data or --input, not both.");
41
+ }
42
+ const raw = opts.data !== undefined
43
+ ? opts.data
44
+ : opts.input !== undefined
45
+ ? opts.input === "-"
46
+ ? readFileSync(0, "utf-8")
47
+ : readFileSync(opts.input, "utf-8")
48
+ : undefined;
49
+ if (raw === undefined)
50
+ return undefined;
51
+ try {
52
+ return JSON.parse(raw);
53
+ }
54
+ catch {
55
+ return error("Invalid input", "Call input must be valid JSON.");
56
+ }
57
+ }
58
+ function parseTimeout(value) {
59
+ if (!value)
60
+ return DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS;
61
+ const parsed = Math.floor(Number(value));
62
+ if (!Number.isFinite(parsed) || parsed <= 0 || parsed > DESKTOP_COMMAND_MAX_TIMEOUT_MS) {
63
+ return error("Invalid timeout", `--timeout-ms must be between 1 and ${DESKTOP_COMMAND_MAX_TIMEOUT_MS} milliseconds.`);
64
+ }
65
+ return parsed;
66
+ }
67
+ async function resolveAppTarget(client, ref) {
68
+ const normalized = hasAppScheme(ref) ? ref.replace(/^[a-zA-Z]+:\/\//, "") : ref;
69
+ const parsed = parseAppRef(normalized);
70
+ const detail = await getAppByRef(client, normalized);
71
+ return {
72
+ kind: "app",
73
+ appId: detail.app.id,
74
+ label: detail.app.slug,
75
+ launch: {
76
+ ...(parsed.search ? { search: parsed.search } : {}),
77
+ ...(parsed.hash ? { hash: parsed.hash } : {}),
78
+ },
79
+ };
80
+ }
81
+ async function resolveOpenTarget(client, command, value) {
82
+ if (hasFileScheme(value))
83
+ return { kind: "file", path: parseFilePath(value) };
84
+ if (hasAppScheme(value))
85
+ return resolveAppTarget(client, value);
86
+ const spaceId = optionalSpaceId(command);
87
+ if (spaceId) {
88
+ try {
89
+ await client.space(spaceId).files.read(value);
90
+ return { kind: "file", path: value };
91
+ }
92
+ catch (cause) {
93
+ if (!(cause instanceof HttpError) || cause.status !== 404)
94
+ throw cause;
95
+ }
96
+ }
97
+ return resolveAppTarget(client, value);
98
+ }
99
+ function reportDispatch(record) {
100
+ if (record.status !== "pending") {
101
+ reportOutcome(record, Boolean(record.command.call));
102
+ return;
103
+ }
104
+ ok(`Desktop command dispatched (${record.commandId})`);
105
+ }
106
+ function reportOutcome(record, called) {
107
+ if (record.status === "applied") {
108
+ ok(called ? "App window shown and method called" : "Window shown");
109
+ if (record.result !== undefined) {
110
+ console.log(typeof record.result === "string" ? record.result : JSON.stringify(record.result, null, 2));
111
+ }
112
+ return;
113
+ }
114
+ error(`Desktop command ${record.status}`, record.error?.message ?? "The Cohub desktop did not apply this command.");
115
+ }
116
+ async function openWindow(target, opts, command) {
117
+ const callInput = opts.call ? readCallInput(opts) : undefined;
118
+ if (!opts.call && (opts.data !== undefined || opts.input !== undefined)) {
119
+ return error("Missing --call", "--data and --input only apply together with --call.");
120
+ }
121
+ if (opts.noWait && opts.timeoutMs !== undefined) {
122
+ return error("Conflicting wait options", "Use either --no-wait or --timeout-ms, not both.");
123
+ }
124
+ const timeoutMs = parseTimeout(opts.timeoutMs);
125
+ const client = createClient();
126
+ try {
127
+ const resolved = await resolveOpenTarget(client, command, target);
128
+ if (resolved.kind === "file" && opts.call) {
129
+ return error("Unsupported option", "--call only applies to app targets.");
130
+ }
131
+ const call = opts.call
132
+ ? { method: opts.call, ...(callInput === undefined ? {} : { input: callInput }) }
133
+ : undefined;
134
+ const input = {
135
+ command: {
136
+ type: "desktop.open",
137
+ target: resolved.kind === "file"
138
+ ? resolved
139
+ : {
140
+ kind: "app",
141
+ appId: resolved.appId,
142
+ label: resolved.label,
143
+ ...(resolved.launch.search || resolved.launch.hash ? { launch: resolved.launch } : {}),
144
+ },
145
+ ...(call ? { call } : {}),
146
+ },
147
+ ...(opts.client ? { targetClientId: opts.client } : {}),
148
+ ...(opts.commandId ? { commandId: opts.commandId } : {}),
149
+ };
150
+ const record = opts.noWait
151
+ ? (await client.desktop.create(input)).command
152
+ : await client.desktop.run(input, { timeoutMs });
153
+ if (jsonRequested(opts))
154
+ return outJson(record);
155
+ if (opts.noWait)
156
+ return reportDispatch(record);
157
+ reportOutcome(record, Boolean(call));
158
+ }
159
+ catch (e) {
160
+ if (e instanceof Error && e.name === "AppRefParseError")
161
+ return error(e.message);
162
+ handleHttp(e);
163
+ }
164
+ }
165
+ const OPEN_HELP = `
166
+ Commands reach only the desktop that originated the current chat, resolved
167
+ from request provenance. Nothing else can be targeted.
168
+
169
+ Examples:
170
+ cohub desktop open <app-or-file>
171
+ cohub desktop open file://src/main.ts
172
+ cohub desktop open app://alice/studio/launch
173
+ cohub desktop open alice/studio/launch
174
+ cohub desktop open https://cohub.live/alice/studio/w/launch?view=timeline
175
+ cohub desktop open <app-id> --call selection.get
176
+ cohub desktop open <app-id> --call board.focus --data '{"nodeId":"n1"}'
177
+ `;
178
+ const OPEN_NOTES = `
179
+ Notes:
180
+ - Use file:// and app:// to make the target explicit; the legacy work://
181
+ scheme is still accepted.
182
+ - A plain target checks the current Space for a file before resolving an app.
183
+ - Opening a window is idempotent; repeating it re-activates the same tab.
184
+ - --call waits for the app to announce readiness, then invokes the method.
185
+ - Which methods exist is up to the app author.
186
+ `;
187
+ function registerOpen(parent, deprecated) {
188
+ const open = parent
189
+ .command(deprecated ? "preview <app-or-file>" : "open <app-or-file>")
190
+ .description(deprecated ? "Deprecated: use `cohub desktop open`" : "Open a file or app window on the Cohub desktop, optionally calling an app method")
191
+ .option("--call <method>", "Method the app registered via client.app.surface.handle()")
192
+ .option("--data <json>", "Inline JSON input for --call")
193
+ .option("-i, --input <file>", "JSON input file for --call; use - for stdin")
194
+ .option("--client <clientId>", "Target a specific desktop instance of your account")
195
+ .option("--command-id <id>", "Stable id so retries never dispatch twice")
196
+ .option("--no-wait", "Dispatch the command and exit without waiting for a result")
197
+ .option("--timeout-ms <ms>", `How long to wait for the desktop (default: ${DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS}; max: ${DESKTOP_COMMAND_MAX_TIMEOUT_MS})`)
198
+ .option("--json", "Output as JSON")
199
+ .action(async (target, opts, thisCommand) => {
200
+ if (deprecated) {
201
+ ok("Deprecated: `cohub ui preview` is now `cohub desktop open`.");
202
+ }
203
+ await openWindow(target, opts, thisCommand);
204
+ });
205
+ open.addHelpText("after", deprecated ? OPEN_NOTES : OPEN_HELP + OPEN_NOTES);
206
+ }
207
+ export function registerDesktop(program) {
208
+ const desktop = program
209
+ .command("desktop")
210
+ .description("Drive the Cohub desktop that started this chat");
211
+ registerOpen(desktop, false);
212
+ }
213
+ export function registerLegacyUi(program) {
214
+ const ui = program
215
+ .command("ui", { hidden: true })
216
+ .description("Deprecated: use `cohub desktop open`")
217
+ .addHelpText("after", OPEN_HELP);
218
+ registerOpen(ui, true);
219
+ }
@@ -16,7 +16,7 @@ export function registerProfile(program) {
16
16
  const handle = result.profile.username ? `@${result.profile.username}` : result.profile.userUuid;
17
17
  console.log(`\n ${result.profile.displayName} (${handle})`);
18
18
  console.log(` Spaces: ${result.spaces.length}`);
19
- console.log(` Works: ${result.works.length}\n`);
19
+ console.log(` Apps: ${result.apps.length}\n`);
20
20
  if (result.spaces.length > 0) {
21
21
  console.log(" Spaces:");
22
22
  for (const space of result.spaces) {
@@ -24,10 +24,10 @@ export function registerProfile(program) {
24
24
  }
25
25
  console.log("");
26
26
  }
27
- if (result.works.length > 0) {
28
- console.log(" Works:");
29
- for (const work of result.works) {
30
- console.log(` - ${work.title} ${work.publicUrl}`);
27
+ if (result.apps.length > 0) {
28
+ console.log(" Apps:");
29
+ for (const app of result.apps) {
30
+ console.log(` - ${app.title} ${app.publicUrl}`);
31
31
  }
32
32
  console.log("");
33
33
  }
@@ -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", { hidden: true }).description("Task runs");
5
+ const cmd = program.command("tasks").description("Task runs");
6
6
  cmd
7
7
  .command("ls")
8
8
  .alias("list")
package/dist/index.js CHANGED
@@ -19,8 +19,8 @@ import { registerPrompt, registerSpaces } from "./commands/spaces.js";
19
19
  import { maybeHandleRunCommand } from "./commands/run.js";
20
20
  import { registerSandbox } from "./commands/sandbox.js";
21
21
  import { registerTasks } from "./commands/tasks.js";
22
- import { registerUi } from "./commands/ui.js";
23
- import { registerWorks } from "./commands/works.js";
22
+ import { registerDesktop, registerLegacyUi } from "./commands/desktop.js";
23
+ import { registerApps } from "./commands/apps.js";
24
24
  const VERSION = (() => {
25
25
  try {
26
26
  const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
@@ -55,8 +55,8 @@ Common commands:
55
55
  cohub -s <space-id> spaces sessions turns ls <session-id>
56
56
  cohub -s <space-id> spaces files ls
57
57
  cohub -s <space-id> public upload ./dist demo
58
- cohub -s <space-id> works publish demo --file dist/index.html
59
- cohub ui preview <work-id> --call selection.get
58
+ cohub -s <space-id> apps publish demo --file dist/index.html
59
+ cohub desktop open <app-id> --call selection.get
60
60
  cohub -s <space-id> spaces commerce products list
61
61
  cohub models ls
62
62
  cohub models ls --model-type multimodal
@@ -84,8 +84,9 @@ registerReferences(program);
84
84
  registerReferrals(program);
85
85
  registerTasks(program);
86
86
  registerCronJobs(program);
87
- registerWorks(program);
88
- registerUi(program);
87
+ registerApps(program);
88
+ registerDesktop(program);
89
+ registerLegacyUi(program);
89
90
  const argv = process.argv.slice(2);
90
91
  if (await maybeHandleRunCommand(argv)) {
91
92
  process.exit();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/cohub-cli",
3
- "version": "4.0.0",
3
+ "version": "6.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": "6.0.0"
22
+ "@neta-art/cohub": "8.0.0"
23
23
  },
24
24
  "publishConfig": {
25
25
  "access": "public"
@@ -1,2 +0,0 @@
1
- import type { Command } from "commander";
2
- export declare function registerUi(program: Command): void;