@neta-art/cohub-cli 3.10.2 → 3.11.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
@@ -329,17 +329,21 @@ through `client.work.realtime` in the SDK rather than as CLI commands.
329
329
 
330
330
  ## Drive the Cohub UI
331
331
 
332
- Show a Work preview in the Cohub tab that started the current work, and call
332
+ Show a file or Work preview in the Cohub tab that started the current work, and call
333
333
  methods the Work exposes.
334
334
 
335
335
  ```bash
336
- cohub ui preview <workId|url|cohub://works/...|username/space/work>
337
- cohub ui preview <work> --call selection.get
336
+ cohub ui preview <workId|url|cohub://works/...|username/space/work|file://path>
337
+ cohub ui preview file://src/main.ts
338
+ cohub ui preview work://alice/studio/launch
339
+ cohub ui preview <work-or-file> --call selection.get
338
340
  cohub ui preview <work> --call board.focus --data '{"nodeId":"n1"}'
339
341
  cohub ui preview <work> --call report.build --input payload.json --json
340
342
  ```
341
343
 
342
- `ui preview` accepts the same Work references as `works get`. Showing a preview is
344
+ `ui preview` accepts `file://` Space-relative paths, `work://` Work references, and
345
+ legacy bare targets. A bare target checks the current Space for a file first, then
346
+ falls back to the same Work references as `works get`. Showing a preview is
343
347
  idempotent: repeating it re-activates the same tab and refreshes any launch state
344
348
  carried by the reference. With `--call`, the command waits for the Work to announce readiness, invokes the method,
345
349
  and waits for the Work to complete the same UI command with `client.ui.reportResult()`.
@@ -1,8 +1,38 @@
1
1
  import { readFileSync } from "node:fs";
2
+ import { HttpError, } from "@neta-art/cohub";
2
3
  import { parseWorkRef, UI_COMMAND_DEFAULT_TIMEOUT_MS, UI_COMMAND_MAX_TIMEOUT_MS, } from "@neta-art/cohub";
3
4
  import { createClient } from "../client.js";
4
5
  import { error, handleHttp, json as outJson, jsonRequested, ok } from "../output.js";
5
6
  import { getWorkByRef } from "../work-ref.js";
