@neta-art/cohub-cli 9.1.0 → 9.1.2
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 +2 -2
- package/dist/board-export.d.ts +6 -6
- package/dist/board-export.js +11 -11
- package/dist/board-kernel.d.ts +1 -1
- package/dist/board-kernel.js +623 -412
- package/dist/commands/me.js +2 -0
- package/dist/commands/spaces.js +3 -0
- package/dist/network-error.d.ts +3 -0
- package/dist/network-error.js +22 -0
- package/dist/output.js +4 -9
- package/dist/runtime/connection.js +2 -1
- package/dist/runtime/harness.js +3 -6
- package/dist/runtime/model-catalog.d.ts +8 -0
- package/dist/runtime/model-catalog.js +50 -2
- package/dist/runtime/native/execution.js +4 -3
- package/dist/safe-remote-image.d.ts +5 -1
- package/dist/safe-remote-image.js +16 -8
- package/dist/session-status.d.ts +6 -0
- package/dist/session-status.js +14 -0
- package/package.json +4 -4
package/dist/commands/me.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createClient } from "../client.js";
|
|
2
2
|
import { formatLocalDateTime, handleHttp, json as outJson, jsonRequested, table } from "../output.js";
|
|
3
|
+
import { SESSION_STATUS_COLUMN } from "../session-status.js";
|
|
3
4
|
function parseInteger(value, name, min) {
|
|
4
5
|
const parsed = Number.parseInt(value, 10);
|
|
5
6
|
if (!Number.isSafeInteger(parsed) || parsed < min) {
|
|
@@ -34,6 +35,7 @@ export function registerMe(program) {
|
|
|
34
35
|
{ key: "id", label: "ID" },
|
|
35
36
|
{ key: "spaceId", label: "Space" },
|
|
36
37
|
{ key: "title", label: "Title" },
|
|
38
|
+
SESSION_STATUS_COLUMN,
|
|
37
39
|
{ key: "totalMessages", label: "Messages" },
|
|
38
40
|
{ key: "createdAt", label: "Created" },
|
|
39
41
|
]);
|
package/dist/commands/spaces.js
CHANGED
|
@@ -4,6 +4,7 @@ import { readSentTurns, readSessionTurnOrigin, resolveCohubEnvironment } from "@
|
|
|
4
4
|
import { uploadAvatarAsset, uploadChatImageAsset } from "../avatar.js";
|
|
5
5
|
import { createClient } from "../client.js";
|
|
6
6
|
import { table, json as outJson, jsonRequested, ok, error, handleHttp, formatEpochMs, truncateText } from "../output.js";
|
|
7
|
+
import { SESSION_STATUS_COLUMN } from "../session-status.js";
|
|
7
8
|
import { resolveSpace } from "../space.js";
|
|
8
9
|
import { uploadLocalFiles } from "../space-files/upload.js";
|
|
9
10
|
import { registerSpaceCommerce } from "./space-commerce.js";
|
|
@@ -1329,6 +1330,7 @@ function registerSessions(spacesCmd) {
|
|
|
1329
1330
|
table(result.sessions, [
|
|
1330
1331
|
{ key: "id", label: "ID" },
|
|
1331
1332
|
{ key: "title", label: "Title" },
|
|
1333
|
+
SESSION_STATUS_COLUMN,
|
|
1332
1334
|
{ key: "totalMessages", label: "Messages" },
|
|
1333
1335
|
{ key: "createdAt", label: "Created" },
|
|
1334
1336
|
]);
|
|
@@ -1376,6 +1378,7 @@ function registerSessions(spacesCmd) {
|
|
|
1376
1378
|
table([result.session], [
|
|
1377
1379
|
{ key: "id", label: "ID" },
|
|
1378
1380
|
{ key: "title", label: "Title" },
|
|
1381
|
+
SESSION_STATUS_COLUMN,
|
|
1379
1382
|
{ key: "totalMessages", label: "Messages" },
|
|
1380
1383
|
{ key: "totalToolCalls", label: "Tool Calls" },
|
|
1381
1384
|
{ key: "createdAt", label: "Created" },
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const text = (value) => (typeof value === "string" && value ? value : null);
|
|
2
|
+
export const isFetchFailure = (error) => error instanceof Error && error.message === "fetch failed";
|
|
3
|
+
export function fetchFailureReason(error) {
|
|
4
|
+
if (!isFetchFailure(error) || typeof error.cause !== "object" || !error.cause)
|
|
5
|
+
return null;
|
|
6
|
+
const cause = error.cause;
|
|
7
|
+
const message = text(cause.message);
|
|
8
|
+
const code = text(cause.code);
|
|
9
|
+
const hostname = text(cause.hostname);
|
|
10
|
+
const parts = [
|
|
11
|
+
code && !message?.includes(code) ? code : null,
|
|
12
|
+
hostname && !message?.includes(hostname) ? `host: ${hostname}` : null,
|
|
13
|
+
message,
|
|
14
|
+
].filter(Boolean);
|
|
15
|
+
return parts.length > 0 ? parts.join(" · ") : null;
|
|
16
|
+
}
|
|
17
|
+
export function describeError(error) {
|
|
18
|
+
if (!(error instanceof Error))
|
|
19
|
+
return String(error);
|
|
20
|
+
const reason = fetchFailureReason(error);
|
|
21
|
+
return reason ? `${error.message} (${reason})` : error.message;
|
|
22
|
+
}
|
package/dist/output.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
2
|
import { extractBillingPayload } from "@neta-art/cohub";
|
|
3
|
+
import { fetchFailureReason, isFetchFailure } from "./network-error.js";
|
|
3
4
|
/**
|
|
4
5
|
* Timestamp → local ISO-style date and time with the system's UTC offset.
|
|
5
6
|
*
|
|
@@ -146,16 +147,10 @@ function debugErrorMetaFromBody(body) {
|
|
|
146
147
|
return items;
|
|
147
148
|
}
|
|
148
149
|
function fetchFailureDetail(e) {
|
|
149
|
-
if (!(e
|
|
150
|
+
if (!isFetchFailure(e))
|
|
150
151
|
return null;
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
const hostname = typeof cause?.hostname === "string" ? cause.hostname : null;
|
|
154
|
-
const message = typeof cause?.message === "string" ? cause.message : null;
|
|
155
|
-
const parts = [code, hostname && `host: ${hostname}`, message].filter(Boolean);
|
|
156
|
-
return parts.length > 0
|
|
157
|
-
? `Network request failed (${parts.join(" · ")}). Check DNS/proxy/firewall settings and try again.`
|
|
158
|
-
: "Network request failed. Check DNS/proxy/firewall settings and try again.";
|
|
152
|
+
const reason = fetchFailureReason(e);
|
|
153
|
+
return `Network request failed${reason ? ` (${reason})` : ""}. Check DNS/proxy/firewall settings and try again.`;
|
|
159
154
|
}
|
|
160
155
|
function errorPresentationFromHttpError(e) {
|
|
161
156
|
const httpError = e;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { setTimeout as delay } from "node:timers/promises";
|
|
3
3
|
import { RUNTIME_MAX_FRAME_BYTES, RUNTIME_PROTOCOL_VERSION, runtimeCommandSchema, runtimeReadySchema, runtimeNativeStopSchema, } from "@neta-art/cohub";
|
|
4
|
+
import { describeError } from "../network-error.js";
|
|
4
5
|
import { executeTurn } from "./native/execution.js";
|
|
5
6
|
import { ContextRequiredError } from "./native/results.js";
|
|
6
7
|
import { ProcessCleanupUncertainError } from "./process-group.js";
|
|
@@ -579,7 +580,7 @@ async function connect(options) {
|
|
|
579
580
|
error: serializeDiagnosticError(error),
|
|
580
581
|
}, context);
|
|
581
582
|
try {
|
|
582
|
-
emit({ type: "turn.error", message:
|
|
583
|
+
emit({ type: "turn.error", message: describeError(error), uncertain: !!execution.result || frame.resumeOnly === true || error instanceof ProcessCleanupUncertainError });
|
|
583
584
|
}
|
|
584
585
|
catch {
|
|
585
586
|
// Native files remain for recovery.
|
package/dist/runtime/harness.js
CHANGED
|
@@ -2,7 +2,7 @@ import { access, stat } from "node:fs/promises";
|
|
|
2
2
|
import { constants } from "node:fs";
|
|
3
3
|
import { delimiter, join, resolve, win32 } from "node:path";
|
|
4
4
|
import { JsonRpcProcess, record } from "./json-rpc.js";
|
|
5
|
-
import { codexModelCatalog } from "./model-catalog.js";
|
|
5
|
+
import { codexModelCatalog, piModelCatalog } from "./model-catalog.js";
|
|
6
6
|
import { downloadPublicImage } from "../safe-remote-image.js";
|
|
7
7
|
export function harnessExecutableCandidates(binary, cwd, path, platform = process.platform, pathExt = process.env.PATHEXT) {
|
|
8
8
|
const windows = platform === "win32";
|
|
@@ -68,11 +68,8 @@ export async function discoverHarnesses(harnesses, options, cwd) {
|
|
|
68
68
|
try {
|
|
69
69
|
if (harness === "pi") {
|
|
70
70
|
const result = await rpc.request("get_available_models");
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (model.id && model.provider)
|
|
74
|
-
models.push({ harness, id: text(model.id), provider: text(model.provider), name: text(model.name) || text(model.id) });
|
|
75
|
-
}
|
|
71
|
+
const state = await rpc.request("get_state");
|
|
72
|
+
models.push(...piModelCatalog(array(result.models), state));
|
|
76
73
|
}
|
|
77
74
|
else {
|
|
78
75
|
await initializeCodex(rpc);
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import type { RuntimeCapabilities } from "@neta-art/cohub";
|
|
2
2
|
import { type JsonRecord } from "./json-rpc.js";
|
|
3
3
|
type Model = RuntimeCapabilities["models"][number];
|
|
4
|
+
/**
|
|
5
|
+
* Publish only picker metadata, never provider credentials or request configuration.
|
|
6
|
+
* `state` is a fresh Pi's `get_state`: its model is what Pi runs when Cohub requests
|
|
7
|
+
* nothing, and its level is resolved and clamped for that model only. Pi's levels for
|
|
8
|
+
* other models live in its settings, which RPC does not expose; those models fall back
|
|
9
|
+
* to the picker's rule, and Cohub always sends the chosen level explicitly.
|
|
10
|
+
*/
|
|
11
|
+
export declare function piModelCatalog(entries: unknown[], state: JsonRecord): Model[];
|
|
4
12
|
/** Codex's built-in catalog is not authoritative for an arbitrary custom provider. */
|
|
5
13
|
export declare function codexModelCatalog(configResponse: JsonRecord, entries: unknown[]): Model[];
|
|
6
14
|
export {};
|
|
@@ -1,4 +1,50 @@
|
|
|
1
1
|
import { record } from "./json-rpc.js";
|
|
2
|
+
const thinkingLevels = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
3
|
+
const thinkingLevel = (value) => thinkingLevels.includes(value) ? value : undefined;
|
|
4
|
+
/**
|
|
5
|
+
* Publish only picker metadata, never provider credentials or request configuration.
|
|
6
|
+
* `state` is a fresh Pi's `get_state`: its model is what Pi runs when Cohub requests
|
|
7
|
+
* nothing, and its level is resolved and clamped for that model only. Pi's levels for
|
|
8
|
+
* other models live in its settings, which RPC does not expose; those models fall back
|
|
9
|
+
* to the picker's rule, and Cohub always sends the chosen level explicitly.
|
|
10
|
+
*/
|
|
11
|
+
export function piModelCatalog(entries, state) {
|
|
12
|
+
const current = record(state.model);
|
|
13
|
+
const currentLevel = thinkingLevel(state.thinkingLevel);
|
|
14
|
+
return entries.flatMap((value) => {
|
|
15
|
+
const model = record(value);
|
|
16
|
+
if (typeof model.id !== "string" || !model.id || typeof model.provider !== "string" || !model.provider)
|
|
17
|
+
return [];
|
|
18
|
+
const map = record(model.thinkingLevelMap);
|
|
19
|
+
const thinkingLevelMap = Object.fromEntries(thinkingLevels.flatMap((level) => map[level] === null || typeof map[level] === "string" ? [[level, map[level]]] : []));
|
|
20
|
+
const isDefault = current.provider === model.provider && current.id === model.id;
|
|
21
|
+
return [{
|
|
22
|
+
harness: "pi", id: model.id, provider: model.provider, name: typeof model.name === "string" ? model.name : model.id,
|
|
23
|
+
...(isDefault ? { isDefault } : {}),
|
|
24
|
+
...(typeof model.reasoning === "boolean" ? { reasoning: model.reasoning } : {}),
|
|
25
|
+
...(isDefault && currentLevel ? { defaultThinkingLevel: currentLevel } : {}),
|
|
26
|
+
...(Object.keys(thinkingLevelMap).length ? { thinkingLevelMap } : {}),
|
|
27
|
+
}];
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function codexThinking(model, configuredEffort) {
|
|
31
|
+
if (!Array.isArray(model.supportedReasoningEfforts))
|
|
32
|
+
return {};
|
|
33
|
+
const efforts = new Set(model.supportedReasoningEfforts.map((entry) => record(entry).reasoningEffort));
|
|
34
|
+
const thinkingLevelMap = Object.fromEntries(thinkingLevels.map((level) => {
|
|
35
|
+
const effort = level === "off" ? "none" : level;
|
|
36
|
+
return [level, efforts.has(effort) ? effort : null];
|
|
37
|
+
}));
|
|
38
|
+
const normalize = (value) => thinkingLevel(value === "none" ? "off" : value);
|
|
39
|
+
const configured = normalize(configuredEffort);
|
|
40
|
+
const fallback = normalize(model.defaultReasoningEffort);
|
|
41
|
+
const defaultThinkingLevel = [configured, fallback].find((level) => level && thinkingLevelMap[level] != null);
|
|
42
|
+
return {
|
|
43
|
+
reasoning: thinkingLevels.some((level) => level !== "off" && thinkingLevelMap[level] != null),
|
|
44
|
+
thinkingLevelMap,
|
|
45
|
+
...(defaultThinkingLevel ? { defaultThinkingLevel } : {}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
2
48
|
/** Codex's built-in catalog is not authoritative for an arbitrary custom provider. */
|
|
3
49
|
export function codexModelCatalog(configResponse, entries) {
|
|
4
50
|
const config = record(configResponse.config);
|
|
@@ -11,9 +57,11 @@ export function codexModelCatalog(configResponse, entries) {
|
|
|
11
57
|
const id = typeof model.model === "string" ? model.model : typeof model.id === "string" ? model.id : null;
|
|
12
58
|
if (!id || model.hidden)
|
|
13
59
|
return [];
|
|
14
|
-
|
|
60
|
+
// A configured model replaces the catalog's own default.
|
|
61
|
+
const isDefault = configuredModel ? id === configuredModel : model.isDefault === true;
|
|
62
|
+
return [{ harness: "codex", provider, id, name: typeof model.displayName === "string" ? model.displayName : id, ...(isDefault ? { isDefault } : {}), ...codexThinking(model, config.model_reasoning_effort) }];
|
|
15
63
|
});
|
|
16
64
|
if (configuredModel && !models.some((model) => model.id === configuredModel))
|
|
17
|
-
models.unshift({ harness: "codex", provider, id: configuredModel, name: configuredModel });
|
|
65
|
+
models.unshift({ harness: "codex", provider, id: configuredModel, name: configuredModel, isDefault: true });
|
|
18
66
|
return models;
|
|
19
67
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { imageForPi } from "../harness.js";
|
|
2
2
|
import { JsonRpcProcess, record, RpcProcessClosedError } from "../json-rpc.js";
|
|
3
|
+
import { describeError } from "../../network-error.js";
|
|
3
4
|
import { ProcessCleanupUncertainError } from "../process-group.js";
|
|
4
5
|
import { serializeDiagnosticError } from "../diagnostics.js";
|
|
5
6
|
import { openPrivateServer } from "./codex-link.js";
|
|
@@ -164,7 +165,7 @@ async function runPi(executor, turn, context) {
|
|
|
164
165
|
await closeHost(host, null).catch(() => undefined);
|
|
165
166
|
throw error;
|
|
166
167
|
}
|
|
167
|
-
message = { ...message, stopReason: signal.aborted ? "aborted" : "error", errorMessage: signal.aborted ? null :
|
|
168
|
+
message = { ...message, stopReason: signal.aborted ? "aborted" : "error", errorMessage: signal.aborted ? null : describeError(error) };
|
|
168
169
|
}
|
|
169
170
|
return { session, resume: prepared.resume, message, host };
|
|
170
171
|
}
|
|
@@ -293,7 +294,7 @@ async function runCodex(executor, turn, context) {
|
|
|
293
294
|
escalation = setTimeout(() => void host?.close().catch(() => undefined), ABORT_ESCALATION_MS);
|
|
294
295
|
};
|
|
295
296
|
signal.addEventListener("abort", abort, { once: true });
|
|
296
|
-
codex.request("turn/start", { threadId, clientUserMessageId: input.turnId, input: content, ...(input.thinkingLevel ? { effort: input.thinkingLevel } : {}) })
|
|
297
|
+
codex.request("turn/start", { threadId, clientUserMessageId: input.turnId, input: content, ...(input.thinkingLevel ? { effort: input.thinkingLevel === "off" ? "none" : input.thinkingLevel } : {}) })
|
|
297
298
|
.then((result) => { nativeTurnId ??= text(record(result.turn).id) || null; if (signal.aborted)
|
|
298
299
|
interrupt(); })
|
|
299
300
|
.catch(finish);
|
|
@@ -306,7 +307,7 @@ async function runCodex(executor, turn, context) {
|
|
|
306
307
|
await closeHost(host, null).catch(() => undefined);
|
|
307
308
|
throw error;
|
|
308
309
|
}
|
|
309
|
-
message = { ...message, stopReason: signal.aborted ? "aborted" : "error", errorMessage: signal.aborted ? null :
|
|
310
|
+
message = { ...message, stopReason: signal.aborted ? "aborted" : "error", errorMessage: signal.aborted ? null : describeError(error) };
|
|
310
311
|
}
|
|
311
312
|
return { session, resume: prepared.resume, message, host };
|
|
312
313
|
}
|
|
@@ -1,22 +1,26 @@
|
|
|
1
|
+
import { type LookupFunction } from "node:net";
|
|
1
2
|
export declare const REMOTE_IMAGE_MAX_BYTES: number;
|
|
2
3
|
export declare const REMOTE_IMAGE_TIMEOUT_MS = 15000;
|
|
3
4
|
type ResolvedAddress = {
|
|
4
5
|
address: string;
|
|
5
6
|
family: 4 | 6;
|
|
6
7
|
};
|
|
8
|
+
type PinnedAddresses = readonly [ResolvedAddress, ...ResolvedAddress[]];
|
|
7
9
|
type Lookup = (hostname: string) => Promise<readonly ResolvedAddress[]>;
|
|
8
10
|
type RemoteResponse = {
|
|
9
11
|
status: number;
|
|
10
12
|
headers: Headers;
|
|
11
13
|
bytes: Uint8Array;
|
|
12
14
|
};
|
|
13
|
-
type Requester = (url: URL,
|
|
15
|
+
type Requester = (url: URL, addresses: PinnedAddresses, timeoutMs: number, maxBytes: number) => Promise<RemoteResponse>;
|
|
14
16
|
export type RemoteImageDownloadOptions = {
|
|
15
17
|
lookup?: Lookup;
|
|
16
18
|
requester?: Requester;
|
|
17
19
|
maxBytes?: number;
|
|
18
20
|
timeoutMs?: number;
|
|
19
21
|
};
|
|
22
|
+
/** Async like `dns.lookup`: a synchronous answer crashes TLS on an immediate connect failure. */
|
|
23
|
+
export declare function pinnedLookup(addresses: PinnedAddresses): LookupFunction;
|
|
20
24
|
export declare function downloadPublicImage(input: string, options?: RemoteImageDownloadOptions): Promise<{
|
|
21
25
|
bytes: Uint8Array;
|
|
22
26
|
mimeType: string;
|
|
@@ -34,15 +34,22 @@ function responseHeaders(input) {
|
|
|
34
34
|
}
|
|
35
35
|
return headers;
|
|
36
36
|
}
|
|
37
|
-
|
|
37
|
+
/** Async like `dns.lookup`: a synchronous answer crashes TLS on an immediate connect failure. */
|
|
38
|
+
export function pinnedLookup(addresses) {
|
|
39
|
+
const [first] = addresses;
|
|
40
|
+
return (_hostname, options, callback) => {
|
|
41
|
+
process.nextTick(() => options.all
|
|
42
|
+
? callback(null, [...addresses])
|
|
43
|
+
: callback(null, first.address, first.family));
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function requestPinned(url, addresses, timeoutMs, maxBytes) {
|
|
38
47
|
return new Promise((resolve, reject) => {
|
|
39
48
|
const client = url.protocol === "https:" ? https : http;
|
|
40
49
|
const request = client.request(url, {
|
|
41
50
|
agent: false,
|
|
42
51
|
headers: { Accept: "image/avif,image/webp,image/png,image/jpeg,image/gif" },
|
|
43
|
-
lookup: (
|
|
44
|
-
callback(null, address.address, address.family);
|
|
45
|
-
},
|
|
52
|
+
lookup: pinnedLookup(addresses),
|
|
46
53
|
}, (response) => {
|
|
47
54
|
const status = response.statusCode ?? 0;
|
|
48
55
|
const headers = responseHeaders(response.headers);
|
|
@@ -116,11 +123,12 @@ async function resolvePublicUrl(value, lookup, deadline) {
|
|
|
116
123
|
const addresses = isIP(hostname)
|
|
117
124
|
? [{ address: hostname, family: isIP(hostname) }]
|
|
118
125
|
: await withDeadline(lookup(hostname), deadline);
|
|
119
|
-
|
|
126
|
+
const [first, ...rest] = addresses;
|
|
127
|
+
if (!first ||
|
|
120
128
|
addresses.some((entry) => !isPublicBoardRemoteAddress(entry.address))) {
|
|
121
129
|
throw new Error("Image URL resolves to a private address");
|
|
122
130
|
}
|
|
123
|
-
return { url,
|
|
131
|
+
return { url, addresses: [first, ...rest] };
|
|
124
132
|
}
|
|
125
133
|
export async function downloadPublicImage(input, options = {}) {
|
|
126
134
|
const lookup = options.lookup ?? defaultLookup;
|
|
@@ -129,8 +137,8 @@ export async function downloadPublicImage(input, options = {}) {
|
|
|
129
137
|
const deadline = Date.now() + (options.timeoutMs ?? REMOTE_IMAGE_TIMEOUT_MS);
|
|
130
138
|
let current = input;
|
|
131
139
|
for (let redirect = 0; redirect <= MAX_REDIRECTS; redirect += 1) {
|
|
132
|
-
const { url,
|
|
133
|
-
const response = await withDeadline(requester(url,
|
|
140
|
+
const { url, addresses } = await resolvePublicUrl(current, lookup, deadline);
|
|
141
|
+
const response = await withDeadline(requester(url, addresses, remainingMs(deadline), maxBytes), deadline);
|
|
134
142
|
if (REDIRECT_STATUSES.has(response.status)) {
|
|
135
143
|
const location = response.headers.get("location");
|
|
136
144
|
if (!location)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { SessionRecord } from "@neta-art/cohub";
|
|
2
|
+
import type { Column } from "./output.js";
|
|
3
|
+
type SessionStatusFields = Pick<SessionRecord, "activeTurn" | "lastTurnIssue">;
|
|
4
|
+
export declare function sessionStatusText(session: Partial<SessionStatusFields>): string;
|
|
5
|
+
export declare const SESSION_STATUS_COLUMN: Column;
|
|
6
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function sessionStatusText(session) {
|
|
2
|
+
const turn = session.activeTurn;
|
|
3
|
+
if (turn)
|
|
4
|
+
return turn.status === "abort_requested" ? "stopping" : turn.status;
|
|
5
|
+
const issue = session.lastTurnIssue;
|
|
6
|
+
if (issue?.status === "failed")
|
|
7
|
+
return "failed";
|
|
8
|
+
return issue?.reason === "stale_active_recovered" ? "interrupted" : "";
|
|
9
|
+
}
|
|
10
|
+
export const SESSION_STATUS_COLUMN = {
|
|
11
|
+
key: "activeTurn",
|
|
12
|
+
label: "Status",
|
|
13
|
+
format: (_value, row) => sessionStatusText(row),
|
|
14
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@neta-art/cohub-cli",
|
|
3
|
-
"version": "9.1.
|
|
3
|
+
"version": "9.1.2",
|
|
4
4
|
"description": "CLI for Cohub — spaces, sessions, and agent collaboration.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
"NOTICE"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
+
"@neta-art/cohub": "10.0.0",
|
|
21
22
|
"@neta-art/generation": "^0.1.30",
|
|
22
23
|
"commander": "^15.0.0",
|
|
23
24
|
"sharp": "^0.35.4",
|
|
24
|
-
"ws": "^8.21.3"
|
|
25
|
-
"@neta-art/cohub": "9.0.0"
|
|
25
|
+
"ws": "^8.21.3"
|
|
26
26
|
},
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@fontsource/geist": "^5.3.0",
|
|
32
32
|
"@fontsource/geist-mono": "^5.3.0",
|
|
33
|
-
"@types/node": "
|
|
33
|
+
"@types/node": "26.6.4",
|
|
34
34
|
"@types/ws": "^8.18.1",
|
|
35
35
|
"pixi.js": "^8.20.1",
|
|
36
36
|
"tsdown": "^0.22.14",
|