@neta-art/cohub-cli 6.4.0 → 6.6.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/client.d.ts +1 -0
- package/dist/client.js +3 -0
- package/dist/commands/app-commerce.js +9 -5
- package/dist/commands/apps.js +37 -1
- package/dist/commands/space-turns.d.ts +21 -1
- package/dist/commands/space-turns.js +91 -7
- package/package.json +3 -3
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/client.d.ts
CHANGED
package/dist/client.js
CHANGED
|
@@ -10,6 +10,9 @@ const clientOptions = () => ({
|
|
|
10
10
|
export function createClient() {
|
|
11
11
|
return new CohubHttpClient(clientOptions());
|
|
12
12
|
}
|
|
13
|
+
export function createClientWithAccessToken(token) {
|
|
14
|
+
return new CohubHttpClient({ ...clientOptions(), getAccessToken: () => token });
|
|
15
|
+
}
|
|
13
16
|
export function createRealtimeClient() {
|
|
14
17
|
return new CohubClient(clientOptions());
|
|
15
18
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveExecutionAppId, } from "@neta-art/cohub";
|
|
1
2
|
import { createClient } from "../client.js";
|
|
2
3
|
import { error, handleHttp, json as outJson, jsonRequested, ok, table } from "../output.js";
|
|
3
4
|
function collectOption(value, previous = []) {
|
|
@@ -9,6 +10,9 @@ function requireText(value, label, flag) {
|
|
|
9
10
|
return text;
|
|
10
11
|
return error(`Missing ${label}`, `Pass ${flag}.`);
|
|
11
12
|
}
|
|
13
|
+
function requireAppId(value) {
|
|
14
|
+
return requireText(value ?? resolveExecutionAppId() ?? undefined, "app ID", "--app-id <id>");
|
|
15
|
+
}
|
|
12
16
|
function requireList(values, label, flag) {
|
|
13
17
|
const items = [...new Set((values ?? []).map((value) => value.trim()).filter(Boolean))];
|
|
14
18
|
if (items.length > 0)
|
|
@@ -89,7 +93,7 @@ Examples:
|
|
|
89
93
|
.option("--product-key <key>", "Product key", collectOption)
|
|
90
94
|
.option("--json", "Output as JSON")
|
|
91
95
|
.action(async (opts) => {
|
|
92
|
-
const appId =
|
|
96
|
+
const appId = requireAppId(opts.appId);
|
|
93
97
|
const productKeys = requireList(opts.productKey, "product key", "--product-key <key>");
|
|
94
98
|
const client = createClient();
|
|
95
99
|
try {
|
|
@@ -108,7 +112,7 @@ Examples:
|
|
|
108
112
|
.option("--app-id <id>", "App ID")
|
|
109
113
|
.option("--json", "Output as JSON")
|
|
110
114
|
.action(async (opts) => {
|
|
111
|
-
const appId =
|
|
115
|
+
const appId = requireAppId(opts.appId);
|
|
112
116
|
const client = createClient();
|
|
113
117
|
try {
|
|
114
118
|
const result = await client.appCommerce.getEntitlements(appId);
|
|
@@ -132,7 +136,7 @@ Examples:
|
|
|
132
136
|
.option("--reason <text>", "Reason for the consumption")
|
|
133
137
|
.option("--json", "Output as JSON")
|
|
134
138
|
.action(async (opts) => {
|
|
135
|
-
const appId =
|
|
139
|
+
const appId = requireAppId(opts.appId);
|
|
136
140
|
const amountText = requireText(opts.amount, "amount", "--amount <n>");
|
|
137
141
|
const amount = Number.parseInt(amountText, 10);
|
|
138
142
|
if (!Number.isSafeInteger(amount) || amount <= 0) {
|
|
@@ -172,7 +176,7 @@ Examples:
|
|
|
172
176
|
.option("--product-key <key>", "Product key")
|
|
173
177
|
.option("--json", "Output as JSON")
|
|
174
178
|
.action(async (opts) => {
|
|
175
|
-
const appId =
|
|
179
|
+
const appId = requireAppId(opts.appId);
|
|
176
180
|
const productKey = requireText(opts.productKey, "product key", "--product-key <key>");
|
|
177
181
|
const client = createClient();
|
|
178
182
|
try {
|
|
@@ -210,7 +214,7 @@ Examples:
|
|
|
210
214
|
.option("--order-id <id>", "Order ID")
|
|
211
215
|
.option("--json", "Output as JSON")
|
|
212
216
|
.action(async (opts) => {
|
|
213
|
-
const appId =
|
|
217
|
+
const appId = requireAppId(opts.appId);
|
|
214
218
|
const orderId = requireText(opts.orderId, "order ID", "--order-id <id>");
|
|
215
219
|
const client = createClient();
|
|
216
220
|
try {
|
package/dist/commands/apps.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { HttpError } from "@neta-art/cohub";
|
|
2
|
-
import { createClient } from "../client.js";
|
|
2
|
+
import { createClient, createClientWithAccessToken } from "../client.js";
|
|
3
3
|
import { error, handleHttp, json as outJson, jsonRequested, ok, table } from "../output.js";
|
|
4
4
|
import { resolveSpace } from "../space.js";
|
|
5
5
|
import { downloadApp } from "../app-download.js";
|
|
@@ -27,6 +27,16 @@ function parseJsonObject(value, name) {
|
|
|
27
27
|
}
|
|
28
28
|
return error(`Invalid ${name}`, `${name} must be a JSON object`);
|
|
29
29
|
}
|
|
30
|
+
function parseJsonValue(value, name) {
|
|
31
|
+
if (!value)
|
|
32
|
+
return null;
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(value);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return error(`Invalid ${name}`, `${name} must be valid JSON`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
30
40
|
function compactObject(input) {
|
|
31
41
|
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined));
|
|
32
42
|
}
|
|
@@ -259,6 +269,32 @@ export function registerApps(program) {
|
|
|
259
269
|
handleHttp(e);
|
|
260
270
|
}
|
|
261
271
|
});
|
|
272
|
+
const actionsCmd = appsCmd
|
|
273
|
+
.command("actions")
|
|
274
|
+
.description("Run published App Actions");
|
|
275
|
+
actionsCmd
|
|
276
|
+
.command("run <app> <action>")
|
|
277
|
+
.description("Run an App Action")
|
|
278
|
+
.option("--input <json>", "JSON input", "null")
|
|
279
|
+
.option("--json", "Output as JSON")
|
|
280
|
+
.action(async (appRef, action, opts) => {
|
|
281
|
+
const client = createClient();
|
|
282
|
+
try {
|
|
283
|
+
const ref = parseAppRef(appRef);
|
|
284
|
+
const detail = "id" in ref
|
|
285
|
+
? await client.apps.getPublicById(ref.id)
|
|
286
|
+
: await client.apps.getBySlug(ref.username, ref.spaceSlug, ref.appSlug);
|
|
287
|
+
const session = await client.apps.createSession(detail.app.id);
|
|
288
|
+
const appClient = createClientWithAccessToken(session.token);
|
|
289
|
+
const result = await appClient.apps.runAction(detail.app.id, action, parseJsonValue(opts.input, "input"));
|
|
290
|
+
if (jsonRequested(opts))
|
|
291
|
+
return outJson(result);
|
|
292
|
+
ok(`App Action queued: ${result.taskRunId}`);
|
|
293
|
+
}
|
|
294
|
+
catch (e) {
|
|
295
|
+
handleHttp(e);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
262
298
|
appsCmd
|
|
263
299
|
.command("resolve <appSlug>")
|
|
264
300
|
.description("Resolve a published app by owner and space slug")
|
|
@@ -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.6.0",
|
|
4
4
|
"description": "CLI for Cohub — spaces, sessions, and agent collaboration.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
"NOTICE"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@neta-art/generation": "^0.1.
|
|
18
|
+
"@neta-art/generation": "^0.1.25",
|
|
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.9.0"
|
|
23
23
|
},
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"access": "public"
|