@neta-art/cohub-cli 6.3.0 → 6.5.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 +6 -0
- package/dist/board-export.d.ts +3 -2
- package/dist/board-export.js +3 -2
- package/dist/commands/boards/examples.js +55 -22
- package/dist/commands/boards/items.js +24 -10
- package/dist/commands/boards.d.ts +7 -0
- package/dist/commands/boards.js +9 -5
- package/dist/commands/space-turns.d.ts +21 -1
- package/dist/commands/space-turns.js +91 -7
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -133,8 +133,14 @@ List recent turns across all visible Sessions in a Space:
|
|
|
133
133
|
cohub -s <spaceId> spaces turns ls
|
|
134
134
|
cohub -s <spaceId> spaces turns ls --author others --limit 50 --json
|
|
135
135
|
cohub -s <spaceId> spaces turns ls --session <sessionId>
|
|
136
|
+
cohub -s <spaceId> spaces turns intermediate <sessionId> <turnId>
|
|
137
|
+
cohub -s <spaceId> spaces turns intermediate <sessionId> <turnId> --json
|
|
136
138
|
```
|
|
137
139
|
|
|
140
|
+
When `--session` is provided, the CLI uses the same full turn endpoint as the
|
|
141
|
+
Web session view. Intermediate messages are read from the turn's CDN archive;
|
|
142
|
+
`--json` returns the archive without reducing its content blocks.
|
|
143
|
+
|
|
138
144
|
Use `pageInfo.nextCursor` with `--cursor` to load older pages. Use a previous
|
|
139
145
|
`snapshotCursor` with `--after` and an explicit `--before` boundary to query
|
|
140
146
|
newer turns:
|
package/dist/board-export.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Board image export for the CLI.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* The CLI and web editor share canonical Board geometry and card semantics. Each
|
|
5
|
+
* host selects the renderer primitive suited to its backend; this path uses
|
|
6
|
+
* Canvas2D. Everything platform-specific
|
|
6
7
|
* lives here: fetching the document over HTTP, pulling image bytes out of the
|
|
7
8
|
* space, and locating fonts on disk.
|
|
8
9
|
*/
|
package/dist/board-export.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Board image export for the CLI.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* The CLI and web editor share canonical Board geometry and card semantics. Each
|
|
5
|
+
* host selects the renderer primitive suited to its backend; this path uses
|
|
6
|
+
* Canvas2D. Everything platform-specific
|
|
6
7
|
* lives here: fetching the document over HTTP, pulling image bytes out of the
|
|
7
8
|
* space, and locating fonts on disk.
|
|
8
9
|
*/
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { json } from "../../output.js";
|
|
2
|
-
const frame = { x: 120, y: 80, width: 320, height: 48, rotation: 0 };
|
|
3
2
|
const templates = {
|
|
4
3
|
"batch:basic": {
|
|
5
4
|
commands: [
|
|
@@ -11,7 +10,9 @@ const templates = {
|
|
|
11
10
|
items: [{
|
|
12
11
|
id: "title",
|
|
13
12
|
type: "text",
|
|
14
|
-
|
|
13
|
+
position: { x: 120, y: 80 },
|
|
14
|
+
size: { width: 320, height: 48 },
|
|
15
|
+
rotation: 0,
|
|
15
16
|
props: { text: "Launch plan", fontSize: 32 },
|
|
16
17
|
style: { color: "brand" },
|
|
17
18
|
}],
|
|
@@ -24,21 +25,27 @@ const templates = {
|
|
|
24
25
|
{
|
|
25
26
|
id: "request",
|
|
26
27
|
type: "text",
|
|
27
|
-
|
|
28
|
+
position: { x: 80, y: 190 },
|
|
29
|
+
size: { width: 180, height: 48 },
|
|
30
|
+
rotation: 0,
|
|
28
31
|
props: { text: "Request", fontSize: 28 },
|
|
29
32
|
style: { color: "brand" },
|
|
30
33
|
},
|
|
31
34
|
{
|
|
32
35
|
id: "agent",
|
|
33
36
|
type: "geo",
|
|
34
|
-
|
|
37
|
+
position: { x: 360, y: 150 },
|
|
38
|
+
size: { width: 240, height: 120 },
|
|
39
|
+
rotation: 0,
|
|
35
40
|
props: { shape: "rounded", text: "Agent" },
|
|
36
41
|
style: { color: "blue", fillOpacity: 0.08 },
|
|
37
42
|
},
|
|
38
43
|
{
|
|
39
44
|
id: "result",
|
|
40
45
|
type: "geo",
|
|
41
|
-
|
|
46
|
+
position: { x: 700, y: 150 },
|
|
47
|
+
size: { width: 240, height: 120 },
|
|
48
|
+
rotation: 0,
|
|
42
49
|
props: { shape: "rounded", text: "Result" },
|
|
43
50
|
style: { color: "green", fillOpacity: 0.08 },
|
|
44
51
|
},
|
|
@@ -55,28 +62,36 @@ const templates = {
|
|
|
55
62
|
{
|
|
56
63
|
id: "hero",
|
|
57
64
|
type: "image",
|
|
58
|
-
|
|
65
|
+
position: { x: 80, y: 100 },
|
|
66
|
+
size: { width: 640, height: 360 },
|
|
67
|
+
rotation: 0,
|
|
59
68
|
source: { kind: "space-file", path: "assets/hero.webp" },
|
|
60
69
|
props: {},
|
|
61
70
|
},
|
|
62
71
|
{
|
|
63
72
|
id: "demo-video",
|
|
64
73
|
type: "video",
|
|
65
|
-
|
|
74
|
+
position: { x: 760, y: 100 },
|
|
75
|
+
size: { width: 480, height: 270 },
|
|
76
|
+
rotation: 0,
|
|
66
77
|
source: { kind: "space-file", path: "assets/demo.mp4" },
|
|
67
78
|
props: {},
|
|
68
79
|
},
|
|
69
80
|
{
|
|
70
81
|
id: "soundtrack",
|
|
71
82
|
type: "audio",
|
|
72
|
-
|
|
83
|
+
position: { x: 80, y: 500 },
|
|
84
|
+
size: { width: 400, height: 96 },
|
|
85
|
+
rotation: 0,
|
|
73
86
|
source: { kind: "space-file", path: "assets/soundtrack.mp3" },
|
|
74
87
|
props: {},
|
|
75
88
|
},
|
|
76
89
|
{
|
|
77
90
|
id: "brief",
|
|
78
91
|
type: "file",
|
|
79
|
-
|
|
92
|
+
position: { x: 520, y: 500 },
|
|
93
|
+
size: { width: 320, height: 180 },
|
|
94
|
+
rotation: 0,
|
|
80
95
|
source: { kind: "space-file", path: "docs/brief.md" },
|
|
81
96
|
props: {},
|
|
82
97
|
},
|
|
@@ -90,14 +105,16 @@ const templates = {
|
|
|
90
105
|
{
|
|
91
106
|
id: "title",
|
|
92
107
|
type: "text",
|
|
93
|
-
|
|
108
|
+
position: { x: 120, y: 80 },
|
|
109
|
+
size: { width: 320, height: 48 },
|
|
110
|
+
rotation: 0,
|
|
94
111
|
props: { text: "Launch", fontSize: 32 },
|
|
95
112
|
style: { color: "brand" },
|
|
96
113
|
},
|
|
97
114
|
{
|
|
98
115
|
id: "stroke",
|
|
99
116
|
type: "draw",
|
|
100
|
-
|
|
117
|
+
rotation: 0,
|
|
101
118
|
props: { points: [{ x: 2, y: 98, p: 0.5 }, { x: 90, y: 2, p: 0.5 }, { x: 178, y: 98, p: 0.5 }] },
|
|
102
119
|
style: { color: "violet", strokeWidth: 4 },
|
|
103
120
|
},
|
|
@@ -150,70 +167,86 @@ const templates = {
|
|
|
150
167
|
"item:text": {
|
|
151
168
|
id: "title",
|
|
152
169
|
type: "text",
|
|
153
|
-
|
|
170
|
+
position: { x: 120, y: 80 },
|
|
171
|
+
size: { width: 320, height: 48 },
|
|
172
|
+
rotation: 0,
|
|
154
173
|
props: { text: "Launch plan", fontSize: 32 },
|
|
155
174
|
style: { color: "brand" },
|
|
156
175
|
},
|
|
157
176
|
"item:image": {
|
|
158
177
|
id: "hero",
|
|
159
178
|
type: "image",
|
|
160
|
-
|
|
179
|
+
position: { x: 120, y: 160 },
|
|
180
|
+
size: { width: 640, height: 360 },
|
|
181
|
+
rotation: 0,
|
|
161
182
|
source: { kind: "space-file", path: "assets/hero.webp" },
|
|
162
183
|
props: {},
|
|
163
184
|
},
|
|
164
185
|
"item:geo": {
|
|
165
186
|
id: "goal",
|
|
166
187
|
type: "geo",
|
|
167
|
-
|
|
188
|
+
position: { x: 120, y: 160 },
|
|
189
|
+
size: { width: 280, height: 140 },
|
|
190
|
+
rotation: 0,
|
|
168
191
|
props: { shape: "rounded", text: "Ship" },
|
|
169
192
|
style: { color: "green", fillOpacity: 0.12 },
|
|
170
193
|
},
|
|
171
194
|
"item:frame": {
|
|
172
195
|
id: "scene",
|
|
173
196
|
type: "frame",
|
|
174
|
-
|
|
197
|
+
position: { x: 80, y: 60 },
|
|
198
|
+
size: { width: 960, height: 540 },
|
|
199
|
+
rotation: 0,
|
|
175
200
|
props: { label: "Scene 1" },
|
|
176
201
|
style: { color: "neutral" },
|
|
177
202
|
},
|
|
178
203
|
"item:draw": {
|
|
179
204
|
id: "stroke",
|
|
180
205
|
type: "draw",
|
|
181
|
-
|
|
182
|
-
props: { points: [{ x:
|
|
206
|
+
rotation: 0,
|
|
207
|
+
props: { points: [{ x: 102, y: 198, p: 0.5 }, { x: 190, y: 102, p: 0.5 }, { x: 278, y: 198, p: 0.5 }] },
|
|
183
208
|
style: { color: "violet", strokeWidth: 4 },
|
|
184
209
|
},
|
|
185
210
|
"item:arrow": {
|
|
186
211
|
id: "arrow",
|
|
187
212
|
type: "arrow",
|
|
188
|
-
|
|
213
|
+
rotation: 0,
|
|
189
214
|
props: { start: { x: 116, y: 150 }, end: { x: 344, y: 150 }, bend: 0, arrowStart: false, arrowEnd: true, label: "next" },
|
|
190
215
|
style: { color: "brand", strokeWidth: 2.5 },
|
|
191
216
|
},
|
|
192
217
|
"item:video": {
|
|
193
218
|
id: "demo-video",
|
|
194
219
|
type: "video",
|
|
195
|
-
|
|
220
|
+
position: { x: 120, y: 160 },
|
|
221
|
+
size: { width: 640, height: 360 },
|
|
222
|
+
rotation: 0,
|
|
196
223
|
source: { kind: "space-file", path: "assets/demo.mp4" },
|
|
197
224
|
props: {},
|
|
198
225
|
},
|
|
199
226
|
"item:audio": {
|
|
200
227
|
id: "soundtrack",
|
|
201
228
|
type: "audio",
|
|
202
|
-
|
|
229
|
+
position: { x: 120, y: 540 },
|
|
230
|
+
size: { width: 480, height: 96 },
|
|
231
|
+
rotation: 0,
|
|
203
232
|
source: { kind: "space-file", path: "assets/soundtrack.mp3" },
|
|
204
233
|
props: {},
|
|
205
234
|
},
|
|
206
235
|
"item:file": {
|
|
207
236
|
id: "brief",
|
|
208
237
|
type: "file",
|
|
209
|
-
|
|
238
|
+
position: { x: 120, y: 160 },
|
|
239
|
+
size: { width: 360, height: 220 },
|
|
240
|
+
rotation: 0,
|
|
210
241
|
source: { kind: "space-file", path: "docs/brief.md" },
|
|
211
242
|
props: {},
|
|
212
243
|
},
|
|
213
244
|
"item:task": {
|
|
214
245
|
id: "task",
|
|
215
246
|
type: "task",
|
|
216
|
-
|
|
247
|
+
position: { x: 120, y: 160 },
|
|
248
|
+
size: { width: 420, height: 240 },
|
|
249
|
+
rotation: 0,
|
|
217
250
|
props: { taskRunId: "task-run-id", snapshot: { taskType: "generation", status: "running", title: "Generate concept", artifactCount: 0, artifacts: [] } },
|
|
218
251
|
},
|
|
219
252
|
"effect:pulse": {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { BoardAuthoringItemSchema, BoardItemPatchSchema, } from "@neta-art/cohub";
|
|
3
|
+
import { computeArrowFrame, computeDrawBounds } from "@neta-art/cohub/board";
|
|
3
4
|
import { BOARD_DOMAIN_INPUT_MAX_BYTES, readBoardJsonObject, } from "../../board-command-support.js";
|
|
4
5
|
import { handleHttp, json, jsonRequested, ok, table } from "../../output.js";
|
|
5
6
|
import { readInput, readOptions, resolvedBoard, withJson, } from "./context.js";
|
|
@@ -38,6 +39,25 @@ function mutationOptions(command) {
|
|
|
38
39
|
.option("--mutation-id <id>", "Stable id for safe retries")
|
|
39
40
|
.option("--dry-run", "Validate on the server (references, version, cascade) without writing");
|
|
40
41
|
}
|
|
42
|
+
function itemLayout(item) {
|
|
43
|
+
if ("position" in item) {
|
|
44
|
+
const size = "size" in item ? item.size : undefined;
|
|
45
|
+
return { x: item.position.x, y: item.position.y, width: size?.width ?? "", height: size?.height ?? "" };
|
|
46
|
+
}
|
|
47
|
+
if (item.type === "draw") {
|
|
48
|
+
const points = item.props.points;
|
|
49
|
+
const strokeWidth = item.style?.strokeWidth ?? 4;
|
|
50
|
+
const bounds = computeDrawBounds(points, strokeWidth);
|
|
51
|
+
return { x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height };
|
|
52
|
+
}
|
|
53
|
+
const bounds = computeArrowFrame({
|
|
54
|
+
start: item.props.start,
|
|
55
|
+
end: item.props.end,
|
|
56
|
+
bend: item.props.bend,
|
|
57
|
+
size: item.style?.strokeWidth ?? 2.5,
|
|
58
|
+
});
|
|
59
|
+
return { x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height };
|
|
60
|
+
}
|
|
41
61
|
export function registerBoardItemCommands(boards) {
|
|
42
62
|
const items = boards.command("items").description("Author Board items with semantic JSON");
|
|
43
63
|
readOptions(withJson(items.command("list <board>").alias("ls").description("List semantic Board items")))
|
|
@@ -51,10 +71,7 @@ export function registerBoardItemCommands(boards) {
|
|
|
51
71
|
table(items.map((item) => ({
|
|
52
72
|
id: item.id,
|
|
53
73
|
type: item.type,
|
|
54
|
-
|
|
55
|
-
y: item.frame.y,
|
|
56
|
-
width: item.frame.width,
|
|
57
|
-
height: item.frame.height,
|
|
74
|
+
...itemLayout(item),
|
|
58
75
|
})), [
|
|
59
76
|
{ key: "id", label: "ID" },
|
|
60
77
|
{ key: "type", label: "TYPE" },
|
|
@@ -81,10 +98,7 @@ export function registerBoardItemCommands(boards) {
|
|
|
81
98
|
table([{
|
|
82
99
|
id: item.id,
|
|
83
100
|
type: item.type,
|
|
84
|
-
|
|
85
|
-
y: item.frame.y,
|
|
86
|
-
width: item.frame.width,
|
|
87
|
-
height: item.frame.height,
|
|
101
|
+
...itemLayout(item),
|
|
88
102
|
}], [
|
|
89
103
|
{ key: "id", label: "ID" },
|
|
90
104
|
{ key: "type", label: "TYPE" },
|
|
@@ -103,7 +117,7 @@ export function registerBoardItemCommands(boards) {
|
|
|
103
117
|
.requiredOption("-i, --input <file>", "Board Item JSON; use - for stdin")
|
|
104
118
|
.addHelpText("after", `
|
|
105
119
|
Minimal text item:
|
|
106
|
-
{"id":"title","type":"text","
|
|
120
|
+
{"id":"title","type":"text","position":{"x":120,"y":80},"size":{"width":320,"height":48},"props":{"text":"Launch plan","fontSize":32},"style":{"color":"brand"}}`))
|
|
107
121
|
.action(async (target, options) => {
|
|
108
122
|
try {
|
|
109
123
|
const value = await readBoardJsonObject(options.input, BOARD_DOMAIN_INPUT_MAX_BYTES);
|
|
@@ -122,7 +136,7 @@ Minimal text item:
|
|
|
122
136
|
.addHelpText("after", `
|
|
123
137
|
Objects merge recursively, arrays replace, and null clears optional fields.
|
|
124
138
|
Move and rename a text item without replacing its size or font:
|
|
125
|
-
{"
|
|
139
|
+
{"position":{"x":160,"y":120},"props":{"text":"Updated"}}`))
|
|
126
140
|
.action(async (target, itemId, options) => {
|
|
127
141
|
try {
|
|
128
142
|
const value = await readBoardJsonObject(options.input, BOARD_DOMAIN_INPUT_MAX_BYTES);
|
|
@@ -7,4 +7,11 @@ export declare function parseViewport(value?: string): {
|
|
|
7
7
|
width: number;
|
|
8
8
|
height: number;
|
|
9
9
|
} | undefined;
|
|
10
|
+
export declare function showCreated(result: {
|
|
11
|
+
board: {
|
|
12
|
+
id: string;
|
|
13
|
+
title: string;
|
|
14
|
+
version: number;
|
|
15
|
+
};
|
|
16
|
+
}, path: string): void;
|
|
10
17
|
export declare function registerBoards(program: Command): Command;
|
package/dist/commands/boards.js
CHANGED
|
@@ -38,8 +38,9 @@ export function parseViewport(value) {
|
|
|
38
38
|
throw new Error("viewport width and height must be greater than zero");
|
|
39
39
|
return { x, y, width, height };
|
|
40
40
|
}
|
|
41
|
-
function showCreated(result) {
|
|
42
|
-
table([result.board], [
|
|
41
|
+
export function showCreated(result, path) {
|
|
42
|
+
table([{ path, ...result.board }], [
|
|
43
|
+
{ key: "path", label: "Path" },
|
|
43
44
|
{ key: "id", label: "ID" },
|
|
44
45
|
{ key: "title", label: "Title" },
|
|
45
46
|
{ key: "version", label: "Version" },
|
|
@@ -240,9 +241,9 @@ Generate an editable seed:
|
|
|
240
241
|
};
|
|
241
242
|
const result = await createClient().space(resolveSpace(boards)).boards.create(input);
|
|
242
243
|
if (jsonRequested(options))
|
|
243
|
-
return outJson(result);
|
|
244
|
-
ok(`Board created: ${
|
|
245
|
-
showCreated(result);
|
|
244
|
+
return outJson({ ...result, path });
|
|
245
|
+
ok(`Board created: ${path}`);
|
|
246
|
+
showCreated(result, path);
|
|
246
247
|
}
|
|
247
248
|
catch (cause) {
|
|
248
249
|
handleHttp(cause);
|
|
@@ -276,6 +277,9 @@ Apply multiple changes atomically:
|
|
|
276
277
|
.description("Show authoring schemas and runtime capabilities")
|
|
277
278
|
.addHelpText("after", `
|
|
278
279
|
Use --json for complete Item, patch, mutation, effect, Composition, and create JSON Schemas.
|
|
280
|
+
Coordinates:
|
|
281
|
+
draw points and arrow start/end points are world-space.
|
|
282
|
+
Position and size are optional; Board geometry derives the persisted frame automatically.
|
|
279
283
|
Use boards examples for editable starter JSON.`))
|
|
280
284
|
.action(async (target, options) => {
|
|
281
285
|
try {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { SpaceTurnListItem, SpaceTurnListOptions, SpaceTurnsResponse } from "@neta-art/cohub";
|
|
1
|
+
import type { SpaceTurnListItem, SpaceTurnListOptions, SpaceTurnsResponse, SessionTurnRecord, TurnIntermediateMessagesFile } from "@neta-art/cohub";
|
|
2
2
|
import type { Command } from "commander";
|
|
3
3
|
import { type Row } from "../output.js";
|
|
4
4
|
export type SpaceTurnListCliOptions = {
|
|
@@ -9,12 +9,30 @@ export type SpaceTurnListCliOptions = {
|
|
|
9
9
|
limit?: string;
|
|
10
10
|
session?: string;
|
|
11
11
|
json?: boolean;
|
|
12
|
+
direction?: "older" | "newer";
|
|
12
13
|
};
|
|
13
14
|
type SpaceTurnsCommandClient = {
|
|
14
15
|
space(spaceId: string): {
|
|
15
16
|
turns: {
|
|
16
17
|
list(options: SpaceTurnListOptions): Promise<SpaceTurnsResponse>;
|
|
17
18
|
};
|
|
19
|
+
session(sessionId: string): {
|
|
20
|
+
turns: {
|
|
21
|
+
listPaginated(options?: {
|
|
22
|
+
cursor?: number;
|
|
23
|
+
limit?: number;
|
|
24
|
+
direction?: "older" | "newer";
|
|
25
|
+
}): Promise<{
|
|
26
|
+
session: unknown;
|
|
27
|
+
turns: SessionTurnRecord[];
|
|
28
|
+
hasMore: boolean;
|
|
29
|
+
nextCursor: number | undefined;
|
|
30
|
+
}>;
|
|
31
|
+
intermediate: {
|
|
32
|
+
get(turnId: string): Promise<TurnIntermediateMessagesFile | null>;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
};
|
|
18
36
|
};
|
|
19
37
|
};
|
|
20
38
|
export declare class InvalidSpaceTurnCliOptionsError extends Error {
|
|
@@ -22,6 +40,8 @@ export declare class InvalidSpaceTurnCliOptionsError extends Error {
|
|
|
22
40
|
constructor(message: string, detail: string);
|
|
23
41
|
}
|
|
24
42
|
export declare function parseSpaceTurnListOptions(options: SpaceTurnListCliOptions): SpaceTurnListOptions;
|
|
43
|
+
export declare function parseSessionTurnCursor(value: string | undefined): number | undefined;
|
|
44
|
+
export declare function validateSpaceTurnListMode(options: SpaceTurnListCliOptions): void;
|
|
25
45
|
export declare function toSpaceTurnRows(turns: SpaceTurnListItem[]): Row[];
|
|
26
46
|
export declare function registerSpaceTurns(spacesCmd: Command, dependencies?: {
|
|
27
47
|
createClient?: () => SpaceTurnsCommandClient;
|
|
@@ -48,6 +48,23 @@ export function parseSpaceTurnListOptions(options) {
|
|
|
48
48
|
sessionId: options.session,
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
|
+
export function parseSessionTurnCursor(value) {
|
|
52
|
+
if (value === undefined)
|
|
53
|
+
return undefined;
|
|
54
|
+
const cursor = Number(value);
|
|
55
|
+
if (!Number.isSafeInteger(cursor) || cursor < 1) {
|
|
56
|
+
throw new InvalidSpaceTurnCliOptionsError("Invalid cursor", "Session cursor must be a positive turn sequence");
|
|
57
|
+
}
|
|
58
|
+
return cursor;
|
|
59
|
+
}
|
|
60
|
+
export function validateSpaceTurnListMode(options) {
|
|
61
|
+
if (!options.session && options.direction !== undefined) {
|
|
62
|
+
throw new InvalidSpaceTurnCliOptionsError("Invalid direction", "--direction only applies with --session");
|
|
63
|
+
}
|
|
64
|
+
if (options.session && (options.author || options.after || options.before)) {
|
|
65
|
+
throw new InvalidSpaceTurnCliOptionsError("Invalid session turn options", "--session uses the Session turns API; omit --author, --after, and --before");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
51
68
|
export function toSpaceTurnRows(turns) {
|
|
52
69
|
return turns.map((turn) => ({
|
|
53
70
|
createdAt: turn.createdAt,
|
|
@@ -71,13 +88,14 @@ export function registerSpaceTurns(spacesCmd, dependencies = {}) {
|
|
|
71
88
|
turnsCmd
|
|
72
89
|
.command("ls")
|
|
73
90
|
.alias("list")
|
|
74
|
-
.description("List
|
|
75
|
-
.option("--author <any|self|others>", "Filter turns by author")
|
|
76
|
-
.option("--after <cursor>", "
|
|
77
|
-
.option("--before <timestamp>", "
|
|
78
|
-
.option("--cursor <cursor>", "
|
|
79
|
-
.option("--limit <n>", "
|
|
80
|
-
.option("--session <id>", "
|
|
91
|
+
.description("List turns in the space, or full turns from one session")
|
|
92
|
+
.option("--author <any|self|others>", "Filter space turns by author")
|
|
93
|
+
.option("--after <cursor>", "Start after a previous snapshot cursor")
|
|
94
|
+
.option("--before <timestamp>", "End at an ISO 8601 timestamp")
|
|
95
|
+
.option("--cursor <cursor>", "Page cursor; with --session, use a turn sequence")
|
|
96
|
+
.option("--limit <n>", "Maximum turns per page, 1-100")
|
|
97
|
+
.option("--session <id>", "Use the full turn list for this session")
|
|
98
|
+
.option("--direction <older|newer>", "Session page direction; use with --session")
|
|
81
99
|
.option("--json", "Output as JSON")
|
|
82
100
|
.action(async (options) => {
|
|
83
101
|
let query;
|
|
@@ -93,6 +111,45 @@ export function registerSpaceTurns(spacesCmd, dependencies = {}) {
|
|
|
93
111
|
const spaceId = resolveSpace(spacesCmd);
|
|
94
112
|
const client = dependencies.createClient?.() ?? createClient();
|
|
95
113
|
try {
|
|
114
|
+
try {
|
|
115
|
+
validateSpaceTurnListMode(options);
|
|
116
|
+
}
|
|
117
|
+
catch (cause) {
|
|
118
|
+
if (cause instanceof InvalidSpaceTurnCliOptionsError)
|
|
119
|
+
return error(cause.message, cause.detail);
|
|
120
|
+
throw cause;
|
|
121
|
+
}
|
|
122
|
+
if (options.session) {
|
|
123
|
+
let sessionCursor;
|
|
124
|
+
try {
|
|
125
|
+
sessionCursor = parseSessionTurnCursor(options.cursor);
|
|
126
|
+
}
|
|
127
|
+
catch (cause) {
|
|
128
|
+
if (cause instanceof InvalidSpaceTurnCliOptionsError)
|
|
129
|
+
return error(cause.message, cause.detail);
|
|
130
|
+
throw cause;
|
|
131
|
+
}
|
|
132
|
+
const result = await client.space(spaceId).session(options.session).turns.listPaginated({
|
|
133
|
+
cursor: sessionCursor,
|
|
134
|
+
limit: query.limit,
|
|
135
|
+
direction: options.direction,
|
|
136
|
+
});
|
|
137
|
+
if (jsonRequested(options))
|
|
138
|
+
return outJson(result);
|
|
139
|
+
if (result.turns.length === 0)
|
|
140
|
+
return console.log(" No turns found");
|
|
141
|
+
table(result.turns, [
|
|
142
|
+
{ key: "sequence", label: "Seq" },
|
|
143
|
+
{ key: "id", label: "Turn ID" },
|
|
144
|
+
{ key: "status", label: "Status" },
|
|
145
|
+
{ key: "userText", label: "User" },
|
|
146
|
+
{ key: "assistantText", label: "Assistant" },
|
|
147
|
+
{ key: "updatedAt", label: "Updated" },
|
|
148
|
+
]);
|
|
149
|
+
if (result.hasMore)
|
|
150
|
+
console.log(`\n More turns available - next cursor: ${result.nextCursor}`);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
96
153
|
const result = await client.space(spaceId).turns.list(query);
|
|
97
154
|
if (jsonRequested(options))
|
|
98
155
|
return outJson(result);
|
|
@@ -117,5 +174,32 @@ export function registerSpaceTurns(spacesCmd, dependencies = {}) {
|
|
|
117
174
|
handleHttp(cause);
|
|
118
175
|
}
|
|
119
176
|
});
|
|
177
|
+
turnsCmd
|
|
178
|
+
.command("intermediate <sessionId> <turnId>")
|
|
179
|
+
.description("Read persisted intermediate messages from the CDN archive")
|
|
180
|
+
.option("--json", "Output as JSON")
|
|
181
|
+
.action(async (sessionId, turnId, options) => {
|
|
182
|
+
const spaceId = resolveSpace(spacesCmd);
|
|
183
|
+
const client = dependencies.createClient?.() ?? createClient();
|
|
184
|
+
try {
|
|
185
|
+
const archive = await client.space(spaceId).session(sessionId).turns.intermediate.get(turnId);
|
|
186
|
+
if (jsonRequested(options))
|
|
187
|
+
return outJson(archive);
|
|
188
|
+
if (!archive)
|
|
189
|
+
return console.log(" No intermediate messages found");
|
|
190
|
+
console.log(`Turn ${archive.turnId}`);
|
|
191
|
+
console.log(` ${archive.summary.messageCount} intermediate messages · ${archive.summary.toolCallCount} tool calls`);
|
|
192
|
+
for (const [index, message] of archive.messages.entries()) {
|
|
193
|
+
console.log(`\n${index + 1}. ${message.role}${message.provider ? ` · ${message.provider}/${message.model ?? ""}` : ""}`);
|
|
194
|
+
if (message.text)
|
|
195
|
+
console.log(message.text);
|
|
196
|
+
if (message.toolCallsObjectKey)
|
|
197
|
+
console.log(" Tool calls: available");
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
catch (cause) {
|
|
201
|
+
handleHttp(cause);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
120
204
|
return turnsCmd;
|
|
121
205
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neta-art/cohub-cli",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.5.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.
|
|
22
|
+
"@neta-art/cohub": "8.8.0"
|
|
23
23
|
},
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"access": "public"
|