cozyclay 2.0.2 → 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.
Files changed (256) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +17 -2
  3. package/THIRD_PARTY_NOTICES.md +8 -0
  4. package/bin/agent/agent-routes.mjs +211 -36
  5. package/bin/agent/agent-runner.mjs +98 -4
  6. package/bin/agent/codex-client.mjs +2 -1
  7. package/bin/agent/fal-contract.mjs +0 -17
  8. package/bin/agent/h3-preservation.mjs +6 -8
  9. package/bin/agent/provider-keys.mjs +1 -1
  10. package/bin/agent/providers.mjs +124 -3
  11. package/bin/agent/script-tool.mjs +74 -0
  12. package/bin/agent/session-store.mjs +5 -5
  13. package/bin/agent/studio-prompt.mjs +2 -1
  14. package/bin/agent/studio-rebase.mjs +314 -0
  15. package/bin/agent/studio-tools.mjs +21 -14
  16. package/bin/agent/supervisor.mjs +311 -0
  17. package/bin/agent/video-adapters.mjs +0 -7
  18. package/bin/codex-auth.mjs +4 -4
  19. package/bin/live/cli.mjs +2 -3
  20. package/bin/live/client.mjs +1 -1
  21. package/bin/live/studio.mjs +5 -2
  22. package/bin/mcp-runtime.mjs +2 -2
  23. package/bin/telemetry-state.mjs +3 -3
  24. package/dist/app/index.html +6 -6
  25. package/dist/assets/analytics-B41FmPYI.js +1 -0
  26. package/dist/assets/app-Do-Og5Pu.css +1 -0
  27. package/dist/assets/app-DwP-tl2x.js +4920 -0
  28. package/dist/assets/{demo-ByeYp-dY.js → demo-Dt0qZfS7.js} +1 -1
  29. package/dist/assets/{first-shot-handoff-DgT28ZiV.js → first-shot-handoff-BlT33CGf.js} +1 -1
  30. package/dist/assets/{landing-BaXkpFip.js → landing-BSesrCpL.js} +1 -1
  31. package/dist/assets/scene-asset-sync-BCWDJB1d.js +19 -0
  32. package/dist/assets/{shot-prompt-CbqWcWYi.css → scene-asset-sync-DjJ_cJ2R.css} +1 -1
  33. package/dist/assets/{ticket-BcmsnHn5.js → ticket-jqMhqL88.js} +1 -1
  34. package/dist/assets/workflow-Br-K7Srs.js +179 -0
  35. package/dist/cozyclay-package.json +1 -1
  36. package/dist/index.html +328 -222
  37. package/dist/sitemap.xml +1 -1
  38. package/dist/workflow/index.html +4 -4
  39. package/mcp/LIVE-PROTOCOL.md +23 -2
  40. package/mcp/README.md +37 -2
  41. package/mcp/ardy-prompts.mjs +1 -1
  42. package/mcp/image-file.mjs +132 -0
  43. package/mcp/live-hub.mjs +2 -2
  44. package/mcp/package.json +1 -0
  45. package/mcp/runtime/package-lock.json +7 -0
  46. package/mcp/runtime/package.json +1 -0
  47. package/mcp/server.mjs +0 -6
  48. package/mcp/tool-handlers.mjs +289 -87
  49. package/package.json +11 -2
  50. package/src/App.jsx +789 -263
  51. package/src/agent-image-client.js +94 -0
  52. package/src/analytics.js +47 -3
  53. package/src/app-stage.jsx +129 -2745
  54. package/src/ardy/auto-physics.js +6 -16
  55. package/src/ardy/collision-blockers.js +3 -7
  56. package/src/ardy/fix-collisions.js +5 -35
  57. package/src/ardy/ik-drag.js +86 -5
  58. package/src/ardy/ik-key-json.js +3 -1
  59. package/src/ardy/ik.js +15 -13
  60. package/src/ardy/motion-calibration.js +1 -1
  61. package/src/ardy/npz.js +0 -8
  62. package/src/ardy/physics-panel.jsx +3 -2
  63. package/src/ardy/physics-review.js +3 -2
  64. package/src/ardy/physics-support.js +1 -1
  65. package/src/ardy/physics-surface.js +1 -1
  66. package/src/ardy/platform-fit-panel.jsx +2 -1
  67. package/src/ardy/playback.js +1 -5
  68. package/src/ardy/range-pin.js +2 -2
  69. package/src/ardy/root-drop.js +0 -1
  70. package/src/ardy/timeline.css +184 -6
  71. package/src/ardy/timeline.jsx +113 -28
  72. package/src/ardy/to-cskel27.js +0 -2
  73. package/src/ardy/waypoints.js +0 -3
  74. package/src/ardy-bridge-contract.js +281 -0
  75. package/src/asset-pane.jsx +19 -9
  76. package/src/auto-color.js +1 -1
  77. package/src/burn-in.js +1 -1
  78. package/src/camera-block.js +1 -1
  79. package/src/camera-follow.js +75 -32
  80. package/src/camera-move.js +0 -6
  81. package/src/camera-tutorial.jsx +6 -7
  82. package/src/character-kind.js +79 -0
  83. package/src/command-bus.js +76 -17
  84. package/src/commands/cast.js +31 -7
  85. package/src/commands/elements/object.js +1 -1
  86. package/src/commands/elements/shot.js +1 -1
  87. package/src/commands/elements.js +1 -1
  88. package/src/commands/export.js +3 -3
  89. package/src/commands/motion.js +2 -2
  90. package/src/commands/objects.js +105 -10
  91. package/src/commands/project.js +15 -2
  92. package/src/commands/sculpt-images.js +53 -0
  93. package/src/commands/shot.js +88 -10
  94. package/src/commands/view.js +10 -4
  95. package/src/coplanar-depth.js +216 -0
  96. package/src/cuts.js +7 -6
  97. package/src/domains/cast.js +60 -43
  98. package/src/domains/motion/motion-intent-codec.js +50 -0
  99. package/src/domains/motion/motion-range-pin-rules.js +23 -0
  100. package/src/domains/motion/motion-video-capture.js +664 -0
  101. package/src/domains/motion.js +253 -742
  102. package/src/domains/objects.js +106 -5
  103. package/src/domains/scenes.js +79 -28
  104. package/src/domains/shots.js +33 -7
  105. package/src/error-boundary.jsx +1 -1
  106. package/src/execution-telemetry.js +5 -5
  107. package/src/facing-marks.js +14 -1
  108. package/src/first-success-guide.jsx +35 -4
  109. package/src/gizmo-claim.js +3 -1
  110. package/src/grid-floor.jsx +1 -4
  111. package/src/hierarchy-model.js +48 -35
  112. package/src/hierarchy-panel.css +1 -0
  113. package/src/hierarchy-panel.jsx +54 -102
  114. package/src/history.js +0 -2
  115. package/src/i2v-motion-client.js +1 -4
  116. package/src/ime.js +6 -0
  117. package/src/keyframe-pack.js +15 -6
  118. package/src/line-edit.js +4 -4
  119. package/src/live-control.js +6 -6
  120. package/src/matte-editor.js +2 -8
  121. package/src/matte.js +0 -19
  122. package/src/mesh-graph-clone.js +16 -2
  123. package/src/mesh-sniff.js +0 -1
  124. package/src/monotone-cubic.js +40 -0
  125. package/src/motion-store.js +4 -4
  126. package/src/motion-trail.js +5 -11
  127. package/src/multimodel-ingest.js +1 -3
  128. package/src/object-catalog.jsx +7 -5
  129. package/src/object-gizmo.jsx +45 -31
  130. package/src/object-path.js +310 -67
  131. package/src/object-travel.js +224 -0
  132. package/src/otio.js +8 -3
  133. package/src/panels/CharacterTransformPanel.jsx +24 -6
  134. package/src/panels/ObjectTransformPanel.jsx +4 -3
  135. package/src/panels/PoseImagePanel.jsx +205 -0
  136. package/src/panels/PromptBlocksPanel.jsx +3 -1
  137. package/src/panels/ProxyFigurePanel.jsx +56 -0
  138. package/src/panels/ReferenceImageField.jsx +1 -1
  139. package/src/panels/RigControlPanel.jsx +24 -3
  140. package/src/panels/RigPanel.jsx +2 -1
  141. package/src/panels/SculptPanel.jsx +405 -0
  142. package/src/panels/SubjectBox.jsx +1 -1
  143. package/src/panels/SubjectsPanel.jsx +0 -2
  144. package/src/panels/VideoCapturePanel.jsx +2 -1
  145. package/src/panels/pose-image.css +42 -0
  146. package/src/panels/pose.css +3 -0
  147. package/src/panels/sculpt.css +162 -0
  148. package/src/plan-diagram.js +184 -0
  149. package/src/planview.jsx +127 -80
  150. package/src/playground.js +3 -5
  151. package/src/pose-fade.js +77 -0
  152. package/src/pose-gizmo.css +23 -0
  153. package/src/pose-gizmo.js +96 -0
  154. package/src/posestudio.jsx +134 -83
  155. package/src/project-browser.css +53 -0
  156. package/src/project-browser.jsx +49 -4
  157. package/src/project.js +55 -24
  158. package/src/props.jsx +177 -36
  159. package/src/proxy-figure.jsx +69 -0
  160. package/src/range-pin-panel.jsx +0 -1
  161. package/src/receipt-findings.js +20 -0
  162. package/src/render-passes.js +0 -1
  163. package/src/resource-status.jsx +1 -1
  164. package/src/root-path.js +48 -0
  165. package/src/route-owner.js +52 -0
  166. package/src/scene-asset-cache.js +1 -2
  167. package/src/scene-assets.js +12 -4
  168. package/src/scene-fbx.js +1 -1
  169. package/src/scene-mesh-cache.js +0 -1
  170. package/src/scene-objects.js +319 -19
  171. package/src/scenes.js +58 -9
  172. package/src/sculpt-blueprint.js +65 -0
  173. package/src/sculpt-compare.js +384 -0
  174. package/src/sculpt-geometry.js +141 -0
  175. package/src/sculpt-mode.js +168 -0
  176. package/src/sculpt-recipe.js +385 -0
  177. package/src/sculpt-run.js +351 -0
  178. package/src/sculpt-stage.jsx +221 -0
  179. package/src/settings-menu.jsx +1 -1
  180. package/src/shell/BoardSlot.jsx +341 -0
  181. package/src/shell/BottomDock.jsx +58 -14
  182. package/src/shell/DetailsSlot.jsx +26 -6
  183. package/src/shell/MenuBar.jsx +78 -6
  184. package/src/shell/OutlinerSlot.jsx +3 -1
  185. package/src/shell/TopBar.jsx +17 -3
  186. package/src/shell/ViewportToolbar.jsx +18 -3
  187. package/src/shell/dock.css +239 -0
  188. package/src/shell/glass-regions.css +3 -2
  189. package/src/shell/log-store.js +4 -4
  190. package/src/shell/mode.css +20 -0
  191. package/src/shell/studio-shell-context.js +4 -1
  192. package/src/shot-authoring.js +69 -6
  193. package/src/stable-items.js +1 -1
  194. package/src/stage-attach-frames.js +169 -0
  195. package/src/stage-canvas-rigs.jsx +402 -0
  196. package/src/stage-capture.jsx +216 -0
  197. package/src/stage-characters.jsx +302 -0
  198. package/src/stage-editor-gizmos.jsx +1229 -0
  199. package/src/stage-labels-ko.js +204 -0
  200. package/src/stage-startup-defaults.js +182 -0
  201. package/src/starter-scenes.js +1 -1
  202. package/src/storyboard.js +24 -5
  203. package/src/studio-actions.js +42 -9
  204. package/src/studio-agent-commands.js +186 -23
  205. package/src/studio-agent-context.js +11 -9
  206. package/src/studio-agent-motion.js +2 -1
  207. package/src/studio-agent-protocol.js +56 -23
  208. package/src/studio-app-binding.js +52 -23
  209. package/src/studio-contact-sheet.js +3 -3
  210. package/src/studio-elements.js +11 -2
  211. package/src/studio-geometry-facts.js +114 -0
  212. package/src/styles.css +92 -9
  213. package/src/ui-scrub.js +4 -4
  214. package/src/ui.jsx +6 -4
  215. package/src/use-asset-shelf.js +60 -0
  216. package/src/use-camera-tutorial.js +50 -0
  217. package/src/use-embed-bridge.js +12 -0
  218. package/src/use-first-success-prompt.js +7 -0
  219. package/src/use-i2v-capture-modal.js +15 -0
  220. package/src/use-sculpt-mode.js +95 -0
  221. package/src/workflow/AgentPanel.jsx +106 -18
  222. package/src/workflow/WorkflowBuilder.jsx +1 -1
  223. package/src/workflow/agent-client.js +113 -45
  224. package/src/workflow/agent-panel.css +35 -0
  225. package/src/workflow/attachment-image.js +1 -1
  226. package/src/workflow/canvas-commands.js +3 -3
  227. package/src/workflow/cozy-scene-node.js +3 -3
  228. package/src/workflow/local-workflow.js +2 -2
  229. package/src/workflow/motion-input.js +3 -3
  230. package/src/workflow/node-schema.js +1 -1
  231. package/src/workflow/scene-asset-sync.js +2 -9
  232. package/src/workflow/shot-prompt-node.js +1 -1
  233. package/src/workflow/video-contract.js +1 -1
  234. package/src/workflow/workflow-resources.js +0 -8
  235. package/src/workflow-send.js +87 -0
  236. package/tools/ardy/__pycache__/cclay_gvhmr_worker.cpython-313.pyc +0 -0
  237. package/tools/check-eslint.mjs +57 -0
  238. package/tools/check-knip.mjs +60 -0
  239. package/tools/check-loc.mjs +54 -0
  240. package/tools/demo-worker/api-client.mjs +7 -13
  241. package/tools/demo-worker/generate.mjs +2 -4
  242. package/tools/eslint-baseline.json +23 -0
  243. package/tools/kimodo/effector-constraints.mjs +4 -4
  244. package/tools/kimodo/generate.mjs +1 -1
  245. package/tools/knip-baseline.json +331 -0
  246. package/tools/loc-baseline.json +126 -0
  247. package/tools/process-supervisor.mjs +1 -1
  248. package/tools/projflow/replay.mjs +4 -4
  249. package/tools/projflow/runner.mjs +3 -3
  250. package/tools/qa/studio-control-count.mjs +2 -0
  251. package/tools/run-tests.mjs +94 -7
  252. package/dist/assets/analytics-MVZOEW8O.js +0 -1
  253. package/dist/assets/app-BxBLC6GT.js +0 -4876
  254. package/dist/assets/app-CJdxvnrl.css +0 -1
  255. package/dist/assets/shot-prompt-hN0j_O-p.js +0 -17
  256. package/dist/assets/workflow-B5XZwVfe.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
