cozyclay 2.0.0 → 2.1.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/CHANGELOG.md +112 -0
- package/README.md +17 -2
- package/THIRD_PARTY_NOTICES.md +8 -0
- package/bin/agent/agent-routes.mjs +219 -38
- package/bin/agent/agent-runner.mjs +98 -4
- package/bin/agent/codex-client.mjs +2 -1
- package/bin/agent/fal-contract.mjs +0 -17
- package/bin/agent/h3-preservation.mjs +6 -8
- package/bin/agent/provider-keys.mjs +1 -1
- package/bin/agent/providers.mjs +124 -3
- package/bin/agent/script-tool.mjs +74 -0
- package/bin/agent/session-store.mjs +5 -5
- package/bin/agent/studio-prompt.mjs +2 -1
- package/bin/agent/studio-rebase.mjs +314 -0
- package/bin/agent/studio-tools.mjs +21 -14
- package/bin/agent/supervisor.mjs +311 -0
- package/bin/agent/video-adapters.mjs +0 -7
- package/bin/codex-auth.mjs +6 -5
- package/bin/cozyclay.mjs +12 -3
- package/bin/live/cli.mjs +2 -3
- package/bin/live/client.mjs +1 -1
- package/bin/live/studio.mjs +5 -2
- package/bin/mcp-runtime.mjs +12 -2
- package/bin/telemetry-state.mjs +3 -3
- package/dist/app/index.html +6 -6
- package/dist/assets/analytics-B41FmPYI.js +1 -0
- package/dist/assets/app-Do-Og5Pu.css +1 -0
- package/dist/assets/app-DwP-tl2x.js +4920 -0
- package/dist/assets/{demo-ByeYp-dY.js → demo-Dt0qZfS7.js} +1 -1
- package/dist/assets/{first-shot-handoff-DgT28ZiV.js → first-shot-handoff-BlT33CGf.js} +1 -1
- package/dist/assets/{landing-BaXkpFip.js → landing-BSesrCpL.js} +1 -1
- package/dist/assets/scene-asset-sync-BCWDJB1d.js +19 -0
- package/dist/assets/{shot-prompt-CbqWcWYi.css → scene-asset-sync-DjJ_cJ2R.css} +1 -1
- package/dist/assets/{ticket-BcmsnHn5.js → ticket-jqMhqL88.js} +1 -1
- package/dist/assets/workflow-Br-K7Srs.js +179 -0
- package/dist/cozyclay-package.json +1 -1
- package/dist/index.html +328 -222
- package/dist/sitemap.xml +5 -5
- package/dist/workflow/index.html +4 -4
- package/mcp/LIVE-PROTOCOL.md +23 -2
- package/mcp/README.md +37 -2
- package/mcp/ardy-prompts.mjs +1 -1
- package/mcp/image-file.mjs +132 -0
- package/mcp/live-hub.mjs +2 -2
- package/mcp/package.json +1 -0
- package/mcp/runtime/package-lock.json +7 -0
- package/mcp/runtime/package.json +1 -0
- package/mcp/server.mjs +0 -6
- package/mcp/tool-handlers.mjs +289 -87
- package/package.json +11 -2
- package/src/App.jsx +801 -259
- package/src/agent-image-client.js +94 -0
- package/src/analytics.js +47 -3
- package/src/app-stage.jsx +129 -2745
- package/src/ardy/auto-physics.js +6 -16
- package/src/ardy/collision-blockers.js +3 -7
- package/src/ardy/fix-collisions.js +5 -35
- package/src/ardy/ik-drag.js +86 -5
- package/src/ardy/ik-key-json.js +3 -1
- package/src/ardy/ik.js +15 -13
- package/src/ardy/motion-calibration.js +1 -1
- package/src/ardy/npz.js +0 -8
- package/src/ardy/physics-panel.jsx +3 -2
- package/src/ardy/physics-review.js +3 -2
- package/src/ardy/physics-support.js +1 -1
- package/src/ardy/physics-surface.js +1 -1
- package/src/ardy/platform-fit-panel.jsx +2 -1
- package/src/ardy/playback.js +1 -5
- package/src/ardy/range-pin.js +2 -2
- package/src/ardy/root-drop.js +0 -1
- package/src/ardy/timeline.css +196 -3
- package/src/ardy/timeline.jsx +117 -29
- package/src/ardy/to-cskel27.js +0 -2
- package/src/ardy/waypoints.js +1 -4
- package/src/ardy-bridge-contract.js +281 -0
- package/src/asset-pane.jsx +19 -9
- package/src/auto-color.js +1 -1
- package/src/burn-in.js +1 -1
- package/src/camera-block.js +1 -1
- package/src/camera-follow.js +75 -32
- package/src/camera-move.js +0 -6
- package/src/camera-tutorial.jsx +43 -24
- package/src/character-kind.js +79 -0
- package/src/command-bus.js +76 -17
- package/src/commands/cast.js +31 -7
- package/src/commands/elements/object.js +1 -1
- package/src/commands/elements/shot.js +1 -1
- package/src/commands/elements.js +1 -1
- package/src/commands/export.js +3 -3
- package/src/commands/motion.js +2 -2
- package/src/commands/objects.js +105 -10
- package/src/commands/project.js +16 -3
- package/src/commands/sculpt-images.js +53 -0
- package/src/commands/shot.js +88 -10
- package/src/commands/view.js +10 -4
- package/src/coplanar-depth.js +216 -0
- package/src/cuts.js +7 -6
- package/src/domains/cast.js +60 -43
- package/src/domains/motion/motion-intent-codec.js +50 -0
- package/src/domains/motion/motion-range-pin-rules.js +23 -0
- package/src/domains/motion/motion-video-capture.js +664 -0
- package/src/domains/motion.js +253 -742
- package/src/domains/objects.js +106 -5
- package/src/domains/scenes.js +89 -32
- package/src/domains/shots.js +33 -7
- package/src/error-boundary.jsx +1 -1
- package/src/execution-telemetry.js +5 -5
- package/src/facing-marks.js +14 -1
- package/src/first-success-guide.jsx +35 -4
- package/src/gizmo-claim.js +3 -1
- package/src/grid-floor.jsx +1 -4
- package/src/hierarchy-model.js +48 -35
- package/src/hierarchy-panel.css +1 -0
- package/src/hierarchy-panel.jsx +54 -102
- package/src/history.js +0 -2
- package/src/i2v-motion-client.js +1 -4
- package/src/ime.js +6 -0
- package/src/keyframe-pack.js +15 -6
- package/src/line-edit.js +4 -4
- package/src/live-control.js +6 -6
- package/src/matte-editor.js +2 -8
- package/src/matte.js +0 -19
- package/src/mesh-graph-clone.js +16 -2
- package/src/mesh-sniff.js +0 -1
- package/src/monotone-cubic.js +40 -0
- package/src/motion/generation.js +1 -1
- package/src/motion-store.js +4 -4
- package/src/motion-trail.js +5 -11
- package/src/multimodel-ingest.js +1 -3
- package/src/object-catalog.jsx +7 -5
- package/src/object-gizmo.jsx +45 -31
- package/src/object-path.js +310 -67
- package/src/object-travel.js +224 -0
- package/src/otio.js +8 -3
- package/src/panels/CharacterTransformPanel.jsx +24 -6
- package/src/panels/ObjectTransformPanel.jsx +4 -3
- package/src/panels/PoseImagePanel.jsx +205 -0
- package/src/panels/PromptBlocksPanel.jsx +3 -1
- package/src/panels/ProxyFigurePanel.jsx +56 -0
- package/src/panels/ReferenceImageField.jsx +1 -1
- package/src/panels/RigControlPanel.jsx +24 -3
- package/src/panels/RigPanel.jsx +2 -1
- package/src/panels/SculptPanel.jsx +405 -0
- package/src/panels/SubjectBox.jsx +1 -1
- package/src/panels/SubjectsPanel.jsx +0 -2
- package/src/panels/VideoCapturePanel.jsx +2 -1
- package/src/panels/pose-image.css +42 -0
- package/src/panels/pose.css +3 -0
- package/src/panels/sculpt.css +162 -0
- package/src/plan-diagram.js +184 -0
- package/src/planview.jsx +127 -80
- package/src/playground.js +3 -5
- package/src/pose-fade.js +77 -0
- package/src/pose-gizmo.css +23 -0
- package/src/pose-gizmo.js +96 -0
- package/src/posestudio.jsx +134 -83
- package/src/project-browser.css +53 -0
- package/src/project-browser.jsx +52 -3
- package/src/project.js +55 -24
- package/src/props.jsx +177 -36
- package/src/proxy-figure.jsx +69 -0
- package/src/range-pin-panel.jsx +0 -1
- package/src/receipt-findings.js +20 -0
- package/src/render-passes.js +0 -1
- package/src/resource-status.jsx +1 -1
- package/src/root-path.js +48 -0
- package/src/route-owner.js +52 -0
- package/src/scene-asset-cache.js +1 -2
- package/src/scene-assets.js +12 -4
- package/src/scene-fbx.js +1 -1
- package/src/scene-mesh-cache.js +0 -1
- package/src/scene-objects.js +319 -19
- package/src/scenes.js +62 -17
- package/src/sculpt-blueprint.js +65 -0
- package/src/sculpt-compare.js +384 -0
- package/src/sculpt-geometry.js +141 -0
- package/src/sculpt-mode.js +168 -0
- package/src/sculpt-recipe.js +385 -0
- package/src/sculpt-run.js +351 -0
- package/src/sculpt-stage.jsx +221 -0
- package/src/settings-menu.jsx +1 -1
- package/src/shell/BoardSlot.jsx +341 -0
- package/src/shell/BottomDock.jsx +58 -14
- package/src/shell/DetailsSlot.jsx +26 -6
- package/src/shell/MenuBar.jsx +78 -6
- package/src/shell/OutlinerSlot.jsx +3 -1
- package/src/shell/TopBar.jsx +17 -3
- package/src/shell/ViewportToolbar.jsx +18 -3
- package/src/shell/dock.css +239 -0
- package/src/shell/glass-regions.css +3 -2
- package/src/shell/log-store.js +4 -4
- package/src/shell/mode.css +20 -0
- package/src/shell/studio-shell-context.js +4 -1
- package/src/shot-authoring.js +69 -6
- package/src/stable-items.js +1 -1
- package/src/stage-attach-frames.js +169 -0
- package/src/stage-canvas-rigs.jsx +402 -0
- package/src/stage-capture.jsx +216 -0
- package/src/stage-characters.jsx +302 -0
- package/src/stage-editor-gizmos.jsx +1229 -0
- package/src/stage-labels-ko.js +204 -0
- package/src/stage-startup-defaults.js +182 -0
- package/src/starter-scenes.js +1 -1
- package/src/storyboard.js +24 -5
- package/src/studio-actions.js +42 -9
- package/src/studio-agent-commands.js +186 -23
- package/src/studio-agent-context.js +11 -9
- package/src/studio-agent-motion.js +2 -1
- package/src/studio-agent-protocol.js +56 -23
- package/src/studio-app-binding.js +52 -23
- package/src/studio-contact-sheet.js +3 -3
- package/src/studio-elements.js +11 -2
- package/src/studio-geometry-facts.js +114 -0
- package/src/styles.css +111 -10
- package/src/theme.js +1 -1
- package/src/timeline-fps.js +13 -0
- package/src/ui-scrub.js +4 -4
- package/src/ui.jsx +6 -4
- package/src/use-asset-shelf.js +60 -0
- package/src/use-camera-tutorial.js +50 -0
- package/src/use-embed-bridge.js +12 -0
- package/src/use-first-success-prompt.js +7 -0
- package/src/use-i2v-capture-modal.js +15 -0
- package/src/use-sculpt-mode.js +95 -0
- package/src/workflow/AgentPanel.jsx +110 -20
- package/src/workflow/WorkflowBuilder.jsx +1 -1
- package/src/workflow/agent-client.js +113 -45
- package/src/workflow/agent-panel.css +35 -0
- package/src/workflow/attachment-image.js +1 -1
- package/src/workflow/canvas-commands.js +3 -3
- package/src/workflow/cozy-scene-node.js +3 -3
- package/src/workflow/local-workflow.js +2 -2
- package/src/workflow/motion-input.js +3 -3
- package/src/workflow/node-schema.js +1 -1
- package/src/workflow/scene-asset-sync.js +2 -9
- package/src/workflow/shot-prompt-node.js +1 -1
- package/src/workflow/video-contract.js +1 -1
- package/src/workflow/workflow-resources.js +0 -8
- package/src/workflow-send.js +87 -0
- package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
- package/tools/check-eslint.mjs +57 -0
- package/tools/check-knip.mjs +60 -0
- package/tools/check-loc.mjs +54 -0
- package/tools/demo-worker/api-client.mjs +7 -13
- package/tools/demo-worker/generate.mjs +2 -4
- package/tools/eslint-baseline.json +23 -0
- package/tools/kimodo/effector-constraints.mjs +4 -4
- package/tools/kimodo/generate.mjs +1 -1
- package/tools/knip-baseline.json +331 -0
- package/tools/loc-baseline.json +126 -0
- package/tools/process-supervisor.mjs +1 -1
- package/tools/projflow/replay.mjs +4 -4
- package/tools/projflow/runner.mjs +3 -3
- package/tools/qa/studio-control-count.mjs +2 -0
- package/tools/run-tests.mjs +96 -7
- package/dist/assets/analytics-MVZOEW8O.js +0 -1
- package/dist/assets/app-C1sdVbg4.css +0 -1
- package/dist/assets/app-CA55tcI3.js +0 -4876
- package/dist/assets/shot-prompt-Yrt99wZx.js +0 -17
- package/dist/assets/workflow-dNPGY9xz.js +0 -179
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Send a storyboard panel to the Workflow canvas: one Upload node holding the
|
|
3
|
+
* panel PNG, wired into a fresh Video generation node whose prompt is the
|
|
4
|
+
* shot prompt plus the panel caption. The helper only appends to the draft
|
|
5
|
+
* (`cozyclay.workflow.v1`); it never runs the workflow and never touches an
|
|
6
|
+
* existing node or edge. Kept free of React so Node tests can drive it.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { loadWorkflowGraph, normalizeWorkflowGraph, WORKFLOW_STORAGE_KEY, WORKFLOW_VERSION } from "./project.js";
|
|
10
|
+
import { pastedImageNodeData } from "./workflow/clipboard-image.js";
|
|
11
|
+
import { DEFAULT_NODE_SCHEMAS, defaultFormValues, schemaProperties } from "./workflow/node-schema.js";
|
|
12
|
+
import { buildShotPrompt } from "./shot-prompt.js";
|
|
13
|
+
|
|
14
|
+
const COLUMN_GAP = 380;
|
|
15
|
+
const ROW_GAP = 280;
|
|
16
|
+
const FIRST_POSITION = { x: 80, y: 100 };
|
|
17
|
+
const VIDEO_MODEL = { id: "video-generation", name: "Video generation" };
|
|
18
|
+
|
|
19
|
+
/** The video prompt for a panel: the labelled shot block, then its caption. */
|
|
20
|
+
export function panelVideoPrompt(meta, caption = "") {
|
|
21
|
+
const prompt = buildShotPrompt(meta, { target: "video" });
|
|
22
|
+
const note = typeof caption === "string" ? caption.trim() : "";
|
|
23
|
+
return note ? `${prompt}\nCAPTION: ${note}` : prompt;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function nodeBase(label) {
|
|
27
|
+
return { label, cost: 0, outputHistory: [], outputs: [], resultUrl: null, isLoading: false, errorMsg: null };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function freshId(type, taken, stamp) {
|
|
31
|
+
let id = `${type}-${stamp}`;
|
|
32
|
+
for (let n = 2; taken.has(id); n += 1) id = `${type}-${stamp}-${n}`;
|
|
33
|
+
taken.add(id);
|
|
34
|
+
return id;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Pure: the draft with the panel's two nodes and one edge appended. Every
|
|
38
|
+
* pre-existing node and edge is carried over untouched. */
|
|
39
|
+
export function appendPanelToWorkflowGraph(draft, { dataUrl, mimeType = "image/png", fileName = "panel.png", prompt = "", now = Date.now() } = {}) {
|
|
40
|
+
if (typeof dataUrl !== "string" || !dataUrl.startsWith("data:image/")) throw new Error("A panel image is required");
|
|
41
|
+
const graph = normalizeWorkflowGraph(draft);
|
|
42
|
+
const taken = new Set(graph.nodes.map((node) => node.id));
|
|
43
|
+
const uploadId = freshId("upload", taken, now);
|
|
44
|
+
const videoId = freshId("video", taken, now);
|
|
45
|
+
// Below everything already on the canvas, left-aligned with it.
|
|
46
|
+
const x = graph.nodes.length ? Math.min(...graph.nodes.map((node) => node.position.x)) : FIRST_POSITION.x;
|
|
47
|
+
const y = graph.nodes.length ? Math.max(...graph.nodes.map((node) => node.position.y)) + ROW_GAP : FIRST_POSITION.y;
|
|
48
|
+
const upload = {
|
|
49
|
+
id: uploadId,
|
|
50
|
+
type: "upload",
|
|
51
|
+
position: { x, y },
|
|
52
|
+
data: { ...nodeBase("Upload"), ...pastedImageNodeData({ name: fileName, type: mimeType }, dataUrl), uploadName: fileName, thumbnail: dataUrl },
|
|
53
|
+
};
|
|
54
|
+
const formValues = { ...defaultFormValues(schemaProperties(DEFAULT_NODE_SCHEMAS, "video", VIDEO_MODEL.id)), prompt };
|
|
55
|
+
const video = {
|
|
56
|
+
id: videoId,
|
|
57
|
+
type: "video",
|
|
58
|
+
position: { x: x + COLUMN_GAP, y },
|
|
59
|
+
data: { ...nodeBase("Video"), prompt, model: VIDEO_MODEL.id, selectedModel: { ...VIDEO_MODEL }, formValues },
|
|
60
|
+
};
|
|
61
|
+
const edge = { id: `e-${uploadId}-${videoId}`, source: uploadId, target: videoId, sourceHandle: "output", targetHandle: "input" };
|
|
62
|
+
return normalizeWorkflowGraph({
|
|
63
|
+
version: WORKFLOW_VERSION,
|
|
64
|
+
nodes: [...graph.nodes, upload, video],
|
|
65
|
+
edges: [...graph.edges, edge],
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Write the panel into the stored draft. Unlike storeWorkflowGraph this lets a
|
|
70
|
+
* storage failure (quota, private mode) surface, so the caller can say so. */
|
|
71
|
+
export function sendPanelToWorkflow(panel, storage = globalThis.localStorage, now = Date.now()) {
|
|
72
|
+
if (!storage) throw new Error("Browser storage is unavailable");
|
|
73
|
+
// A corrupt stored draft throws here, before anything is written.
|
|
74
|
+
const draft = loadWorkflowGraph(storage, { strict: true });
|
|
75
|
+
const graph = appendPanelToWorkflowGraph(draft, { ...panel, now });
|
|
76
|
+
storage.setItem(WORKFLOW_STORAGE_KEY, JSON.stringify(graph));
|
|
77
|
+
try { globalThis.dispatchEvent?.(new CustomEvent("cozyclay:workflow-change")); } catch { /* non-browser runtime */ }
|
|
78
|
+
return graph;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** A Blob's bytes as a data URL, for a stylized panel read from the asset store. */
|
|
82
|
+
export function bytesToDataUrl(bytes, type = "image/png") {
|
|
83
|
+
const view = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
|
|
84
|
+
let binary = "";
|
|
85
|
+
for (let i = 0; i < view.length; i += 0x8000) binary += String.fromCharCode.apply(null, view.subarray(i, i + 0x8000));
|
|
86
|
+
return `data:${type};base64,${btoa(binary)}`;
|
|
87
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// ESLint ratchet: fail when the error count of any rule exceeds tools/eslint-baseline.json.
|
|
2
|
+
// Usage: node tools/check-eslint.mjs [--update]
|
|
3
|
+
// (no flag) compare current counts with the baseline; exit 1 on any regression
|
|
4
|
+
// --update rewrite the baseline with the current counts
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
7
|
+
import { dirname, join, relative } from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
|
|
10
|
+
const root = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
11
|
+
const baselinePath = join(root, "tools", "eslint-baseline.json");
|
|
12
|
+
|
|
13
|
+
const run = spawnSync(join(root, "node_modules", ".bin", "eslint"), [".", "--format", "json"], {
|
|
14
|
+
cwd: root,
|
|
15
|
+
encoding: "utf8",
|
|
16
|
+
maxBuffer: 512 * 1024 * 1024,
|
|
17
|
+
});
|
|
18
|
+
// eslint exits 1 when it finds errors; anything else without JSON output is a real failure.
|
|
19
|
+
let results;
|
|
20
|
+
try {
|
|
21
|
+
results = JSON.parse(run.stdout);
|
|
22
|
+
} catch {
|
|
23
|
+
console.error(`eslint did not produce JSON (status ${run.status})\n${run.stderr}`);
|
|
24
|
+
process.exit(2);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const counts = {};
|
|
28
|
+
const hits = {};
|
|
29
|
+
for (const file of results) {
|
|
30
|
+
for (const m of file.messages) {
|
|
31
|
+
if (m.severity !== 2) continue;
|
|
32
|
+
const rule = m.ruleId ?? "fatal";
|
|
33
|
+
counts[rule] = (counts[rule] ?? 0) + 1;
|
|
34
|
+
(hits[rule] ??= []).push(`${relative(root, file.filePath)}:${m.line}: ${m.message}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const sorted = Object.fromEntries(Object.entries(counts).sort(([a], [b]) => a.localeCompare(b)));
|
|
38
|
+
|
|
39
|
+
if (process.argv.includes("--update")) {
|
|
40
|
+
writeFileSync(baselinePath, `${JSON.stringify(sorted, null, "\t")}\n`);
|
|
41
|
+
console.log(`eslint: baseline updated (${Object.keys(sorted).length} rules)`);
|
|
42
|
+
process.exit(0);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const baseline = JSON.parse(readFileSync(baselinePath, "utf8"));
|
|
46
|
+
const regressed = Object.keys(sorted).filter((rule) => sorted[rule] > (baseline[rule] ?? 0));
|
|
47
|
+
if (regressed.length) {
|
|
48
|
+
for (const rule of regressed) {
|
|
49
|
+
console.log(`${rule}: ${baseline[rule] ?? 0} -> ${sorted[rule]}`);
|
|
50
|
+
}
|
|
51
|
+
console.log("");
|
|
52
|
+
for (const line of regressed.flatMap((rule) => hits[rule]).slice(0, 10)) console.log(line);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
const total = Object.values(sorted).reduce((a, b) => a + b, 0);
|
|
56
|
+
const base = Object.values(baseline).reduce((a, b) => a + b, 0);
|
|
57
|
+
console.log(`eslint: ${total} errors across ${Object.keys(sorted).length} rules (baseline ${base}), 0 regressions`);
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Usage: node tools/check-knip.mjs [--update]
|
|
3
|
+
// Ratchet for knip: fails (exit 1) on findings that are not in tools/knip-baseline.json.
|
|
4
|
+
// --update rewrites the baseline with the current findings and exits 0.
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
7
|
+
import { dirname, join } from 'node:path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
|
|
10
|
+
const root = join(dirname(fileURLToPath(import.meta.url)), '..');
|
|
11
|
+
const baselinePath = join(root, 'tools', 'knip-baseline.json');
|
|
12
|
+
|
|
13
|
+
// knip exits 1 when it finds issues; only a missing/unparseable report is a failure.
|
|
14
|
+
const run = spawnSync(join(root, 'node_modules', '.bin', 'knip'), ['--reporter', 'json', '--no-progress'], {
|
|
15
|
+
cwd: root,
|
|
16
|
+
encoding: 'utf8',
|
|
17
|
+
maxBuffer: 256 * 1024 * 1024,
|
|
18
|
+
});
|
|
19
|
+
if (run.error) {
|
|
20
|
+
console.error(`knip failed to start: ${run.error.message}`);
|
|
21
|
+
process.exit(2);
|
|
22
|
+
}
|
|
23
|
+
let report;
|
|
24
|
+
try {
|
|
25
|
+
report = JSON.parse(run.stdout);
|
|
26
|
+
} catch {
|
|
27
|
+
console.error(`knip produced no JSON (exit ${run.status})\n${run.stderr}`);
|
|
28
|
+
process.exit(2);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const keys = new Set();
|
|
32
|
+
for (const issue of report.issues ?? []) {
|
|
33
|
+
const file = issue.file;
|
|
34
|
+
if (issue.files === true || (Array.isArray(issue.files) && issue.files.length)) keys.add(`file ${file}`);
|
|
35
|
+
for (const e of issue.exports ?? []) keys.add(`export ${file}#${e.name}`);
|
|
36
|
+
for (const e of issue.types ?? []) keys.add(`export ${file}#${e.name}`);
|
|
37
|
+
for (const e of issue.enumMembers ?? []) keys.add(`export ${file}#${e.name}`);
|
|
38
|
+
for (const e of issue.dependencies ?? []) keys.add(`dependency ${file}#${e.name}`);
|
|
39
|
+
for (const e of issue.devDependencies ?? []) keys.add(`dependency ${file}#${e.name}`);
|
|
40
|
+
for (const e of issue.unlisted ?? []) keys.add(`unlisted ${file}#${e.name}`);
|
|
41
|
+
}
|
|
42
|
+
for (const f of report.files ?? []) keys.add(`file ${f}`);
|
|
43
|
+
const current = [...keys].sort();
|
|
44
|
+
|
|
45
|
+
if (process.argv.includes('--update')) {
|
|
46
|
+
writeFileSync(baselinePath, `${JSON.stringify(current, null, '\t')}\n`);
|
|
47
|
+
console.log(`knip: baseline updated, ${current.length} findings`);
|
|
48
|
+
process.exit(0);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const baseline = existsSync(baselinePath) ? JSON.parse(readFileSync(baselinePath, 'utf8')) : [];
|
|
52
|
+
const known = new Set(baseline);
|
|
53
|
+
const now = new Set(current);
|
|
54
|
+
const added = current.filter((k) => !known.has(k));
|
|
55
|
+
const fixed = baseline.filter((k) => !now.has(k));
|
|
56
|
+
|
|
57
|
+
for (const k of added) console.log(`new: ${k}`);
|
|
58
|
+
if (fixed.length) console.log(`fixed: ${fixed.length}`);
|
|
59
|
+
console.log(`knip: ${current.length} findings, ${added.length} new, ${fixed.length} fixed (baseline ${baseline.length})`);
|
|
60
|
+
process.exit(added.length ? 1 : 0);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Usage: node tools/check-loc.mjs [--update]
|
|
3
|
+
// Ratchet pure source LOC; --update rewrites tools/loc-baseline.json.
|
|
4
|
+
import { readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
|
|
8
|
+
const root = join(fileURLToPath(new URL('.', import.meta.url)), '..');
|
|
9
|
+
const baselinePath = join(root, 'tools', 'loc-baseline.json');
|
|
10
|
+
const extensions = new Set(['.js', '.jsx', '.mjs']);
|
|
11
|
+
const roots = ['src', 'bin', 'mcp', 'tools', 'workers'];
|
|
12
|
+
|
|
13
|
+
function filesUnder(directory) {
|
|
14
|
+
const files = [];
|
|
15
|
+
for (const entry of readdirSync(join(root, directory), { withFileTypes: true })) {
|
|
16
|
+
if (entry.name === 'node_modules' || entry.name === 'dist') continue;
|
|
17
|
+
const path = join(directory, entry.name);
|
|
18
|
+
if (entry.isDirectory()) files.push(...filesUnder(path));
|
|
19
|
+
else if (entry.isFile() && extensions.has(path.slice(path.lastIndexOf('.')))) files.push(path);
|
|
20
|
+
}
|
|
21
|
+
return files;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function pureLoc(path) {
|
|
25
|
+
return readFileSync(join(root, path), 'utf8')
|
|
26
|
+
.split(/\r?\n/)
|
|
27
|
+
.filter((line) => line.trim() && !/^\s*\/\//.test(line)).length;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const current = Object.fromEntries(
|
|
31
|
+
roots.flatMap(filesUnder)
|
|
32
|
+
.map((path) => [path.replaceAll('\\', '/'), pureLoc(path)])
|
|
33
|
+
.filter(([, lines]) => lines > 250)
|
|
34
|
+
.sort(([a], [b]) => a.localeCompare(b)),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
if (process.argv.includes('--update')) {
|
|
38
|
+
writeFileSync(baselinePath, `${JSON.stringify(current, null, '\t')}\n`);
|
|
39
|
+
console.log(`loc: baseline updated (${Object.keys(current).length} files over 250)`);
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const baseline = JSON.parse(readFileSync(baselinePath, 'utf8'));
|
|
44
|
+
const violations = [];
|
|
45
|
+
for (const [path, lines] of Object.entries(current)) {
|
|
46
|
+
if (!(path in baseline)) violations.push(`new oversized file ${path} (${lines} lines)`);
|
|
47
|
+
else if (lines > baseline[path]) violations.push(`${path} grew ${baseline[path]} -> ${lines}`);
|
|
48
|
+
}
|
|
49
|
+
for (const message of violations) console.log(message);
|
|
50
|
+
|
|
51
|
+
const fixed = Object.keys(baseline).filter((path) => !(path in current));
|
|
52
|
+
if (fixed.length) console.log(`fixed: ${fixed.length}`);
|
|
53
|
+
console.log(`loc: ${Object.keys(current).length} files over 250 (baseline ${Object.keys(baseline).length}), ${violations.length} violations`);
|
|
54
|
+
process.exit(violations.length ? 1 : 0);
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import { bodyBytes, randomNonce, signedHeaders } from "./sign-core.mjs";
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
class DemoApiError extends Error {
|
|
12
12
|
constructor(status, code, body = null) {
|
|
13
13
|
super(`demo API ${status}${code ? ` ${code}` : ""}`);
|
|
14
14
|
this.name = "DemoApiError";
|
|
@@ -77,7 +77,7 @@ function asJob(body) {
|
|
|
77
77
|
* `fetchImpl`, `now`, and `nonceFactory` are injectable to make protocol tests
|
|
78
78
|
* deterministic without changing production behavior.
|
|
79
79
|
*/
|
|
80
|
-
|
|
80
|
+
class DemoApiClient {
|
|
81
81
|
constructor({
|
|
82
82
|
baseUrl = process.env.CC_DEMO_API_BASE,
|
|
83
83
|
base,
|
|
@@ -198,42 +198,36 @@ export class DemoApiClient {
|
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
-
export const ApiClient = DemoApiClient;
|
|
202
|
-
|
|
203
201
|
export function createApiClient(options = {}) {
|
|
204
202
|
return new DemoApiClient(options);
|
|
205
203
|
}
|
|
206
204
|
|
|
207
|
-
export const createDemoApiClient = createApiClient;
|
|
208
|
-
|
|
209
205
|
let defaultClient = null;
|
|
210
206
|
function envClient() {
|
|
211
207
|
if (!defaultClient) defaultClient = createApiClient();
|
|
212
208
|
return defaultClient;
|
|
213
209
|
}
|
|
214
210
|
|
|
215
|
-
|
|
211
|
+
function next(options) {
|
|
216
212
|
return envClient().next(options);
|
|
217
213
|
}
|
|
218
214
|
|
|
219
|
-
|
|
215
|
+
function complete(jobId, leaseToken, npzBuffer, options) {
|
|
220
216
|
return envClient().complete(jobId, leaseToken, npzBuffer, options);
|
|
221
217
|
}
|
|
222
218
|
|
|
223
|
-
|
|
219
|
+
function fail(jobId, leaseToken, reason, options) {
|
|
224
220
|
return envClient().fail(jobId, leaseToken, reason, options);
|
|
225
221
|
}
|
|
226
222
|
|
|
227
|
-
|
|
223
|
+
function heartbeat(jobId, leaseToken, options) {
|
|
228
224
|
return envClient().heartbeat(jobId, leaseToken, options);
|
|
229
225
|
}
|
|
230
226
|
|
|
231
|
-
|
|
227
|
+
function signRequest(secretOrOptions, maybeOptions) {
|
|
232
228
|
if (typeof secretOrOptions === "string") {
|
|
233
229
|
return signedHeaders({ ...(maybeOptions || {}), secret: secretOrOptions });
|
|
234
230
|
}
|
|
235
231
|
return signedHeaders(secretOrOptions);
|
|
236
232
|
}
|
|
237
233
|
|
|
238
|
-
export default createApiClient;
|
|
239
|
-
|
|
@@ -16,7 +16,7 @@ import { createLocalRunner } from "../ardy/runners/local.mjs";
|
|
|
16
16
|
import { killGroup, track } from "../ardy/runners/proc.mjs";
|
|
17
17
|
import { POLICY } from "../../workers/api/src/policy.js";
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
const JOB_HARD_TIMEOUT_MS = POLICY.JOB_HARD_TIMEOUT_MS;
|
|
20
20
|
const DEFAULT_TIMEOUT_ENV = ["CC_JOB_HARD_TIMEOUT_MS", "JOB_HARD_TIMEOUT_MS"];
|
|
21
21
|
let sharedRunner = null;
|
|
22
22
|
|
|
@@ -122,7 +122,7 @@ function waitForChild(child) {
|
|
|
122
122
|
* installing ARDY. Production callers leave it unset and get
|
|
123
123
|
* createLocalRunner().
|
|
124
124
|
*/
|
|
125
|
-
|
|
125
|
+
async function generate(prompt, durationS, options = {}) {
|
|
126
126
|
// Accept both generate(prompt, duration, options) and generate({prompt,
|
|
127
127
|
// duration}, options). The latter is convenient for queue adapters and does
|
|
128
128
|
// not change the public positional contract.
|
|
@@ -298,6 +298,4 @@ export function createGenerator({ runner = null, workerReady = localWorkerReady,
|
|
|
298
298
|
};
|
|
299
299
|
}
|
|
300
300
|
|
|
301
|
-
export const generateJob = generate;
|
|
302
|
-
export default generate;
|
|
303
301
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"no-async-promise-executor": 1,
|
|
3
|
+
"no-case-declarations": 1,
|
|
4
|
+
"no-constant-binary-expression": 2,
|
|
5
|
+
"no-control-regex": 5,
|
|
6
|
+
"no-dupe-keys": 1,
|
|
7
|
+
"no-loss-of-precision": 1,
|
|
8
|
+
"no-regex-spaces": 2,
|
|
9
|
+
"no-undef": 25,
|
|
10
|
+
"no-unsafe-finally": 5,
|
|
11
|
+
"no-unused-vars": 168,
|
|
12
|
+
"no-useless-assignment": 21,
|
|
13
|
+
"no-useless-catch": 2,
|
|
14
|
+
"no-useless-escape": 7,
|
|
15
|
+
"preserve-caught-error": 16,
|
|
16
|
+
"react-hooks/immutability": 66,
|
|
17
|
+
"react-hooks/purity": 5,
|
|
18
|
+
"react-hooks/refs": 87,
|
|
19
|
+
"react-hooks/rules-of-hooks": 3,
|
|
20
|
+
"react-hooks/set-state-in-effect": 31,
|
|
21
|
+
"react-hooks/use-memo": 1,
|
|
22
|
+
"require-yield": 2
|
|
23
|
+
}
|
|
@@ -99,10 +99,10 @@
|
|
|
99
99
|
|
|
100
100
|
import { buildFullBodyConstraints } from "./pose-constraints.mjs";
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
const LEFT_HAND_TYPE = "left-hand";
|
|
103
|
+
const RIGHT_HAND_TYPE = "right-hand";
|
|
104
|
+
const LEFT_FOOT_TYPE = "left-foot";
|
|
105
|
+
const RIGHT_FOOT_TYPE = "right-foot";
|
|
106
106
|
|
|
107
107
|
/**
|
|
108
108
|
* IK/mid-joint track id -> Kimodo effector constraint type.
|
|
@@ -50,7 +50,7 @@ const SSH_OPTS = [
|
|
|
50
50
|
"-o", "ServerAliveCountMax=240",
|
|
51
51
|
];
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
const DEFAULT_MODEL = "Kimodo-SOMA-RP-v1.1";
|
|
54
54
|
export const KIMODO_BACKENDS = {
|
|
55
55
|
"nvidia-cuda": { repo: "$HOME/.cozyclay/kimodo", entry: ".venv/bin/kimodo_gen", mode: "cuda", promptFlag: null, durationFlag: "--duration", stepsFlag: "--diffusion_steps", outputFlag: "--output" },
|
|
56
56
|
"kimodo-mlx": { repo: "$HOME/.cozyclay/kimodo-mlx", entry: "$HOME/.cozyclay/kimodo-mlx-venv/bin/python", motion: "$HOME/.cozyclay/kimodo-mlx/models/nvidia-soma-rp-v1.1", text: "$HOME/.cozyclay/kimodo-mlx/models/llm2vec-text-bundle", mode: "mlx", promptFlag: "--prompt", framesFlag: "--frames", stepsFlag: "--steps", outputFlag: null },
|