@neta-art/cohub-cli 8.4.1 → 9.1.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.
Files changed (70) hide show
  1. package/README.md +66 -30
  2. package/dist/board-export.d.ts +21 -36
  3. package/dist/board-export.js +163 -88
  4. package/dist/board-kernel.d.ts +2 -3
  5. package/dist/board-kernel.js +3428 -3770
  6. package/dist/board-time.d.ts +3 -0
  7. package/dist/board-time.js +28 -0
  8. package/dist/commands/apps.js +2 -2
  9. package/dist/commands/auth.js +2 -2
  10. package/dist/commands/boards/examples.d.ts +8 -2
  11. package/dist/commands/boards/examples.js +79 -411
  12. package/dist/commands/boards.d.ts +4 -9
  13. package/dist/commands/boards.js +365 -286
  14. package/dist/commands/desktop.d.ts +1 -5
  15. package/dist/commands/desktop.js +5 -16
  16. package/dist/commands/generations.js +1 -1
  17. package/dist/commands/me.js +1 -1
  18. package/dist/commands/public.js +3 -3
  19. package/dist/commands/references.js +3 -0
  20. package/dist/commands/referrals.js +2 -2
  21. package/dist/commands/run.js +3 -3
  22. package/dist/commands/runtime.d.ts +1 -1
  23. package/dist/commands/runtime.js +14 -9
  24. package/dist/commands/sandboxd-binary.d.ts +1 -1
  25. package/dist/commands/sandboxd-binary.js +2 -23
  26. package/dist/commands/search.js +15 -7
  27. package/dist/commands/space-activity.js +3 -5
  28. package/dist/commands/space-displays.d.ts +2 -0
  29. package/dist/commands/space-displays.js +347 -0
  30. package/dist/commands/space-files.d.ts +5 -0
  31. package/dist/commands/space-files.js +215 -0
  32. package/dist/commands/space-invitations.js +3 -3
  33. package/dist/commands/spaces.d.ts +3 -11
  34. package/dist/commands/spaces.js +146 -75
  35. package/dist/index.js +8 -5
  36. package/dist/runtime/launch.d.ts +2 -0
  37. package/dist/runtime/launch.js +38 -8
  38. package/dist/runtime/presentation.d.ts +1 -0
  39. package/dist/runtime/presentation.js +3 -2
  40. package/dist/runtime/supervisor.d.ts +1 -0
  41. package/dist/runtime/supervisor.js +2 -1
  42. package/dist/space-files/limit.d.ts +2 -0
  43. package/dist/space-files/limit.js +16 -0
  44. package/dist/space-files/operands.d.ts +16 -0
  45. package/dist/space-files/operands.js +33 -0
  46. package/dist/space-files/transfer.d.ts +53 -0
  47. package/dist/space-files/transfer.js +296 -0
  48. package/dist/space-files/upload.d.ts +14 -0
  49. package/dist/space-files/upload.js +53 -0
  50. package/dist/space.d.ts +18 -16
  51. package/dist/space.js +86 -39
  52. package/dist/web.d.ts +1 -0
  53. package/dist/web.js +6 -0
  54. package/package.json +2 -2
  55. package/dist/commands/board-domain.d.ts +0 -2
  56. package/dist/commands/board-domain.js +0 -16
  57. package/dist/commands/boards/animation.d.ts +0 -2
  58. package/dist/commands/boards/animation.js +0 -171
  59. package/dist/commands/boards/appearance.d.ts +0 -2
  60. package/dist/commands/boards/appearance.js +0 -127
  61. package/dist/commands/boards/batch.d.ts +0 -4
  62. package/dist/commands/boards/batch.js +0 -62
  63. package/dist/commands/boards/connections.d.ts +0 -2
  64. package/dist/commands/boards/connections.js +0 -95
  65. package/dist/commands/boards/context.d.ts +0 -31
  66. package/dist/commands/boards/context.js +0 -71
  67. package/dist/commands/boards/items.d.ts +0 -2
  68. package/dist/commands/boards/items.js +0 -184
  69. package/dist/commands/boards/transactions.d.ts +0 -12
  70. package/dist/commands/boards/transactions.js +0 -69
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/cohub-cli",
3
- "version": "8.4.1",
3
+ "version": "9.1.0",
4
4
  "description": "CLI for Cohub — spaces, sessions, and agent collaboration.",
5
5
  "type": "module",