+ }
@@ -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
- export class DemoApiError extends Error {
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
- export class DemoApiClient {
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
- export function next(options) {
211
+ function next(options) {
216
212
  return envClient().next(options);
217
213
  }
218
214
 
219
- export function complete(jobId, leaseToken, npzBuffer, options) {
215
+ function complete(jobId, leaseToken, npzBuffer, options) {
220
216
  return envClient().complete(jobId, leaseToken, npzBuffer, options);
221
217
  }
222
218
 
223
- export function fail(jobId, leaseToken, reason, options) {
219
+ function fail(jobId, leaseToken, reason, options) {
224
220
  return envClient().fail(jobId, leaseToken, reason, options);
225
221
  }
226
222
 
227
- export function heartbeat(jobId, leaseToken, options) {
223
+ function heartbeat(jobId, leaseToken, options) {
228
224
  return envClient().heartbeat(jobId, leaseToken, options);
229
225
  }
230
226
 
231
- export function signRequest(secretOrOptions, maybeOptions) {
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
- export const JOB_HARD_TIMEOUT_MS = POLICY.JOB_HARD_TIMEOUT_MS;
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
- export async function generate(prompt, durationS, options = {}) {
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
- export const LEFT_HAND_TYPE = "left-hand";
103
- export const RIGHT_HAND_TYPE = "right-hand";
104
- export const LEFT_FOOT_TYPE = "left-foot";
105
- export const RIGHT_FOOT_TYPE = "right-foot";
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
- export const DEFAULT_MODEL = "Kimodo-SOMA-RP-v1.1";
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 },