@neta-art/cohub-cli 9.0.0 → 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.
package/README.md CHANGED
@@ -115,6 +115,13 @@ cohub runtime down
115
115
  cohub runtime up -n --name another-project
116
116
  ```
117
117
 
118
+ On macOS and Linux X11, a new Runtime offers screen sharing by default. The prompt
119
+ explains that collaborators and agents can see and control the screen; Enter accepts,
120
+ and `n` declines. Use `--no-display` to skip sharing, or `--display xvfb[:WIDTHxHEIGHT]`
121
+ for a virtual screen. `--yes` also authorizes default screen sharing. Headless and
122
+ Wayland-only Linux sessions skip the default offer; an existing Runtime keeps its
123
+ screen configuration unless explicitly overridden (configuration changes require a restart).
124
+
118
125
  Runtime diagnostics stay as redacted JSONL under the local Runtime state directory and
119
126
  are never uploaded automatically. Use `runtime logs --json` to export a failure report;
120
127
  events carry the local `runtimeId` plus server `requestId` / `traceparent` when a turn has
@@ -225,57 +232,63 @@ cohub -s <spaceId> spaces activity 365 --json
225
232
 
226
233
  ## Boards
227
234
 
228
- Board targets accept a Board ID or a `.board` path. Every command supports `-h` and `--json`:
235
+ A Board is one JSON document with three collections — `board` settings, `items`,
236
+ and `animations`. Board targets accept a Board ID or a `.board` path. Every
237
+ command supports `-h`, and the global `--json` makes the output machine-readable:
229
238
 
230
239
  ```bash
231
240
  cohub boards -h
232
- cohub -s <spaceId> boards inspect boards/plan.board --json
233
- cohub -s <spaceId> boards items list <boardId>
234
- cohub -s <spaceId> boards connections list <boardId>
235
- cohub -s <spaceId> boards capabilities <boardId>
241
+ cohub -s <spaceId> boards get boards/plan.board --json
242
+ cohub -s <spaceId> boards get <boardId> --only items
243
+ cohub -s <spaceId> boards get <boardId> --only board
244
+ cohub -s <spaceId> boards schema
245
+ cohub -s <spaceId> boards history <boardId> --json
236
246
  cohub -s <spaceId> boards watch <boardId> --json
237
247
  ```
238
248
 
239
- Use targeted reads for large Boards:
249
+ Use the filters so a large Board is not read whole:
240
250
 
241
251
  ```bash
242
- cohub -s <spaceId> boards items get <boardId> <itemId> --json
243
- cohub -s <spaceId> boards connections get <boardId> <connectionId> --json
244
- cohub -s <spaceId> boards effects get <boardId> <effectId> --json
245
- cohub -s <spaceId> boards compositions get <boardId> <compositionId> --json
252
+ cohub -s <spaceId> boards get <boardId> --items title,note
253
+ cohub -s <spaceId> boards get <boardId> --within s1
254
+ cohub -s <spaceId> boards get <boardId> --rect 0,0,1600,900
246
255
  ```
247
256
 
248
- Create semantic JSON from an example, then apply it to one resource:
257
+ Start from an example and write it with `apply`, a JSON Merge Patch where fields
258
+ merge, `null` deletes, and unmentioned fields stay:
249
259
 
250
260
  ```bash
251
- cohub boards examples item text > item.json
252
- cohub -s <spaceId> boards items create <boardId> --input item.json
261
+ cohub boards examples workflow > seed.json
262
+ cohub -s <spaceId> boards create boards/plan.board --title "Plan" -i seed.json
253
263
 
