@neta-art/cohub-cli 6.8.2 → 6.9.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.
@@ -4,6 +4,7 @@ import { registerBoardExampleCommands } from "./boards/examples.js";
4
4
  import { registerBoardBatchCommand } from "./boards/batch.js";
5
5
  import { registerBoardConnectionCommands } from "./boards/connections.js";
6
6
  import { registerBoardItemCommands } from "./boards/items.js";
7
+ import { registerBoardTransactionCommands } from "./boards/transactions.js";
7
8
  export function registerBoardDomainCommands(boards) {
8
9
  registerBoardExampleCommands(boards);
9
10
  registerBoardAppearanceCommands(boards);
@@ -11,4 +12,5 @@ export function registerBoardDomainCommands(boards) {
11
12
  registerBoardConnectionCommands(boards);
12
13
  registerBoardItemCommands(boards);
13
14
  registerBoardAnimationCommands(boards);
15
+ registerBoardTransactionCommands(boards);
14
16
  }
@@ -65,6 +65,37 @@ Examples:
65
65
  handleHttp(cause);
66
66
  }
67
67
  });
68
+ withJson(boards.command("motion <board>")
69
+ .description("Configure the default Board motion for newly added nodes")
70
+ .option("--preset <preset>", "Enter motion preset (deal)")
71
+ .option("--clear", "Disable the default enter motion")
72
+ .addHelpText("after", `
73
+ Examples:
74
+ cohub boards motion plan.board --preset deal
75
+ cohub boards motion plan.board --clear`))
76
+ .action(async (target, options) => {
77
+ try {
78
+ if (Boolean(options.clear) === Boolean(options.preset))
79
+ throw new Error("Use --preset or --clear");
80
+ if (options.preset && options.preset !== "deal")
81
+ throw new Error("--preset must be deal");
82
+ const board = await resolvedBoard(boards, target);
83
+ const current = await board.summary();
84
+ const appearance = appearanceFrom(current.board.metadata);
85
+ const next = patchBoardAppearance(appearance, {
86
+ motion: options.clear
87
+ ? undefined
88
+ : { enter: { kind: "effects.deal", kindVersion: 1, params: {} } },
89
+ });
90
+ showUpdated(await mutateSemantic(board, [{
91
+ type: "board.patch",
92
+ patch: { metadataPatch: { appearance: next } },
93
+ }], { baseVersion: current.board.version }), options);
94
+ }
95
+ catch (cause) {
96
+ handleHttp(cause);
97
+ }
98
+ });
68
99
  withJson(boards.command("playback-policy <board>")
69
100
  .description("Configure automatic Board playback")
70
101
  .option("--composition <id>", "Composition to play")
@@ -269,6 +269,23 @@ const templates = {
269
269
  seed: "float-hero",
270
270
  params: { distance: 8, period: 2200 },
271
271
  },
272
+ "effect:deal": {
273
+ id: "deal-title",
274
+ target: { type: "item", itemId: "title" },
275
+ kind: "effects.deal",
276
+ kindVersion: 1,
277
+ lifecycle: "on-enter",
278
+ timeOrigin: "activation",
279
+ seed: "deal-title",
280
+ params: {},
281
+ },
282
+ "motion:enter-deal": {
283
+ enter: {
284
+ kind: "effects.deal",
285
+ kindVersion: 1,
286
+ params: {},
287
+ },
288
+ },
272
289
  "composition:fade": {
273
290
  id: "intro",
274
291
  name: "Intro",
@@ -392,7 +409,8 @@ Kinds:
392
409
  create [workflow|media|animation]
393
410
  batch basic
394
411
  item text|image|video|audio|file|task|geo|frame|draw|arrow
395
- effect pulse|float
412
+ effect pulse|float|deal
413
+ motion enter-deal
396
414
  composition fade|reveal|draw-reveal|motion-path|trail|impact|flash|particles|camera-shake|camera-focus
397
415
 
398
416
  Examples:
@@ -0,0 +1,12 @@
1
+ import type { BoardTransactionRecord } from "@neta-art/cohub";
2
+ import type { Command } from "commander";
3
+ /** Compact `type×count` summary, e.g. `node.patch×3 connection.create`. */
4
+ export declare function summarizeOperations(operations: BoardTransactionRecord["operations"]): string;
5
+ export declare function transactionRows(transactions: BoardTransactionRecord[]): {
6
+ version: number;
7
+ createdAt: string;
8
+ actor: string;
9
+ via: import("@neta-art/cohub/board").BoardReplayActorKind;
10
+ changes: string;
11
+ }[];
12
+ export declare function registerBoardTransactionCommands(boards: Command): void;
@@ -0,0 +1,69 @@
1
+ import { boardReplayActorKind } from "@neta-art/cohub/board";
2
+ import { handleHttp, json, jsonRequested, table } from "../../output.js";
3
+ import { finite, resolvedBoard, withJson } from "./context.js";
4
+ const columns = [
5
+ { key: "version", label: "VERSION" },
6
+ { key: "createdAt", label: "AT" },
7
+ { key: "actor", label: "ACTOR" },
8
+ { key: "via", label: "VIA" },
9
+ { key: "changes", label: "CHANGES" },
10
+ ];
11
+ /** Compact `type×count` summary, e.g. `node.patch×3 connection.create`. */
12
+ export function summarizeOperations(operations) {
13
+ const counts = new Map();
14
+ for (const operation of operations)
15
+ counts.set(operation.type, (counts.get(operation.type) ?? 0) + 1);
16
+ return [...counts.entries()].map(([type, count]) => (count > 1 ? `${type}×${count}` : type)).join(" ");
17
+ }
18
+ export function transactionRows(transactions) {
19
+ return transactions.map((transaction) => ({
20
+ version: transaction.version,
21
+ createdAt: transaction.createdAt,
22
+ actor: transaction.actorId,
23
+ via: boardReplayActorKind(transaction.source),
24
+ changes: summarizeOperations(transaction.operations),
25
+ }));
26
+ }
27
+ export function registerBoardTransactionCommands(boards) {
28
+ withJson(boards
29
+ .command("transactions <board>")
30
+ .alias("history")
31
+ .description("List the Board transaction log, newest first")
32
+ .option("--before <version>", "Only transactions below this version")
33
+ .option("--limit <count>", "Page size (max 500)", "50")
34
+ .option("--operations", "Include full operations and inverses in --json output")
35
+ .addHelpText("after", `
36
+ Every applied mutation is one transaction. --json prints the page summary; add
37
+ --operations for the full payloads, inverses and (on the newest page) the current
38
+ node and connection rows, which is what the web replay view consumes.`)).action(async (target, options) => {
39
+ try {
40
+ const board = await resolvedBoard(boards, target);
41
+ // The snapshot is only ever printed in full --json --operations output;
42
+ // every other mode would fetch the whole Board just to drop it.
43
+ const full = jsonRequested(options) && Boolean(options.operations);
44
+ const page = await board.transactions({
45
+ ...(options.before !== undefined ? { before: finite(options.before, "--before") } : {}),
46
+ limit: finite(options.limit, "--limit", 50),
47
+ snapshot: full,
48
+ });
49
+ if (jsonRequested(options)) {
50
+ return json(full
51
+ ? page
52
+ : {
53
+ ...page,
54
+ transactions: page.transactions.map(({ operations, ...transaction }) => ({
55
+ ...transaction,
56
+ operationCount: operations.length,
57
+ })),
58
+ });
59
+ }
60
+ table(transactionRows(page.transactions), columns);
61
+ if (page.nextBefore !== null) {
62
+ console.log(`\nOlder transactions: --before ${page.nextBefore}`);
63
+ }
64
+ }
65
+ catch (cause) {
66
+ handleHttp(cause);
67
+ }
68
+ });
69
+ }
@@ -1,3 +1,10 @@
1
+ import { type DesktopSurface } from "@neta-art/cohub";
1
2
  import type { Command } from "commander";
3
+ /**
4
+ * The surface a desktop.open should request: an explicit `--as` wins, then
5
+ * whatever the App declared at publish time. `window` is the implicit default
6
+ * and yields `undefined` so the command stays compact.
7
+ */
8
+ export declare function resolveOpenSurface(requested: string | undefined, declared: unknown): DesktopSurface | undefined;
2
9
  export declare function registerDesktop(program: Command): void;
3
10
  export declare function registerLegacyUi(program: Command): void;
@@ -65,10 +65,19 @@ function parseTimeout(value) {
65
65
  }
66
66
  return parsed;
67
67
  }
68
+ /**
69
+ * The surface a desktop.open should request: an explicit `--as` wins, then
70
+ * whatever the App declared at publish time. `window` is the implicit default
71
+ * and yields `undefined` so the command stays compact.
72
+ */
73
+ export function resolveOpenSurface(requested, declared) {
74
+ return (requested ?? declared) === "overlay" ? "overlay" : undefined;
75
+ }
68
76
  async function resolveAppTarget(client, ref) {
69
77
  const normalized = hasAppScheme(ref) ? ref.replace(/^[a-zA-Z]+:\/\//, "") : ref;
70
78
  const parsed = parseAppRef(normalized);
71
79
  const detail = await getAppByRef(client, normalized);
80
+ const declared = detail.app.meta?.presentation?.surface;
72
81
  return {
73
82
  kind: "app",
74
83
  appId: detail.app.id,
@@ -77,6 +86,7 @@ async function resolveAppTarget(client, ref) {
77
86
  ...(parsed.search ? { search: parsed.search } : {}),
78
87
  ...(parsed.hash ? { hash: parsed.hash } : {}),
79
88
  },
89
+ ...(declared === "overlay" || declared === "window" ? { surface: declared } : {}),
80
90
  };
81
91
  }
82
92
  async function resolveOpenTarget(client, command, value) {
@@ -122,6 +132,9 @@ async function openWindow(target, opts, command) {
122
132
  if (opts.noWait && opts.timeoutMs !== undefined) {
123
133
  return error("Conflicting wait options", "Use either --no-wait or --timeout-ms, not both.");
124
134
  }
135
+ if (opts.as !== undefined && opts.as !== "window" && opts.as !== "overlay") {
136
+ return error("Invalid surface", "--as must be one of: window, overlay.");
137
+ }
125
138
  const timeoutMs = parseTimeout(opts.timeoutMs);
126
139
  const client = createClient();
127
140
  try {
@@ -129,6 +142,9 @@ async function openWindow(target, opts, command) {
129
142
  if (resolved.kind === "file" && opts.call) {
130
143
  return error("Unsupported option", "--call only applies to app targets.");
131
144
  }
145
+ if (resolved.kind === "file" && opts.as) {
146
+ return error("Unsupported option", "--as only applies to app targets.");
147
+ }
132
148
  const call = opts.call
133
149
  ? { method: opts.call, ...(callInput === undefined ? {} : { input: callInput }) }
134
150
  : undefined;
@@ -142,6 +158,7 @@ async function openWindow(target, opts, command) {
142
158
  appId: resolved.appId,
143
159
  label: resolved.label,
144
160
  ...(resolved.launch.search || resolved.launch.hash ? { launch: resolved.launch } : {}),
161
+ ...(resolveOpenSurface(opts.as, resolved.surface) ? { surface: "overlay" } : {}),
145
162
  },
146
163
  ...(call ? { call } : {}),
147
164
  },
@@ -173,6 +190,7 @@ Examples:
173
190
  cohub desktop open app://alice/studio/launch
174
191
  cohub desktop open alice/studio/launch
175
192
  cohub desktop open https://cohub.live/alice/studio/w/launch?view=timeline
193
+ cohub desktop open <app-id> --as overlay
176
194
  cohub desktop open <app-id> --call selection.get
177
195
  cohub desktop open <app-id> --call board.focus --data '{"nodeId":"n1"}'
178
196
  `;
@@ -182,6 +200,9 @@ Notes:
182
200
  scheme is still accepted.
183
201
  - A plain target checks the current Space for a file before resolving an app.
184
202
  - Opening a window is idempotent; repeating it re-activates the same tab.
203
+ - --as picks the surface: window (a preview tab) or overlay (a transparent
204
+ layer above the workspace). Without it, an App published with
205
+ <meta name="cohub:surface" content="overlay"> opens as an overlay.
185
206
  - --call waits for the app to announce readiness, then invokes the method.
186
207
  - Which methods exist is up to the app author.
187
208
  `;
@@ -195,6 +216,7 @@ function registerOpen(parent, deprecated) {
195
216
  .option("--client <clientId>", "Target a specific desktop instance of your account")
196
217
  .option("--command-id <id>", "Stable id so retries never dispatch twice")
197
218
  .option("--no-wait", "Dispatch the command and exit without waiting for a result")
219
+ .option("--as <surface>", "Surface role for app targets: window (default) or overlay")
198
220
  .option("--timeout-ms <ms>", `How long to wait for the desktop (default: ${DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS}; max: ${DESKTOP_COMMAND_MAX_TIMEOUT_MS})`)
199
221
  .option("--json", "Output as JSON")
200
222
  .action(async (target, opts, thisCommand) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/cohub-cli",
3
- "version": "6.8.2",
3
+ "version": "6.9.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.20.1",
21
21
  "sharp": "^0.35.4",
22
- "@neta-art/cohub": "8.11.0"
22
+ "@neta-art/cohub": "8.12.0"
23
23
  },
24
24
  "publishConfig": {
25
25
  "access": "public"