7
+ const FILE_SCHEME = "file://";
8
+ const WORK_SCHEME = "work://";
9
+ function optionalSpaceId(command) {
10
+ let current = command;
11
+ while (current) {
12
+ const opts = current.opts();
13
+ if (typeof opts.space === "string" && opts.space.trim())
14
+ return opts.space.trim();
15
+ current = current.parent ?? null;
16
+ }
17
+ return process.env.COHUB_SPACE_ID?.trim() || undefined;
18
+ }
19
+ function parseFilePath(value) {
20
+ const path = value.slice(FILE_SCHEME.length).trim();
21
+ if (!path ||
22
+ path.startsWith("/") ||
23
+ path.includes("\0") ||
24
+ path.split("/").some((segment) => segment === "..") ||
25
+ path.includes("\\")) {
26
+ return error("Invalid file path", "Use a relative Space path after file://.");
27
+ }
28
+ return path;
29
+ }
30
+ function hasFileScheme(value) {
31
+ return value.toLowerCase().startsWith(FILE_SCHEME);
32
+ }
33
+ function hasWorkScheme(value) {
34
+ return value.toLowerCase().startsWith(WORK_SCHEME);
35
+ }
6
36
  function readCallInput(opts) {
7
37
  if (opts.data !== undefined && opts.input !== undefined) {
8
38
  return error("Conflicting input", "Use either --data or --input, not both.");
@@ -33,9 +63,11 @@ function parseTimeout(value) {
33
63
  return parsed;
34
64
  }
35
65
  async function resolveWorkTarget(client, ref) {
36
- const parsed = parseWorkRef(ref);
37
- const detail = await getWorkByRef(client, ref);
66
+ const normalized = hasWorkScheme(ref) ? ref.slice(WORK_SCHEME.length) : ref;
67
+ const parsed = parseWorkRef(normalized);
68
+ const detail = await getWorkByRef(client, normalized);
38
69
  return {
70
+ kind: "work",
39
71
  workId: detail.work.id,
40
72
  label: detail.work.slug,
41
73
  launch: {
@@ -44,6 +76,24 @@ async function resolveWorkTarget(client, ref) {
44
76
  },
45
77
  };
46
78
  }
79
+ async function resolvePreviewTarget(client, command, value) {
80
+ if (hasFileScheme(value))
81
+ return { kind: "file", path: parseFilePath(value) };
82
+ if (hasWorkScheme(value))
83
+ return resolveWorkTarget(client, value);
84
+ const spaceId = optionalSpaceId(command);
85
+ if (spaceId) {
86
+ try {
87
+ await client.space(spaceId).files.read(value);
88
+ return { kind: "file", path: value };
89
+ }
90
+ catch (cause) {
91
+ if (!(cause instanceof HttpError) || cause.status !== 404)
92
+ throw cause;
93
+ }
94
+ }
95
+ return resolveWorkTarget(client, value);
96
+ }
47
97
  function reportDispatch(record) {
48
98
  if (record.status !== "pending") {
49
99
  reportOutcome(record, Boolean(record.command.request));
@@ -53,7 +103,7 @@ function reportDispatch(record) {
53
103
  }
54
104
  function reportOutcome(record, called) {
55
105
  if (record.status === "applied") {
56
- ok(called ? "Work preview shown and method called" : "Work preview shown");
106
+ ok(called ? "Work preview shown and method called" : "Preview shown");
57
107
  if (record.result !== undefined) {
58
108
  console.log(typeof record.result === "string" ? record.result : JSON.stringify(record.result, null, 2));
59
109
  }
@@ -70,15 +120,17 @@ Commands reach only the frontend instance that originated the current chat,
70
120
  resolved from request provenance. Nothing else can be targeted.
71
121
 
72
122
  Examples:
73
- cohub ui preview <work-id>
123
+ cohub ui preview <work-or-file>
124
+ cohub ui preview file://src/main.ts
125
+ cohub ui preview work://alice/studio/launch
74
126
  cohub ui preview alice/studio/launch
75
127
  cohub ui preview https://cohub.live/alice/studio/w/launch?view=timeline
76
128
  cohub ui preview <work-id> --call selection.get
77
129
  cohub ui preview <work-id> --call board.focus --data '{"nodeId":"n1"}'
78
130
  `);
79
131
  const preview = ui
80
- .command("preview <work>")
81
- .description("Show a Work preview tab, optionally calling a method it exposes")
132
+ .command("preview <work-or-file>")
133
+ .description("Show a file or Work preview tab, optionally calling a Work method")
82
134
  .option("--call <method>", "Method the Work registered via client.work.surface.handle()")
83
135
  .option("--data <json>", "Inline JSON input for --call")
84
136
  .option("-i, --input <file>", "JSON input file for --call; use - for stdin")
@@ -98,19 +150,24 @@ Examples:
98
150
  const timeoutMs = parseTimeout(opts.timeoutMs);
99
151
  const client = createClient();
100
152
  try {
101
- const target = await resolveWorkTarget(client, work);
153
+ const target = await resolvePreviewTarget(client, preview, work);
154
+ if (target.kind === "file" && opts.call) {
155
+ return error("Unsupported option", "--call only applies to Work previews.");
156
+ }
102
157
  const request = opts.call
103
158
  ? { method: opts.call, ...(callInput === undefined ? {} : { input: callInput }) }
104
159
  : undefined;
105
160
  const input = {
106
161
  command: {
107
162
  type: "preview.show",
108
- preview: {
109
- kind: "work",
110
- workId: target.workId,
111
- label: target.label,
112
- ...(target.launch.search || target.launch.hash ? { launch: target.launch } : {}),
113
- },
163
+ preview: target.kind === "file"
164
+ ? target
165
+ : {
166
+ kind: "work",
167
+ workId: target.workId,
168
+ label: target.label,
169
+ ...(target.launch.search || target.launch.hash ? { launch: target.launch } : {}),
170
+ },
114
171
  ...(request ? { request } : {}),
115
172
  },
116
173
  ...(opts.client ? { targetClientId: opts.client } : {}),
@@ -133,6 +190,8 @@ Examples:
133
190
  });
134
191
  preview.addHelpText("after", `
135
192
  Notes:
193
+ - Use file:// and work:// to make the target explicit.
194
+ - A plain target checks the current Space for a file before resolving a Work.
136
195
  - Showing a preview is idempotent; repeating it re-activates the same tab.
137
196
  - --call waits for the Work to announce readiness, then invokes the method.
138
197
  - Which methods exist is up to the Work author.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neta-art/cohub-cli",
3
- "version": "3.10.2",
3
+ "version": "3.11.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": "5.8.2"
22
+ "@neta-art/cohub": "5.9.0"
23
23
  },
24
24
  "publishConfig": {
25
25
  "access": "public"