6
6
  "engines": {
@@ -22,7 +22,7 @@
22
22
  "commander": "^15.0.0",
23
23
  "sharp": "^0.35.4",
24
24
  "ws": "^8.21.3",
25
- "@neta-art/cohub": "8.24.0"
25
+ "@neta-art/cohub": "9.0.0"
26
26
  },
27
27
  "publishConfig": {
28
28
  "access": "public"
@@ -1,2 +0,0 @@
1
- import type { Command } from "commander";
2
- export declare function registerBoardDomainCommands(boards: Command): void;
@@ -1,16 +0,0 @@
1
- import { registerBoardAnimationCommands } from "./boards/animation.js";
2
- import { registerBoardAppearanceCommands } from "./boards/appearance.js";
3
- import { registerBoardExampleCommands } from "./boards/examples.js";
4
- import { registerBoardBatchCommand } from "./boards/batch.js";
5
- import { registerBoardConnectionCommands } from "./boards/connections.js";
6
- import { registerBoardItemCommands } from "./boards/items.js";
7
- import { registerBoardTransactionCommands } from "./boards/transactions.js";
8
- export function registerBoardDomainCommands(boards) {
9
- registerBoardExampleCommands(boards);
10
- registerBoardAppearanceCommands(boards);
11
- registerBoardBatchCommand(boards);
12
- registerBoardConnectionCommands(boards);
13
- registerBoardItemCommands(boards);
14
- registerBoardAnimationCommands(boards);
15
- registerBoardTransactionCommands(boards);
16
- }
@@ -1,2 +0,0 @@
1
- import type { Command } from "commander";
2
- export declare function registerBoardAnimationCommands(boards: Command): void;
@@ -1,171 +0,0 @@
1
- import { parseBoardEffectInput, parseBoardCompositionInput, } from "@neta-art/cohub";
2
- import { BOARD_DOMAIN_INPUT_MAX_BYTES, readBoardJsonObject, } from "../../board-command-support.js";
3
- import { handleHttp, json, jsonRequested, table } from "../../output.js";
4
- import { mutateSemantic, readInput, readOptions, resolvedBoard, showUpdated, withJson, } from "./context.js";
5
- export function registerBoardAnimationCommands(boards) {
6
- const effects = boards.command("effects").description("Manage Board effects");
7
- readOptions(withJson(effects.command("list <board>").alias("ls").description("List effects")))
8
- .action(async (target, options) => {
9
- try {
10
- const board = await resolvedBoard(boards, target);
11
- const result = await board.authoring(readInput(options, "effects"));
12
- const effects = result.effects ?? [];
13
- if (jsonRequested(options))
14
- return json(effects);
15
- table(effects, [
16
- { key: "id", label: "ID" },
17
- { key: "kind", label: "KIND" },
18
- { key: "enabled", label: "ENABLED" },
19
- { key: "layer", label: "LAYER" },
20
- ]);
21
- }
22
- catch (cause) {
23
- handleHttp(cause);
24
- }
25
- });
26
- withJson(effects.command("get <board> <effect-id>").description("Get one complete Board effect"))
27
- .action(async (target, effectId, options) => {
28
- try {
29
- const board = await resolvedBoard(boards, target);
30
- const result = await board.authoring({ include: ["effects"], effectIds: [effectId] });
31
- const effect = result.effects?.[0];
32
- if (!effect)
33
- throw new Error(`Effect not found: ${effectId}`);
34
- if (jsonRequested(options))
35
- return json(effect);
36
- table([effect], [
37
- { key: "id", label: "ID" },
38
- { key: "kind", label: "KIND" },
39
- { key: "enabled", label: "ENABLED" },
40
- { key: "layer", label: "LAYER" },
41
- ]);
42
- }
43
- catch (cause) {
44
- handleHttp(cause);
45
- }
46
- });
47
- withJson(effects.command("apply <board>")
48
- .description("Atomically create or replace an effect")
49
- .requiredOption("-i, --input <file>", "Board effect JSON; use - for stdin")
50
- .addHelpText("after", `
51
- Create a template:
52
- cohub boards examples effect pulse > effect.json
53
-
54
- Discover supported effect kinds:
55
- cohub boards capabilities <board> --json`))
56
- .action(async (target, options) => {
57
- try {
58
- const input = await readBoardJsonObject(options.input, BOARD_DOMAIN_INPUT_MAX_BYTES);
59
- const effect = parseBoardEffectInput(input);
60
- const board = await resolvedBoard(boards, target);
61
- showUpdated(await mutateSemantic(board, [{ type: "effect.apply", effect }]), options);
62
- }
63
- catch (cause) {
64
- handleHttp(cause);
65
- }
66
- });
67
- withJson(effects.command("delete <board> <effect-id>").alias("rm").description("Delete an effect"))
68
- .action(async (target, effectId, options) => {
69
- try {
70
- const board = await resolvedBoard(boards, target);
71
- showUpdated(await mutateSemantic(board, [{ type: "effect.delete", effectId }]), options);
72
- }
73
- catch (cause) {
74
- handleHttp(cause);
75
- }
76
- });
77
- const compositions = boards
78
- .command("compositions")
79
- .description("Manage atomic Board animation compositions");
80
- readOptions(withJson(compositions.command("list <board>").alias("ls").description("List compositions")))
81
- .action(async (target, options) => {
82
- try {
83
- const board = await resolvedBoard(boards, target);
84
- const result = await board.authoring(readInput(options, "compositions"));
85
- const compositions = result.compositions ?? [];
86
- if (jsonRequested(options))
87
- return json(compositions);
88
- table(compositions.map((composition) => ({
89
- id: composition.id,
90
- name: composition.name,
91
- duration: composition.timeline.duration,
92
- tracks: composition.timeline.tracks.length,
93
- clips: composition.timeline.clips.length,
94
- revision: composition.revision,
95
- })), [
96
- { key: "id", label: "ID" },
97
- { key: "name", label: "NAME" },
98
- { key: "duration", label: "DURATION" },
99
- { key: "tracks", label: "TRACKS" },
100
- { key: "clips", label: "CLIPS" },
101
- { key: "revision", label: "REVISION" },
102
- ]);
103
- }
104
- catch (cause) {
105
- handleHttp(cause);
106
- }
107
- });
108
- withJson(compositions.command("get <board> <composition-id>").description("Get one complete composition"))
109
- .action(async (target, compositionId, options) => {
110
- try {
111
- const board = await resolvedBoard(boards, target);
112
- const result = await board.authoring({ include: ["compositions"], compositionIds: [compositionId] });
113
- const composition = result.compositions?.[0];
114
- if (!composition)
115
- throw new Error(`Composition not found: ${compositionId}`);
116
- if (jsonRequested(options))
117
- return json(composition);
118
- table([{
119
- id: composition.id,
120
- name: composition.name,
121
- duration: composition.timeline.duration,
122
- tracks: composition.timeline.tracks.length,
123
- clips: composition.timeline.clips.length,
124
- revision: composition.revision,
125
- }], [
126
- { key: "id", label: "ID" },
127
- { key: "name", label: "NAME" },
128
- { key: "duration", label: "DURATION" },
129
- { key: "tracks", label: "TRACKS" },
130
- { key: "clips", label: "CLIPS" },
131
- { key: "revision", label: "REVISION" },
132
- ]);
133
- }
134
- catch (cause) {
135
- handleHttp(cause);
136
- }
137
- });
138
- withJson(compositions.command("apply <board>")
139
- .description("Atomically create or replace a composition")
140
- .requiredOption("-i, --input <file>", "BoardComposition JSON; use - for stdin")
141
- .addHelpText("after", `
142
- Property changes use timeline.tracks with registered channels and keyframes.
143
- Procedural behavior such as text reveal, particles, and camera focus uses timeline.clips.
144
- Run boards capabilities to discover channels and clip schemas.
145
- Create an editable template with:
146
- cohub boards examples composition fade > intro.json
147
-
148
- Minimal fade composition:
149
- {"id":"intro","name":"Intro","timeline":{"duration":800,"tracks":[{"id":"title-opacity","target":{"type":"item","itemId":"title"},"channel":"style.opacity","fill":"both","keyframes":[{"time":0,"value":0},{"time":800,"value":1,"easing":"ease-out-cubic"}]}],"clips":[],"markers":[]},"playback":{"loop":false,"endBehavior":"hold","reducedMotion":{"mode":"base"}}}`))
150
- .action(async (target, options) => {
151
- try {
152
- const input = await readBoardJsonObject(options.input, BOARD_DOMAIN_INPUT_MAX_BYTES);
153
- const composition = parseBoardCompositionInput(input);
154
- const board = await resolvedBoard(boards, target);
155
- showUpdated(await mutateSemantic(board, [{ type: "composition.apply", composition }]), options);
156
- }
157
- catch (cause) {
158
- handleHttp(cause);
159
- }
160
- });
161
- withJson(compositions.command("delete <board> <composition-id>").alias("rm").description("Delete a composition"))
162
- .action(async (target, compositionId, options) => {
163
- try {
164
- const board = await resolvedBoard(boards, target);
165
- showUpdated(await mutateSemantic(board, [{ type: "composition.delete", compositionId }]), options);
166
- }
167
- catch (cause) {
168
- handleHttp(cause);
169
- }
170
- });
171
- }
@@ -1,2 +0,0 @@
1
- import type { Command } from "commander";
2
- export declare function registerBoardAppearanceCommands(boards: Command): void;
@@ -1,127 +0,0 @@
1
- import { BoardAppearanceSchema, DEFAULT_BOARD_APPEARANCE, patchBoardAppearance, } from "@neta-art/cohub/board";
2
- import { handleHttp } from "../../output.js";
3
- import { finite, mutateSemantic, resolvedBoard, showUpdated, withJson, } from "./context.js";
4
- function appearanceFrom(metadata) {
5
- const parsed = BoardAppearanceSchema.safeParse(metadata.appearance);
6
- return parsed.success ? parsed.data : DEFAULT_BOARD_APPEARANCE;
7
- }
8
- export function registerBoardAppearanceCommands(boards) {
9
- withJson(boards.command("rename <board> <title>").description("Rename a Board"))
10
- .action(async (target, title, options) => {
11
- try {
12
- const board = await resolvedBoard(boards, target);
13
- showUpdated(await mutateSemantic(board, [{ type: "board.patch", patch: { title } }]), options);
14
- }
15
- catch (cause) {
16
- handleHttp(cause);
17
- }
18
- });
19
- withJson(boards.command("background <board>")
20
- .description("Configure the Board background")
21
- .option("--color <color>", "Solid CSS color")
22
- .option("--image <url>", "Public image URL")
23
- .option("--fit <mode>", "cover, contain, or repeat", "cover")
24
- .option("--position <position>", "center, top, bottom, left, or right", "center")
25
- .option("--opacity <value>", "Image opacity from 0 to 1", "1")
26
- .option("--reset", "Restore the default background")
27
- .addHelpText("after", `
28
- Examples:
29
- cohub boards background plan.board --color "#123456"
30
- cohub boards background plan.board --image https://example.com/bg.webp --fit cover --opacity 0.8`))
31
- .action(async (target, options) => {
32
- try {
33
- const selected = [options.color, options.image, options.reset ? "reset" : undefined].filter(Boolean);
34
- if (selected.length !== 1)
35
- throw new Error("Choose one of --color, --image, or --reset");
36
- if (!["cover", "contain", "repeat"].includes(options.fit))
37
- throw new Error("--fit must be cover, contain, or repeat");
38
- if (!["center", "top", "bottom", "left", "right"].includes(options.position))
39
- throw new Error("--position must be center, top, bottom, left, or right");
40
- const opacity = finite(options.opacity, "opacity");
41
- if (opacity < 0 || opacity > 1)
42
- throw new Error("opacity must be between 0 and 1");
43
- const board = await resolvedBoard(boards, target);
44
- const current = await board.summary();
45
- const appearance = appearanceFrom(current.board.metadata);
46
- const background = options.reset
47
- ? { kind: "solid" }
48
- : options.color
49
- ? { kind: "solid", color: options.color }
50
- : {
51
- kind: "image",
52
- imageUrl: options.image,
53
- fit: options.fit,
54
- position: options.position,
55
- opacity,
56
- color: appearance.background.color,
57
- };
58
- const result = await mutateSemantic(board, [{
59
- type: "board.patch",
60
- patch: { metadataPatch: { appearance: patchBoardAppearance(appearance, { background }) } },
61
- }], { baseVersion: current.board.version });
62
- showUpdated(result, options);
63
- }
64
- catch (cause) {
65
- handleHttp(cause);
66
- }
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
- });
99
- withJson(boards.command("playback-policy <board>")
100
- .description("Configure automatic Board playback")
101
- .option("--composition <id>", "Composition to play")
102
- .option("--delay <ms>", "Delay before playback", "0")
103
- .option("--clear", "Remove the playback policy"))
104
- .action(async (target, options) => {
105
- try {
106
- if (options.clear === Boolean(options.composition))
107
- throw new Error("Use --composition or --clear");
108
- const delayMs = finite(options.delay, "delay");
109
- if (delayMs < 0)
110
- throw new Error("delay must be non-negative");
111
- const policy = options.clear
112
- ? null
113
- : { compositionId: options.composition, delayMs };
114
- const board = await resolvedBoard(boards, target);
115
- const current = await board.summary();
116
- const patch = policy
117
- ? { metadataPatch: { playback: policy } }
118
- : { metadata: { ...current.board.metadata, playback: undefined } };
119
- if (!policy)
120
- delete patch.metadata.playback;
121
- showUpdated(await mutateSemantic(board, [{ type: "board.patch", patch }], { baseVersion: current.board.version }), options);
122
- }
123
- catch (cause) {
124
- handleHttp(cause);
125
- }
126
- });
127
- }
@@ -1,4 +0,0 @@
1
- import { type BoardSemanticCommand } from "@neta-art/cohub";
2
- import type { Command } from "commander";
3
- export declare function parseBatchCommands(input: Record<string, unknown>): BoardSemanticCommand[];
4
- export declare function registerBoardBatchCommand(boards: Command): void;
@@ -1,62 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { BoardSemanticCommandSchema } from "@neta-art/cohub";
3
- import { BOARD_TRANSACTION_INPUT_MAX_BYTES, readBoardJsonObject, } from "../../board-command-support.js";
4
- import { handleHttp, json, jsonRequested, ok } from "../../output.js";
5
- import { mutateSemantic, resolvedBoard, withJson, } from "./context.js";
6
- export function parseBatchCommands(input) {
7
- if (!Array.isArray(input.commands) || input.commands.length === 0) {
8
- throw new Error("batch input must contain a non-empty commands array");
9
- }
10
- return input.commands.map((command, index) => {
11
- const parsed = BoardSemanticCommandSchema.safeParse(command);
12
- if (!parsed.success) {
13
- const issue = parsed.error.issues[0];
14
- const path = issue?.path.length ? `.${issue.path.join(".")}` : "";
15
- throw new Error(`commands[${index}]${path}: ${issue?.message ?? "invalid command"}`);
16
- }
17
- return parsed.data;
18
- });
19
- }
20
- export function registerBoardBatchCommand(boards) {
21
- withJson(boards.command("batch <board>")
22
- .description("Apply an atomic batch of semantic Board changes")
23
- .requiredOption("-i, --input <file>", "Batch JSON with a commands array; use - for stdin")
24
- .option("--base-version <version>", "Expected Board version; defaults to latest")
25
- .option("--mutation-id <id>", "Stable id for safe retries")
26
- .option("--dry-run", "Validate the whole batch without writing")
27
- .addHelpText("after", `
28
- Input format:
29
- {"commands":[{"type":"item.patch","itemId":"title","patch":{"props":{"text":"Updated"}}}]}
30
-
31
- Commands are applied atomically. See boards capabilities for supported fields.`))
32
- .action(async (target, options) => {
33
- try {
34
- const input = await readBoardJsonObject(options.input, BOARD_TRANSACTION_INPUT_MAX_BYTES);
35
- const commands = parseBatchCommands(input);
36
- const parsedVersion = options.baseVersion === undefined ? undefined : Number(options.baseVersion);
37
- if (parsedVersion !== undefined && (!Number.isSafeInteger(parsedVersion) || parsedVersion < 0)) {
38
- throw new Error("base version must be a non-negative integer");
39
- }
40
- const board = await resolvedBoard(boards, target);
41
- const result = await mutateSemantic(board, commands, {
42
- ...(parsedVersion === undefined ? {} : { baseVersion: parsedVersion }),
43
- mutationId: options.mutationId?.trim() || randomUUID(),
44
- dryRun: Boolean(options.dryRun),
45
- });
46
- if (jsonRequested(options))
47
- return json(result);
48
- if (result.outcome === "dry-run") {
49
- ok(`Validated ${commands.length} changes; no changes written`);
50
- }
51
- else if (result.outcome === "noop" || result.status === "validated") {
52
- ok(`Board already matches the requested ${commands.length} changes`);
53
- }
54
- else {
55
- ok(`${result.replayed ? "Replayed" : "Applied"} ${commands.length} changes at Board version ${result.board.version}`);
56
- }
57
- }
58
- catch (cause) {
59
- handleHttp(cause);
60
- }
61
- });
62
- }
@@ -1,2 +0,0 @@
1
- import type { Command } from "commander";
2
- export declare function registerBoardConnectionCommands(boards: Command): void;
@@ -1,95 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { createBoardConnection } from "@neta-art/cohub/board";
3
- import { handleHttp, json, jsonRequested, table } from "../../output.js";
4
- import { mutateSemantic, readInput, readOptions, resolvedBoard, showUpdated, withJson, } from "./context.js";
5
- const columns = [
6
- { key: "id", label: "ID" },
7
- { key: "source", label: "SOURCE" },
8
- { key: "target", label: "TARGET" },
9
- { key: "relation", label: "RELATION" },
10
- { key: "direction", label: "DIRECTION" },
11
- { key: "label", label: "LABEL" },
12
- ];
13
- function rows(connections) {
14
- return connections.map((connection) => ({
15
- id: connection.id,
16
- source: connection.source.itemId,
17
- target: connection.target.itemId,
18
- relation: connection.relation ?? "",
19
- direction: connection.direction ?? "",
20
- label: connection.label ?? "",
21
- }));
22
- }
23
- export function registerBoardConnectionCommands(boards) {
24
- const connections = boards.command("connections").description("Manage Board connections");
25
- readOptions(withJson(connections.command("list <board>").alias("ls").description("List Board connections")))
26
- .action(async (target, options) => {
27
- try {
28
- const board = await resolvedBoard(boards, target);
29
- const result = await board.authoring(readInput(options, "connections"));
30
- const value = result.connections ?? [];
31
- if (jsonRequested(options))
32
- return json(value);
33
- table(rows(value), columns);
34
- }
35
- catch (cause) {
36
- handleHttp(cause);
37
- }
38
- });
39
- withJson(connections.command("get <board> <connection-id>").description("Get one complete Board connection"))
40
- .action(async (target, connectionId, options) => {
41
- try {
42
- const board = await resolvedBoard(boards, target);
43
- const result = await board.authoring({ include: ["connections"], connectionIds: [connectionId] });
44
- const connection = result.connections?.[0];
45
- if (!connection)
46
- throw new Error(`Connection not found: ${connectionId}`);
47
- if (jsonRequested(options))
48
- return json(connection);
49
- table(rows([connection]), columns);
50
- }
51
- catch (cause) {
52
- handleHttp(cause);
53
- }
54
- });
55
- withJson(connections.command("create <board> <source> <target>").description("Create a Board connection")
56
- .option("--id <id>", "Connection id")
57
- .option("--relation <relation>", "Relation type")
58
- .option("--direction <direction>", "none, forward, backward, or both", "forward")
59
- .option("--label <label>", "Connection label")
60
- .option("--source-port <id>", "Source port id")
61
- .option("--target-port <id>", "Target port id"))
62
- .action(async (target, source, destination, options) => {
63
- try {
64
- const direction = options.direction ?? "forward";
65
- if (!["none", "forward", "backward", "both"].includes(direction)) {
66
- throw new Error("--direction must be none, forward, backward, or both");
67
- }
68
- const board = await resolvedBoard(boards, target);
69
- const connection = createBoardConnection({
70
- id: options.id ?? randomUUID(),
71
- sourceItemId: source,
72
- targetItemId: destination,
73
- relation: options.relation,
74
- direction: direction,
75
- label: options.label,
76
- sourcePortId: options.sourcePort,
77
- targetPortId: options.targetPort,
78
- });
79
- showUpdated(await mutateSemantic(board, [{ type: "connection.create", connection }]), options);
80
- }
81
- catch (cause) {
82
- handleHttp(cause);
83
- }
84
- });
85
- withJson(connections.command("delete <board> <connection-id>").alias("rm").description("Delete a Board connection"))
86
- .action(async (target, connectionId, options) => {
87
- try {
88
- const board = await resolvedBoard(boards, target);
89
- showUpdated(await mutateSemantic(board, [{ type: "connection.delete", connectionId }]), options);
90
- }
91
- catch (cause) {
92
- handleHttp(cause);
93
- }
94
- });
95
- }
@@ -1,31 +0,0 @@
1
- import type { BoardAuthoringReadInput, BoardClient, BoardSemanticCommand } from "@neta-art/cohub";
2
- import type { Command } from "commander";
3
- export type JsonOptions = {
4
- json?: boolean;
5
- };
6
- export type ReadOptions = JsonOptions & {
7
- ids?: string;
8
- };
9
- export declare function withJson(command: Command): Command;
10
- export declare function finite(value: string | undefined, name: string, fallback?: number): number;
11
- export declare function readInput(options: ReadOptions, section: NonNullable<BoardAuthoringReadInput["include"]>[number]): BoardAuthoringReadInput;
12
- export declare function readOptions(command: Command, label?: string): Command;
13
- export declare function resolvedBoard(boards: Command, target: string): Promise<BoardClient>;
14
- /**
15
- * Send semantic commands with one automatic version-conflict retry when the
16
- * caller did not pin a base version. An explicit `baseVersion` is a strict
17
- * optimistic-concurrency check and must fail instead of being rebased.
18
- */
19
- export declare function mutateSemantic(board: BoardClient, commands: BoardSemanticCommand[], options?: {
20
- baseVersion?: number;
21
- mutationId?: string;
22
- dryRun?: boolean;
23
- }): Promise<import("@neta-art/cohub").BoardMutationReceipt>;
24
- export declare function showUpdated(result: {
25
- board: {
26
- version: number;
27
- };
28
- status?: string;
29
- outcome?: string;
30
- replayed?: boolean;
31
- }, options: JsonOptions): void;
@@ -1,71 +0,0 @@
1
- import { createClient } from "../../client.js";
2
- import { resolveBoardId } from "../../board-command-support.js";
3
- import { json, jsonRequested, ok } from "../../output.js";
4
- import { resolveSpace } from "../../space.js";
5
- export function withJson(command) {
6
- return command.option("--json", "Output as JSON");
7
- }
8
- export function finite(value, name, fallback) {
9
- if (value === undefined && fallback !== undefined)
10
- return fallback;
11
- const result = Number(value);
12
- if (!Number.isFinite(result))
13
- throw new Error(`${name} must be a finite number`);
14
- return result;
15
- }
16
- export function readInput(options, section) {
17
- const ids = options.ids?.split(",").map((id) => id.trim()).filter(Boolean);
18
- return {
19
- include: [section],
20
- ...(ids?.length ? { [`${section.slice(0, -1)}Ids`]: ids } : {}),
21
- };
22
- }
23
- export function readOptions(command, label = "Comma-separated resource IDs") {
24
- return command.option("--ids <ids>", label);
25
- }
26
- export async function resolvedBoard(boards, target) {
27
- const spaceId = await resolveSpace(boards);
28
- const boardId = await resolveBoardId(spaceId, target);
29
- return createClient().space(spaceId).board(boardId);
30
- }
31
- /**
32
- * Send semantic commands with one automatic version-conflict retry when the
33
- * caller did not pin a base version. An explicit `baseVersion` is a strict
34
- * optimistic-concurrency check and must fail instead of being rebased.
35
- */
36
- export async function mutateSemantic(board, commands, options = {}) {
37
- const pinnedVersion = options.baseVersion;
38
- const baseVersion = pinnedVersion ?? (await board.summary()).board.version;
39
- const send = (version) => board.mutateSemantic({
40
- mutationId: options.mutationId,
41
- baseVersion: version,
42
- dryRun: options.dryRun ?? false,
43
- commands,
44
- });
45
- try {
46
- return await send(baseVersion);
47
- }
48
- catch (cause) {
49
- if (options.dryRun || pinnedVersion !== undefined || !isVersionConflict(cause))
50
- throw cause;
51
- const retry = (await board.summary()).board.version;
52
- return send(retry);
53
- }
54
- }
55
- function isVersionConflict(error) {
56
- return Boolean(error && typeof error === "object" &&
57
- error.code === "VERSION_CONFLICT");
58
- }
59
- export function showUpdated(result, options) {
60
- if (jsonRequested(options))
61
- return json(result);
62
- if (result.outcome === "dry-run") {
63
- ok(`Validated against Board version ${result.board.version}; no changes written`);
64
- return;
65
- }
66
- if (result.outcome === "noop" || result.status === "validated") {
67
- ok(`Board already matches the requested state at version ${result.board.version}`);
68
- return;
69
- }
70
- ok(`Board updated to version ${result.board.version}${result.replayed ? " (replayed)" : ""}`);
71
- }
@@ -1,2 +0,0 @@
1
- import type { Command } from "commander";
2
- export declare function registerBoardItemCommands(boards: Command): void;