@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 +8 -4
- package/dist/commands/ui.js +72 -13
- package/package.json +2 -2
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
|
|
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
|
|
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()`.
|
package/dist/commands/ui.js
CHANGED
|
@@ -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
|
|
37
|
-
const
|
|
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" : "
|
|
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-
|
|
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
|
|
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
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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.
|
|
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.
|
|
22
|
+
"@neta-art/cohub": "5.9.0"
|
|
23
23
|
},
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"access": "public"
|