@neta-art/cohub-cli 9.1.1 → 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/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 +40 -30
- package/dist/commands/me.js +2 -0
- package/dist/commands/spaces.js +3 -0
- 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 +1 -1
- package/dist/session-status.d.ts +6 -0
- package/dist/session-status.js +14 -0
- package/package.json +2 -2
package/dist/board-export.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import type { BoardDocument, BoardExportRegion, BoardSceneItem } from "@neta-art/cohub/board";
|
|
2
|
-
import type {
|
|
3
|
-
export declare function resolveBundledFonts():
|
|
2
|
+
import type { BoardNodeExportFormat, BoardNodeFont, BoardNodeRenderer, BoardNodeTexture } from "@neta-art/cohub/board/export/node";
|
|
3
|
+
export declare function resolveBundledFonts(): BoardNodeFont[];
|
|
4
4
|
export type BoardExportSource = {
|
|
5
5
|
document: BoardDocument;
|
|
6
6
|
boardId: string;
|
|
7
7
|
title: string | null;
|
|
8
8
|
};
|
|
9
9
|
export declare function loadBoardDocument(spaceId: string, target: string): Promise<BoardExportSource>;
|
|
10
|
-
export declare function loadBoardTextures(
|
|
10
|
+
export declare function loadBoardTextures(renderer: BoardNodeRenderer, spaceId: string, items: readonly BoardSceneItem[], options?: {
|
|
11
11
|
concurrency?: number;
|
|
12
12
|
}): Promise<{
|
|
13
|
-
textures: Map<string,
|
|
13
|
+
textures: Map<string, BoardNodeTexture>;
|
|
14
14
|
failed: string[];
|
|
15
15
|
omitted: string[];
|
|
16
16
|
}>;
|
|
@@ -24,7 +24,7 @@ export type BoardExportRunOptions = {
|
|
|
24
24
|
padding?: number;
|
|
25
25
|
colorScheme: "dark" | "light";
|
|
26
26
|
background: "paper" | "transparent";
|
|
27
|
-
format:
|
|
27
|
+
format: BoardNodeExportFormat;
|
|
28
28
|
quality?: number;
|
|
29
29
|
withImages: boolean;
|
|
30
30
|
};
|
|
@@ -35,7 +35,7 @@ export type BoardExportFrame = {
|
|
|
35
35
|
height: number;
|
|
36
36
|
scale: number;
|
|
37
37
|
itemCount: number;
|
|
38
|
-
format:
|
|
38
|
+
format: BoardNodeExportFormat;
|
|
39
39
|
};
|
|
40
40
|
export type BoardExportRunResult = {
|
|
41
41
|
frames: BoardExportFrame[];
|
package/dist/board-export.js
CHANGED
|
@@ -7,7 +7,7 @@ import { dirname, join } from "node:path";
|
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { tmpdir } from "node:os";
|
|
9
9
|
const execFile = promisify(execFileCallback);
|
|
10
|
-
import { boardDocumentAt, boardImageKeySource, buildBoardScene,
|
|
10
|
+
import { boardDocumentAt, boardImageKeySource, buildBoardScene, createNodeBoardRenderer, createNodeBoardSketchHost, exportBoardImageBytes, imageAssetKey, parseBoardDocument, planBoardExport, selectBoardExportAssets, } from "./board-kernel.js";
|
|
11
11
|
import { resolveBoardId } from "./board-command-support.js";
|
|
12
12
|
import { BOARD_EXPORT_FONTS } from "./board-fonts.js";
|
|
13
13
|
import { createClient } from "./client.js";
|
|
@@ -45,7 +45,7 @@ export async function loadBoardDocument(spaceId, target) {
|
|
|
45
45
|
throw new Error(`Board ${result.id} could not be read: ${parsed.diagnostics[0]?.path}: ${parsed.diagnostics[0]?.message}`);
|
|
46
46
|
return { document: parsed.document, boardId: result.id, title: result.title };
|
|
47
47
|
}
|
|
48
|
-
export async function loadBoardTextures(
|
|
48
|
+
export async function loadBoardTextures(renderer, spaceId, items, options = {}) {
|
|
49
49
|
const selection = selectBoardExportAssets([...items], imageAssetKey);
|
|
50
50
|
const textures = new Map();
|
|
51
51
|
const failed = [];
|
|
@@ -66,7 +66,7 @@ export async function loadBoardTextures(headless, spaceId, items, options = {})
|
|
|
66
66
|
const { bytes, mimeType } = source.kind === "file"
|
|
67
67
|
? await readSpaceFileBytes(client, spaceId, source.value)
|
|
68
68
|
: await downloadPublicImage(source.value);
|
|
69
|
-
const texture = await
|
|
69
|
+
const texture = await renderer.decodeImage(bytes, mimeType);
|
|
70
70
|
textures.set(key, texture);
|
|
71
71
|
}
|
|
72
72
|
catch {
|
|
@@ -161,7 +161,7 @@ export async function runBoardExport(options) {
|
|
|
161
161
|
});
|
|
162
162
|
if (!plan)
|
|
163
163
|
return null;
|
|
164
|
-
const
|
|
164
|
+
const renderer = await createNodeBoardRenderer({ fonts: resolveBundledFonts() });
|
|
165
165
|
let sketchHost = null;
|
|
166
166
|
try {
|
|
167
167
|
const warnings = [];
|
|
@@ -169,7 +169,7 @@ export async function runBoardExport(options) {
|
|
|
169
169
|
let backgroundTexture;
|
|
170
170
|
let omittedKeys = new Set();
|
|
171
171
|
if (options.withImages) {
|
|
172
|
-
const loaded = await loadBoardTextures(
|
|
172
|
+
const loaded = await loadBoardTextures(renderer, options.spaceId, plan.items);
|
|
173
173
|
textures = loaded.textures;
|
|
174
174
|
omittedKeys = new Set(loaded.omitted);
|
|
175
175
|
if (loaded.failed.length > 0) {
|
|
@@ -186,14 +186,14 @@ export async function runBoardExport(options) {
|
|
|
186
186
|
declaredBackground.imageUrl) {
|
|
187
187
|
try {
|
|
188
188
|
const { bytes, mimeType } = await downloadPublicImage(declaredBackground.imageUrl);
|
|
189
|
-
backgroundTexture = await
|
|
189
|
+
backgroundTexture = await renderer.decodeImage(bytes, mimeType);
|
|
190
190
|
}
|
|
191
191
|
catch {
|
|
192
192
|
warnings.push("The board background image could not be loaded; the fallback color was exported.");
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
const videoCount = plan.items.filter((item) => item.type === "video").length;
|
|
196
|
-
sketchHost =
|
|
196
|
+
sketchHost = createNodeBoardSketchHost(renderer, {
|
|
197
197
|
readModule: async (src) => {
|
|
198
198
|
const { bytes } = await readSpaceFileBytes(createClient(), options.spaceId, src);
|
|
199
199
|
return new TextDecoder().decode(bytes);
|
|
@@ -202,7 +202,7 @@ export async function runBoardExport(options) {
|
|
|
202
202
|
const region = times.length > 1 ? { kind: "rect", rect: plan.world } : options.region;
|
|
203
203
|
const host = sketchHost;
|
|
204
204
|
if (!host)
|
|
205
|
-
throw new Error("
|
|
205
|
+
throw new Error("Node sketch host could not be created.");
|
|
206
206
|
const sketchVariants = new Map();
|
|
207
207
|
for (const time of times) {
|
|
208
208
|
const sketchPlan = planBoardExport({
|
|
@@ -220,7 +220,7 @@ export async function runBoardExport(options) {
|
|
|
220
220
|
}
|
|
221
221
|
await Promise.all([...sketchVariants.values()].map((variant) => host.prepare([variant.item], variant.times, plan.scale)));
|
|
222
222
|
if (videoCount > 0) {
|
|
223
|
-
warnings.push(`${videoCount} video preview${videoCount === 1 ? " was" : "s were"} drawn as placeholders;
|
|
223
|
+
warnings.push(`${videoCount} video preview${videoCount === 1 ? " was" : "s were"} drawn as placeholders; video decoding is unavailable in Node.`);
|
|
224
224
|
}
|
|
225
225
|
const backgroundImage = backgroundTexture
|
|
226
226
|
? { texture: backgroundTexture, fit: declaredBackground.fit ?? "cover", position: "center", opacity: declaredBackground.opacity ?? 1 }
|
|
@@ -234,7 +234,7 @@ export async function runBoardExport(options) {
|
|
|
234
234
|
const frames = [];
|
|
235
235
|
const reported = new Set();
|
|
236
236
|
for (const time of times) {
|
|
237
|
-
const result = exportBoardImageBytes(
|
|
237
|
+
const result = exportBoardImageBytes(renderer, document, {
|
|
238
238
|
...(time === null ? {} : { at: { ...(options.animation ? { animation: options.animation } : {}), time } }),
|
|
239
239
|
region,
|
|
240
240
|
scale: options.scale,
|
|
@@ -272,7 +272,7 @@ export async function runBoardExport(options) {
|
|
|
272
272
|
}
|
|
273
273
|
finally {
|
|
274
274
|
sketchHost?.destroy();
|
|
275
|
-
|
|
275
|
+
renderer.destroy();
|
|
276
276
|
}
|
|
277
277
|
}
|
|
278
278
|
function describeWarning(warning) {
|
package/dist/board-kernel.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { boardDocumentAt, boardImageKeySource, buildBoardScene, imageAssetKey, parseBoardDocument, planBoardExport, selectBoardExportAssets, } from "@neta-art/cohub/board";
|
|
2
|
-
export {
|
|
2
|
+
export { createNodeBoardRenderer, createNodeBoardSketchHost, exportBoardImageBytes } from "@neta-art/cohub/board/export/node";
|
package/dist/board-kernel.js
CHANGED
|
@@ -7119,7 +7119,7 @@ function parseBoardDocument(input) {
|
|
|
7119
7119
|
} : result;
|
|
7120
7120
|
}
|
|
7121
7121
|
//#endregion
|
|
7122
|
-
//#region ../sdk/src/board/animation.ts
|
|
7122
|
+
//#region ../sdk/src/board/model/animation.ts
|
|
7123
7123
|
const linear = (x) => x;
|
|
7124
7124
|
function cubicBezier(x1, y1, x2, y2) {
|
|
7125
7125
|
const cx = 3 * x1;
|
|
@@ -7519,7 +7519,7 @@ function boardDocumentAt(document, animationId, time, options = {}) {
|
|
|
7519
7519
|
};
|
|
7520
7520
|
}
|
|
7521
7521
|
//#endregion
|
|
7522
|
-
//#region ../sdk/src/board/geometry.ts
|
|
7522
|
+
//#region ../sdk/src/board/model/geometry.ts
|
|
7523
7523
|
function worldPoint(x, y) {
|
|
7524
7524
|
return {
|
|
7525
7525
|
x,
|
|
@@ -7862,7 +7862,7 @@ const { PI: e } = Math;
|
|
|
7862
7862
|
e + 1e-4;
|
|
7863
7863
|
const { min: a } = Math;
|
|
7864
7864
|
//#endregion
|
|
7865
|
-
//#region ../sdk/src/board/
|
|
7865
|
+
//#region ../sdk/src/board/model/draw-geometry.ts
|
|
7866
7866
|
const sampleRadius = drawSampleRadius;
|
|
7867
7867
|
const computeDrawBounds = drawPointsBounds;
|
|
7868
7868
|
function trimDrawPoints(points, trim) {
|
|
@@ -8038,7 +8038,7 @@ function selectBoardExportAssets(items, assetKey, maxTextures = 64) {
|
|
|
8038
8038
|
};
|
|
8039
8039
|
}
|
|
8040
8040
|
//#endregion
|
|
8041
|
-
//#region ../sdk/src/board/
|
|
8041
|
+
//#region ../sdk/src/board/model/export-plan.ts
|
|
8042
8042
|
function normalizeBoardDocument(document) {
|
|
8043
8043
|
const parsed = parseBoardDocument(document);
|
|
8044
8044
|
if (!parsed.ok) throw new Error(`Invalid Board document: ${parsed.diagnostics.map((entry) => `${entry.path}: ${entry.message}`).join("; ")}`);
|
|
@@ -8152,7 +8152,7 @@ function planBoardExport(input) {
|
|
|
8152
8152
|
};
|
|
8153
8153
|
}
|
|
8154
8154
|
//#endregion
|
|
8155
|
-
//#region ../sdk/src/board/
|
|
8155
|
+
//#region ../sdk/src/board/model/file-snapshot.ts
|
|
8156
8156
|
const DOC_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
8157
8157
|
"md",
|
|
8158
8158
|
"mdx",
|
|
@@ -8280,7 +8280,7 @@ function fileStem(path) {
|
|
|
8280
8280
|
return name.slice(0, dot);
|
|
8281
8281
|
}
|
|
8282
8282
|
//#endregion
|
|
8283
|
-
//#region ../sdk/src/board/
|
|
8283
|
+
//#region ../sdk/src/board/model/file-preview.ts
|
|
8284
8284
|
/**
|
|
8285
8285
|
* Which tier a card renders at. Derived rather than stored: presentation follows
|
|
8286
8286
|
* from the facts present, so there is no second piece of state to fall out of
|
|
@@ -8310,7 +8310,7 @@ function fileCategoryAccent(category, palette) {
|
|
|
8310
8310
|
}
|
|
8311
8311
|
}
|
|
8312
8312
|
//#endregion
|
|
8313
|
-
//#region ../sdk/src/board/
|
|
8313
|
+
//#region ../sdk/src/board/model/palette.ts
|
|
8314
8314
|
const LABEL_DARK = 16053492;
|
|
8315
8315
|
const LABEL_LIGHT = 1579035;
|
|
8316
8316
|
const BOARD_COLORS = [
|
|
@@ -8467,7 +8467,7 @@ function resolveItemColor(color, fallback, colors, mode, parseCss, part = "strok
|
|
|
8467
8467
|
return parseCss(value) ?? pickBoardColor(colors, fallback, mode)[part];
|
|
8468
8468
|
}
|
|
8469
8469
|
//#endregion
|
|
8470
|
-
//#region ../sdk/src/board/
|
|
8470
|
+
//#region ../sdk/src/board/model/scene.ts
|
|
8471
8471
|
function orderBoardItems(items) {
|
|
8472
8472
|
const children = /* @__PURE__ */ new Map();
|
|
8473
8473
|
for (const [id, item] of Object.entries(items)) {
|
|
@@ -8575,13 +8575,13 @@ function toSceneItem(id, item, layout) {
|
|
|
8575
8575
|
return scene;
|
|
8576
8576
|
}
|
|
8577
8577
|
//#endregion
|
|
8578
|
-
//#region ../sdk/src/board/
|
|
8578
|
+
//#region ../sdk/src/board/model/text-metrics.ts
|
|
8579
8579
|
function boardFontFamily(font, stacks) {
|
|
8580
8580
|
if (font === "sans" || font === "serif" || font === "mono") return stacks[font];
|
|
8581
8581
|
return `"${font.replaceAll("\"", "")}", ${stacks.sans}`;
|
|
8582
8582
|
}
|
|
8583
8583
|
//#endregion
|
|
8584
|
-
//#region ../sdk/src/board/task.ts
|
|
8584
|
+
//#region ../sdk/src/board/model/task.ts
|
|
8585
8585
|
function artifactScore(artifact) {
|
|
8586
8586
|
const kind = {
|
|
8587
8587
|
text: 1,
|
|
@@ -8617,7 +8617,7 @@ function taskArtifactPreviewUrl(artifact) {
|
|
|
8617
8617
|
if (artifact?.type === "video" || artifact?.type === "audio") return artifact.previewUrl;
|
|
8618
8618
|
}
|
|
8619
8619
|
//#endregion
|
|
8620
|
-
//#region ../sdk/src/board/image-key.ts
|
|
8620
|
+
//#region ../sdk/src/board/model/image-key.ts
|
|
8621
8621
|
function imageAssetKey(item) {
|
|
8622
8622
|
if (item.type === "image") return `file:${item.props.src}`;
|
|
8623
8623
|
if (item.type === "file") {
|
|
@@ -28481,7 +28481,7 @@ function sync(container, item, context) {
|
|
|
28481
28481
|
}
|
|
28482
28482
|
//#endregion
|
|
28483
28483
|
//#region ../sdk/src/board/render/renderers/board-renderer-registry.ts
|
|
28484
|
-
const
|
|
28484
|
+
const builtinCardRenderers = [
|
|
28485
28485
|
textCardRenderer,
|
|
28486
28486
|
imageCardRenderer,
|
|
28487
28487
|
{
|
|
@@ -28546,13 +28546,21 @@ const boardCardRenderers = [
|
|
|
28546
28546
|
arrowCardRenderer,
|
|
28547
28547
|
frameCardRenderer,
|
|
28548
28548
|
effectCardRenderer,
|
|
28549
|
-
sketchCardRenderer
|
|
28550
|
-
unknownCardRenderer
|
|
28549
|
+
sketchCardRenderer
|
|
28551
28550
|
];
|
|
28552
|
-
|
|
28553
|
-
|
|
28554
|
-
|
|
28551
|
+
/**
|
|
28552
|
+
* Pick a renderer per item: `renderers` first, then the built-ins. Items no
|
|
28553
|
+
* renderer claims draw as a labelled placeholder box.
|
|
28554
|
+
*/
|
|
28555
|
+
function createBoardCardRendererResolver(renderers = []) {
|
|
28556
|
+
const candidates = [...renderers, ...builtinCardRenderers];
|
|
28557
|
+
return (item, context) => {
|
|
28558
|
+
ensureBoardTextMeasurement();
|
|
28559
|
+
return candidates.find((renderer) => renderer.canRender(item, context)) ?? unknownCardRenderer;
|
|
28560
|
+
};
|
|
28555
28561
|
}
|
|
28562
|
+
/** The built-in renderers alone. */
|
|
28563
|
+
const getBoardCardRenderer = createBoardCardRendererResolver();
|
|
28556
28564
|
//#endregion
|
|
28557
28565
|
//#region ../sdk/src/board/export/scene.ts
|
|
28558
28566
|
function buildContext(input) {
|
|
@@ -28625,11 +28633,12 @@ function createBoardExportScene(input) {
|
|
|
28625
28633
|
});
|
|
28626
28634
|
world.scale.set(input.scale);
|
|
28627
28635
|
world.position.set(-input.world.x * input.scale, -input.world.y * input.scale);
|
|
28636
|
+
const resolve = input.renderers ?? getBoardCardRenderer;
|
|
28628
28637
|
const groups = /* @__PURE__ */ new Map();
|
|
28629
28638
|
for (const item of input.items) {
|
|
28630
28639
|
const clip = clippingAncestor(input.scene, item);
|
|
28631
28640
|
const host = clip && groups.get(clip) || world;
|
|
28632
|
-
const container =
|
|
28641
|
+
const container = resolve(item, context).create(item, context);
|
|
28633
28642
|
container.alpha = input.scene.opacity(item.id);
|
|
28634
28643
|
host.addChild(container);
|
|
28635
28644
|
if (isClippingFrame(item)) {
|
|
@@ -28686,6 +28695,7 @@ function renderBoardExport(renderer, input, options = {}) {
|
|
|
28686
28695
|
textures: options.textures,
|
|
28687
28696
|
sketches: options.sketches,
|
|
28688
28697
|
assetKey: options.assetKey,
|
|
28698
|
+
renderers: options.renderers,
|
|
28689
28699
|
background: resolveBackground(options.background, palette, document, colorScheme),
|
|
28690
28700
|
backgroundImage: options.backgroundImage
|
|
28691
28701
|
});
|
|
@@ -28721,7 +28731,7 @@ function renderBoardExport(renderer, input, options = {}) {
|
|
|
28721
28731
|
}
|
|
28722
28732
|
}
|
|
28723
28733
|
//#endregion
|
|
28724
|
-
//#region ../sdk/src/board/
|
|
28734
|
+
//#region ../sdk/src/board/export/node/sketch.ts
|
|
28725
28735
|
const MAX_EDGE = 2048;
|
|
28726
28736
|
const DEFAULT_TIMEOUT_MS = 2e3;
|
|
28727
28737
|
const DEFAULT_READ_TIMEOUT_MS = 15e3;
|
|
@@ -28755,7 +28765,7 @@ function runModule(code, src, timeoutMs) {
|
|
|
28755
28765
|
...module.exports
|
|
28756
28766
|
} : module.exports;
|
|
28757
28767
|
if (typeof value.draw !== "function") throw new Error(`${src} does not export draw(ctx, frame)`);
|
|
28758
|
-
if (value.draw.constructor?.name === "AsyncFunction") throw new Error(`${src} must export a synchronous draw(ctx, frame) for
|
|
28768
|
+
if (value.draw.constructor?.name === "AsyncFunction") throw new Error(`${src} must export a synchronous draw(ctx, frame) for Node export.`);
|
|
28759
28769
|
return {
|
|
28760
28770
|
module: value,
|
|
28761
28771
|
context
|
|
@@ -28780,7 +28790,7 @@ function textureFromCanvas(canvas) {
|
|
|
28780
28790
|
height: canvas.height
|
|
28781
28791
|
}) });
|
|
28782
28792
|
}
|
|
28783
|
-
function
|
|
28793
|
+
function createNodeBoardSketchHost(renderer, options) {
|
|
28784
28794
|
const textures = /* @__PURE__ */ new Map();
|
|
28785
28795
|
const errors = /* @__PURE__ */ new Map();
|
|
28786
28796
|
const lastErrors = /* @__PURE__ */ new Map();
|
|
@@ -28803,7 +28813,7 @@ function createBoardHeadlessSketchHost(headless, options) {
|
|
|
28803
28813
|
const runtime = await loadModule(item.props.src);
|
|
28804
28814
|
const width = Math.max(1, Math.round(item.frame.width * scale));
|
|
28805
28815
|
const height = Math.max(1, Math.round(item.frame.height * scale));
|
|
28806
|
-
const canvas =
|
|
28816
|
+
const canvas = renderer.canvasModule.createCanvas(width, height);
|
|
28807
28817
|
const context = canvas.getContext("2d");
|
|
28808
28818
|
context.scale(scale, scale);
|
|
28809
28819
|
const frame = {
|
|
@@ -28818,7 +28828,7 @@ function createBoardHeadlessSketchHost(headless, options) {
|
|
|
28818
28828
|
runtime.context.__boardFrame = frame;
|
|
28819
28829
|
try {
|
|
28820
28830
|
const drawResult = new Script("__boardDraw(__boardDrawContext, __boardFrame)", { filename: item.props.src }).runInContext(runtime.context, { timeout: timeoutMs });
|
|
28821
|
-
if (drawResult && typeof drawResult.then === "function") throw new Error(`${item.props.src} must export a synchronous draw(ctx, frame) for
|
|
28831
|
+
if (drawResult && typeof drawResult.then === "function") throw new Error(`${item.props.src} must export a synchronous draw(ctx, frame) for Node export.`);
|
|
28822
28832
|
} finally {
|
|
28823
28833
|
delete runtime.context.__boardDraw;
|
|
28824
28834
|
delete runtime.context.__boardDrawContext;
|
|
@@ -28864,7 +28874,7 @@ function createBoardHeadlessSketchHost(headless, options) {
|
|
|
28864
28874
|
};
|
|
28865
28875
|
}
|
|
28866
28876
|
//#endregion
|
|
28867
|
-
//#region ../sdk/src/board/
|
|
28877
|
+
//#region ../sdk/src/board/export/node/index.ts
|
|
28868
28878
|
const SYSTEM_UI_FAMILIES = [
|
|
28869
28879
|
"Noto Sans",
|
|
28870
28880
|
"DejaVu Sans",
|
|
@@ -28922,7 +28932,7 @@ function installAnimationFrameShim() {
|
|
|
28922
28932
|
globals.cancelAnimationFrame = void 0;
|
|
28923
28933
|
};
|
|
28924
28934
|
}
|
|
28925
|
-
async function
|
|
28935
|
+
async function createNodeBoardRenderer(options = {}) {
|
|
28926
28936
|
const canvasModule = options.canvasModule ?? await loadCanvasModule();
|
|
28927
28937
|
extensions.add(CanvasGraphicsPipe, GraphicsPipe, CanvasGraphicsContextSystem, GraphicsContextSystem, CanvasRendererTextSystem, CanvasTextSystem, CanvasTextPipe);
|
|
28928
28938
|
for (const font of options.fonts ?? []) canvasModule.GlobalFonts.registerFromPath(font.path, font.family ?? "Geist");
|
|
@@ -28939,14 +28949,14 @@ async function createBoardHeadlessRenderer(options = {}) {
|
|
|
28939
28949
|
getCanvasRenderingContext2D: () => createCanvas(1, 1).getContext("2d").constructor,
|
|
28940
28950
|
getWebGLRenderingContext: () => WEBGL_PROBE_STUB,
|
|
28941
28951
|
getNavigator: () => ({
|
|
28942
|
-
userAgent: "cohub-board-
|
|
28952
|
+
userAgent: "cohub-board-node",
|
|
28943
28953
|
gpu: null
|
|
28944
28954
|
}),
|
|
28945
28955
|
getBaseUrl: () => "file://",
|
|
28946
28956
|
getFontFaceSet: () => null,
|
|
28947
28957
|
fetch: (url, init) => fetch(url, init),
|
|
28948
28958
|
parseXML: () => {
|
|
28949
|
-
throw new Error("XML parsing is not available in
|
|
28959
|
+
throw new Error("XML parsing is not available in Node board export.");
|
|
28950
28960
|
}
|
|
28951
28961
|
});
|
|
28952
28962
|
installBoardTextMeasurement();
|
|
@@ -28988,9 +28998,9 @@ const MIME = {
|
|
|
28988
28998
|
jpeg: "image/jpeg",
|
|
28989
28999
|
webp: "image/webp"
|
|
28990
29000
|
};
|
|
28991
|
-
function exportBoardImageBytes(
|
|
29001
|
+
function exportBoardImageBytes(renderer, document, options = {}) {
|
|
28992
29002
|
const { format = "png", quality = .92, textures, sketches, backgroundImage, ...rest } = options;
|
|
28993
|
-
const result = renderBoardExport(
|
|
29003
|
+
const result = renderBoardExport(renderer.renderer, document, {
|
|
28994
29004
|
...rest,
|
|
28995
29005
|
...sketches ? { sketches } : {},
|
|
28996
29006
|
...textures ? { textures } : {},
|
|
@@ -29010,4 +29020,4 @@ function exportBoardImageBytes(headless, document, options = {}) {
|
|
|
29010
29020
|
};
|
|
29011
29021
|
}
|
|
29012
29022
|
//#endregion
|
|
29013
|
-
export { boardDocumentAt, boardImageKeySource, buildBoardScene,
|
|
29023
|
+
export { boardDocumentAt, boardImageKeySource, buildBoardScene, createNodeBoardRenderer, createNodeBoardSketchHost, exportBoardImageBytes, imageAssetKey, parseBoardDocument, planBoardExport, selectBoardExportAssets };
|
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" },
|
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
|
}
|
|
@@ -294,7 +294,7 @@ async function runCodex(executor, turn, context) {
|
|
|
294
294
|
escalation = setTimeout(() => void host?.close().catch(() => undefined), ABORT_ESCALATION_MS);
|
|
295
295
|
};
|
|
296
296
|
signal.addEventListener("abort", abort, { once: true });
|
|
297
|
-
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 } : {}) })
|
|
298
298
|
.then((result) => { nativeTurnId ??= text(record(result.turn).id) || null; if (signal.aborted)
|
|
299
299
|
interrupt(); })
|
|
300
300
|
.catch(finish);
|
|
@@ -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,7 +18,7 @@
|
|
|
18
18
|
"NOTICE"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@neta-art/cohub": "
|
|
21
|
+
"@neta-art/cohub": "10.0.0",
|
|
22
22
|
"@neta-art/generation": "^0.1.30",
|
|
23
23
|
"commander": "^15.0.0",
|
|
24
24
|
"sharp": "^0.35.4",
|