254
- cohub boards examples composition fade > intro.json
255
- cohub -s <spaceId> boards compositions apply <boardId> --input intro.json
264
+ cohub -s <spaceId> boards apply <boardId> '{"items":{"title":{"props":{"text":"Updated"}}}}'
265
+ cohub -s <spaceId> boards apply <boardId> -i changes.json
256
266
  ```
257
267
 
258
- Apply related changes atomically with one request. The batch contains semantic commands, not a full Board snapshot:
268
+ Group a whole change into one patch so it lands as one version. `--replace` makes
269
+ the document equal to the patch instead of merging; `--cascade` also deletes
270
+ children and the tracks targeting a deleted item:
259
271
 
260
- ```json
261
- {"commands":[
262
- {"type":"item.patch","itemId":"title","patch":{"props":{"text":"Updated"}}},
263
- {"type":"connection.create","connection":{"id":"title-agent","source":{"itemId":"title"},"target":{"itemId":"agent"}}}
264
- ]}
272
+ ```bash
273
+ cohub -s <spaceId> boards apply <boardId> -i changes.json --dry-run
274
+ cohub -s <spaceId> boards apply <boardId> -i changes.json --base-version 12 --mutation-id deploy-1
265
275
  ```
266
276
 
277
+ Use `--base-version` and `--mutation-id` for controlled, retry-safe scripts, and
278
+ `--dry-run` to validate without writing. `boards preset` prints a preset motion as
279
+ tracks — an apply-ready patch with `--animation`, bare tracks without it:
280
+
267
281
  ```bash
268
- cohub boards examples batch basic > changes.json
269
- cohub -s <spaceId> boards batch <boardId> --input changes.json --dry-run
270
- cohub -s <spaceId> boards batch <boardId> --input changes.json
282
+ cohub -s <spaceId> boards preset rise --targets a,b --animation intro | cohub boards apply <boardId> -i -
283
+ cohub -s <spaceId> boards preset float --targets ship > idle-tracks.json
271
284
  ```
272
285
 
273
- Use `--base-version` and `--mutation-id` for controlled, retry-safe scripts. Use `--dry-run` to validate without writing. Playback is grouped under `playback`:
286
+ Playback commands sit on `boards` directly:
274
287
 
275
288
  ```bash
276
- cohub -s <spaceId> boards playback play <boardId> <compositionId>
277
- cohub -s <spaceId> boards playback seek <boardId> <playbackId> 400
278
- cohub -s <spaceId> boards playback stop <boardId> <playbackId>
289
+ cohub -s <spaceId> boards play <boardId> <animationId> --at 2.5s
290
+ cohub -s <spaceId> boards seek <boardId> 12.5s
291
+ cohub -s <spaceId> boards stop <boardId>
279
292
  ```
280
293
 
281
294
  ## Search
@@ -145,7 +145,7 @@ function registerExportCommand(boards) {
145
145
  .option("--scale <factor>", "Output pixels per board unit", "2")
146
146
  .option("--padding <units>", "Padding around the content in board units")
147
147
  .option("--theme <mode>", "dark or light", "dark")
148
- .option("--background <mode>", "paper or transparent", "paper")
148
+ .option("--paper <mode>", "Output paper: paper or transparent", "paper")
149
149
  .option("--format <format>", `Override the format (${BOARD_EXPORT_FORMATS.join(", ")}, ${BOARD_VIDEO_FORMATS.join(", ")})`)
150
150
  .option("--quality <q>", "JPEG/WebP quality from 0 to 1", "0.92")
151
151
  .option("--fps <rate>", "Video frame rate; defaults to the --at step")
@@ -189,7 +189,7 @@ Examples:
189
189
  scale: parseNumber(options.scale ?? "2", "--scale", { min: 0.01, max: 16 }),
190
190
  ...(options.padding === undefined ? {} : { padding: parseNumber(options.padding, "--padding", { min: 0 }) }),
191
191
  colorScheme: parseChoice(options.theme, "--theme", ["dark", "light"]),
192
- background: parseChoice(options.background, "--background", ["paper", "transparent"]),
192
+ background: parseChoice(options.paper, "--paper", ["paper", "transparent"]),
193
193
  format: videoFormat,
194
194
  fps,
195
195
  output: out,
@@ -213,7 +213,7 @@ Examples:
213
213
  scale: parseNumber(options.scale ?? "2", "--scale", { min: 0.01, max: 16 }),
214
214
  ...(options.padding === undefined ? {} : { padding: parseNumber(options.padding, "--padding", { min: 0 }) }),
215
215
  colorScheme: parseChoice(options.theme, "--theme", ["dark", "light"]),
216
- background: parseChoice(options.background, "--background", ["paper", "transparent"]),
216
+ background: parseChoice(options.paper, "--paper", ["paper", "transparent"]),
217
217
  format: parseExportFormat(options, out),
218
218
  quality: parseNumber(options.quality ?? "0.92", "--quality", { min: 0, max: 1 }),
219
219
  withImages: options.images !== false,
@@ -252,6 +252,9 @@ A Board is one JSON document: { board, items, animations }.
252
252
  get read it
253
253
  apply write it with a JSON Merge Patch; null deletes
254
254
  schema the schema of any part, and what tracks can animate
255
+ preset tracks for a preset motion, ready to apply
256
+ history list versions, or restore one
257
+ export render an image, a frame sequence or a video
255
258
  examples starter documents
256
259
  Times are milliseconds; 500ms, 12.5s and 2m also work on the command line.`);
257
260
  withJson(boards.command("create <path>")
@@ -288,7 +291,7 @@ Times are milliseconds; 500ms, 12.5s and 2m also work on the command line.`);
288
291
  });
289
292
  boards.command("get <board>")
290
293
  .description("Print the Board document as JSON")
291
- .option("--only <sections>", "items, animations, or both (comma-separated)")
294
+ .option("--only <sections>", "board, items, animations (comma-separated)")
292
295
  .option("--items <ids>", "Only these items")
293
296
  .option("--within <frame-id>", "A frame and everything inside it")
294
297
  .option("--rect <rect>", "Items intersecting x,y,width,height")
@@ -298,14 +301,15 @@ Times are milliseconds; 500ms, 12.5s and 2m also work on the command line.`);
298
301
  .addHelpText("after", `
299
302
  cohub boards get plan.board
300
303
  cohub boards get plan.board --within s2
301
- cohub boards get plan.board --only animations`)
304
+ cohub boards get plan.board --only board`)
302
305
  .action(async (target, options) => {
303
306
  try {
304
307
  const only = list(options.only);
305
- if (only?.some((section) => section !== "items" && section !== "animations"))
306
- throw new Error("--only takes items, animations, or both.");
308
+ if (only?.some((section) => section !== "board" && section !== "items" && section !== "animations"))
309
+ throw new Error("--only takes board, items, animations, or a combination.");
310
+ const sections = only ? new Set(only) : null;
307
311
  const input = {
308
- ...(only ? { only: only } : {}),
312
+ ...(only ? { only: only.filter((section) => section !== "board") } : {}),
309
313
  ...(list(options.items) ? { items: list(options.items) } : {}),
310
314
  ...(options.within ? { within: options.within } : {}),
311
315
  ...(options.rect ? { rect: parseRect(options.rect) } : {}),
@@ -313,7 +317,20 @@ Times are milliseconds; 500ms, 12.5s and 2m also work on the command line.`);
313
317
  ...(options.limit ? { limit: parseNumber(options.limit, "--limit", { min: 1, integer: true }) } : {}),
314
318
  ...(options.cursor ? { cursor: options.cursor } : {}),
315
319
  };
316
- outJson(await (await boardClient(boards, target)).get(input));
320
+ const result = await (await boardClient(boards, target)).get(input);
321
+ if (!sections)
322
+ return outJson(result);
323
+ outJson({
324
+ id: result.id,
325
+ title: result.title,
326
+ version: result.version,
327
+ updatedAt: result.updatedAt,
328
+ playback: result.playback,
329
+ ...(sections.has("board") ? { board: result.board } : {}),
330
+ ...(sections.has("items") && result.items ? { items: result.items } : {}),
331
+ ...(sections.has("animations") && result.animations ? { animations: result.animations } : {}),
332
+ ...(result.next ? { next: result.next } : {}),
333
+ });
317
334
  }
318
335
  catch (cause) {
319
336
  handleHttp(cause);
@@ -393,6 +410,7 @@ Times are milliseconds; 500ms, 12.5s and 2m also work on the command line.`);
393
410
  boards.command("schema [target]")
394
411
  .description("Print JSON Schema for a part of a Board")
395
412
  .addHelpText("after", `
413
+ With a target: one item type, or animation, track, or camera.
396
414
  Targets: ${BOARD_SCHEMA_TARGETS.join(", ")}
397
415
  Without a target: units, item types, colors and every animatable property.`)
398
416
  .action((target) => {
@@ -411,14 +429,15 @@ Without a target: units, item types, colors and every animatable property.`)
411
429
  boards.command("preset <name>")
412
430
  .description("Print tracks for a preset motion as a patch for apply")
413
431
  .requiredOption("--targets <ids>", "Comma-separated item ids")
414
- .requiredOption("--animation <id>", "Animation to add the tracks to")
432
+ .option("--animation <id>", "Wrap the tracks in this animation; omit to get bare tracks")
415
433
  .option("--at <time>", "Start of the first target", "0")
416
434
  .option("--stagger <time>", "Delay added per target", "0")
417
435
  .option("--duration <time>", "Length of each target's motion")
418
436
  .option("--ease <ease>", "CSS easing, e.g. ease-out or cubic-bezier(.2,.8,.2,1)")
419
437
  .addHelpText("after", `
420
438
  Presets: ${BOARD_PRESET_NAMES.join(", ")}
421
- cohub boards preset rise --targets a,b,c --animation intro --stagger 120ms | cohub boards apply plan.board -i -`)
439
+ cohub boards preset rise --targets a,b,c --animation intro --stagger 120ms | cohub boards apply plan.board -i -
440
+ cohub boards preset float --targets ship | jq -c '{animations:{idle:.}}' | cohub boards apply plan.board -i -`)
422
441
  .action((name, options) => {
423
442
  try {
424
443
  if (!isBoardPresetName(name))
@@ -433,7 +452,7 @@ Presets: ${BOARD_PRESET_NAMES.join(", ")}
433
452
  ...(options.duration ? { duration: parseBoardTime(options.duration, "--duration") } : {}),
434
453
  ...(options.ease ? { ease: options.ease } : {}),
435
454
  });
436
- outJson({ animations: { [options.animation]: { tracks } } });
455
+ outJson(options.animation ? { animations: { [options.animation]: { tracks } } } : { tracks });
437
456
  }
438
457
  catch (cause) {
439
458
  handleHttp(cause);
@@ -41,8 +41,9 @@ export function registerRuntime(program) {
41
41
  .option("--harness <name>", "Pi or Codex; repeatable", (value, previous) => [...previous, value], [])
42
42
  .option("--pi <path>", "Pi executable")
43
43
  .option("--codex <path>", "Codex executable")
44
- .option("--display [screen]", "Share a screen: auto (this computer), or xvfb[:WIDTHxHEIGHT] for a virtual one")
45
- .option("-y, --yes", "Accept defaults and authorize local execution and native sync")
44
+ .option("--display [screen]", "Share a screen (default: auto when available), or xvfb[:WIDTHxHEIGHT] for a virtual one")
45
+ .option("--no-display", "Disable screen sharing and skip its confirmation")
46
+ .option("-y, --yes", "Accept defaults and authorize local execution, native sync, and screen sharing")
46
47
  .option("--verbose", "Show diagnostic details")
47
48
  .option("--json", "JSON output")
48
49
  .action(async (dir, options) => {
@@ -1,4 +1,4 @@
1
- export declare const SANDBOXD_VERSION = "v2.55.0";
1
+ export declare const SANDBOXD_VERSION = "v2.61.1";
2
2
  export declare class SandboxdDownloadError extends Error {
3
3
  name: string;
4
4
  }
@@ -6,29 +6,8 @@ import { homedir, tmpdir } from "node:os";
6
6
  import { dirname, join } from "node:path";
7
7
  import { pipeline } from "node:stream/promises";
8
8
  import { Readable } from "node:stream";
9
- // The sandboxd binary version is pinned independently of the CLI package: it
10
- // only needs to change when apps/sandbox changes, and the agent-sandbox wire
11
- // protocol ("1") guarantees backward compatibility.
12
- //
13
- // IMPORTANT: this must point at a tag whose CDN artifacts have already been
14
- // published by .github/workflows/sandbox-binaries-build.yml. Only bump it AFTER
15
- // that tag's publish-cdn job has succeeded, otherwise `runtime up` 404s on the
16
- // default download.
17
- //
18
- // v2.54.0 is the first published tag with the private managed Runtime control
19
- // pipe (`COHUB_RUNTIME_MANAGED` over fd 3), so `runtime up` reads connection
20
- // state from the daemon instead of polling the API every five seconds. It also
21
- // carries the optional workspace-search runner download. v2.53.1 already has
22
- // the native FSEvents backends and the `runtimeId` control frame, and older
23
- // releases stay usable through the compatibility readiness/restart path.
24
- //
25
- // v2.54.1 keeps the same wire protocol and adds relay diagnostics: data-channel
26
- // pairing outlives the runner's dial timeout, dial failures distinguish a
27
- // timeout from an explicit rejection, and teardown-time websocket write
28
- // failures log at debug instead of warn. v2.55.0 republishes that compatible
29
- // runner alongside the matching platform release; no new runner capability is
30
- // required.
31
- export const SANDBOXD_VERSION = "v2.55.0";
9
+ // Pin only after all platform artifacts are published to the public CDN.
10
+ export const SANDBOXD_VERSION = "v2.61.1";
32
11
  const BINARY_NAME = "cohub-sandboxd";
33
12
  // Public CDN prefix hosting the release archives (the repo is private, so the
34
13
  // GitHub Release assets are not publicly downloadable). Overridable for staging
package/dist/index.js CHANGED
@@ -56,7 +56,7 @@ Common commands:
56
56
  cohub runtime up ./my-project
57
57
  cohub runtime logs --follow
58
58
  cohub search "release notes"
59
- cohub -s <space-id> boards inspect <board-id>
59
+ cohub -s <space-id> boards get <board-id>
60
60
  cohub -s <space-id> spaces turns ls --author others
61
61
  cohub -s <space-id> spaces sessions turns ls <session-id>
62
62
  cohub -s home spaces files ls
@@ -16,7 +16,7 @@ export type RuntimeUpOptions = {
16
16
  };
17
17
  export declare const resolveLocalSpaceName: (root: string, name?: string) => string;
18
18
  export declare function parseRuntimeHarnesses(values: string[]): ("pi" | "codex")[];
19
- export declare function parseRuntimeDisplay(value: string | boolean | undefined): string | undefined;
19
+ export declare function parseRuntimeDisplay(value: string | boolean | undefined, platform?: NodeJS.Platform, env?: NodeJS.ProcessEnv): string | undefined;
20
20
  export declare function resolveRuntimeTarget(program: Command, target?: string): Promise<string>;
21
21
  export declare function startBackgroundRuntime(config: RuntimeLaunch): Promise<RuntimeSummary>;
22
22
  export declare function runtimeUp(program: Command, dir: string | undefined, options: RuntimeUpOptions): Promise<void>;
@@ -20,15 +20,17 @@ export function parseRuntimeHarnesses(values) {
20
20
  throw new Error("Harness must be pi or codex");
21
21
  return [...new Set(names.length ? names : ["pi"])];
22
22
  }
23
- export function parseRuntimeDisplay(value) {
24
- if (value === undefined || value === false)
23
+ export function parseRuntimeDisplay(value, platform = process.platform, env = process.env) {
24
+ if (value === false)
25
25
  return undefined;
26
- const spec = value === true ? "auto" : value.trim();
26
+ if (value === undefined && platform !== "darwin" && (platform !== "linux" || !env.DISPLAY))
27
+ return undefined;
28
+ const spec = value === undefined || value === true ? "auto" : value.trim();
27
29
  if (!/^(auto|macos|x11(:[\w.:-]+)?|xvfb(:\d{3,4}x\d{3,4})?)$/.test(spec)) {
28
30
  throw new Error("Display must be auto, macos, x11[:display] or xvfb[:WIDTHxHEIGHT]");
29
31
  }
30
- if (process.platform === "linux" && spec === "auto" && !process.env.DISPLAY) {
31
- throw new Error(process.env.WAYLAND_DISPLAY
32
+ if (platform === "linux" && spec === "auto" && !env.DISPLAY) {
33
+ throw new Error(env.WAYLAND_DISPLAY
32
34
  ? "Wayland sessions cannot be shared yet: log in with an X11 session, or use --display xvfb for a virtual screen"
33
35
  : "No X11 session to share (DISPLAY is unset): use --display xvfb for a virtual screen");
34
36
  }
@@ -106,11 +108,11 @@ export async function runtimeUp(program, dir, options) {
106
108
  const binding = await getRuntimeSpaceBinding(root, identity);
107
109
  const existingId = requested || binding?.spaceId;
108
110
  let harnesses = parseRuntimeHarnesses(options.harness);
109
- let display = parseRuntimeDisplay(options.display);
111
+ let display = options.display === undefined ? undefined : parseRuntimeDisplay(options.display);
110
112
  if (existingId && !options.new) {
111
113
  const existing = await requestRuntimeInstance(runtimeInstanceDirectory(identity, existingId));
112
114
  if (existing) {
113
- if (existing.root !== root || options.harness.length && [...existing.harnesses].sort().join() !== [...harnesses].sort().join() || options.pi || options.codex || display && display !== existing.display) {
115
+ if (existing.root !== root || options.harness.length && [...existing.harnesses].sort().join() !== [...harnesses].sort().join() || options.pi || options.codex || options.display !== undefined && display !== existing.display) {
114
116
  throw new Error("Runtime is running with a different configuration. Use down first");
115
117
  }
116
118
  // `up` is the single idempotent entry: a reused instance adopts its running
@@ -125,6 +127,7 @@ export async function runtimeUp(program, dir, options) {
125
127
  return;
126
128
  }
127
129
  }
130
+ display = parseRuntimeDisplay(options.display);
128
131
  if (!options.harness.length)
129
132
  harnesses = await installedHarnesses(root, options);
130
133
  if (!harnesses.length)
@@ -157,8 +160,8 @@ export async function runtimeUp(program, dir, options) {
157
160
  if (/^n(o)?$/i.test(answer.trim()))
158
161
  return;
159
162
  if (display && !isVirtualDisplay(display)) {
160
- const share = await rl.question("Collaborators and agents can see and control this screen. Share it? [y/N] ");
161
- if (!/^y(es)?$/i.test(share.trim()))
163
+ const share = await rl.question("Collaborators and agents can see and control this screen. Share it? [Y/n] ");
164
+ if (!/^(y(es)?)?$/i.test(share.trim()))
162
165
  display = undefined;
163
166
  }
164
167
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/cohub-cli",
3
- "version": "9.0.0",
3
+ "version": "9.1.0",
4
4
  "description": "CLI for Cohub — spaces, sessions, and agent collaboration.",
5
5
  "type": "module",
6
6
  "engines": {