@field/cascade 0.10.0 → 0.12.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 (229) hide show
  1. package/AGENTS.md +25 -5
  2. package/ARCHITECTURE.md +5 -4
  3. package/CHANGELOG.md +382 -0
  4. package/README.md +1 -2
  5. package/dist/assets/AgentPanel-1JugFjE4.css +1 -0
  6. package/dist/assets/AgentPanel-BDL5MddA.js +5 -0
  7. package/dist/assets/{CodeEditor-DSZBpzdy.js → CodeEditor-C-xYG-Wn.js} +120 -120
  8. package/dist/assets/CodePanel-CtXd50m6.js +2 -0
  9. package/dist/assets/CookInfoPanel-B3NMhKJF.css +1 -0
  10. package/dist/assets/CookInfoPanel-DG4_TRsX.js +2 -0
  11. package/dist/assets/CustomNodeDialog-CHfZUs-r.js +1 -0
  12. package/dist/assets/DefinitionNode-CaRTXQ_b.js +1 -0
  13. package/dist/assets/DefinitionPanel-C3CxZY9r.js +1 -0
  14. package/dist/assets/DefinitionPanel-VNLnAiQD.css +1 -0
  15. package/dist/assets/OpenGraphDialog-BLdr6k_q.js +1 -0
  16. package/dist/assets/ProjectSettings-ZnvmIAir.js +2 -0
  17. package/dist/assets/{SettingsDialog-p25h3tj0.js → SettingsDialog-CBX3Wa87.js} +1 -1
  18. package/dist/assets/TimelinePanel-CDeR0arY.css +1 -0
  19. package/dist/assets/TimelinePanel-CWgnFH1e.js +1 -0
  20. package/dist/assets/{VersionHistoryDialog-DzFLUotB.js → VersionHistoryDialog-BOSoW1R-.js} +1 -1
  21. package/dist/assets/assert-BO2_erPD.js +1 -0
  22. package/dist/assets/assert-hvykGCnP.js +1 -0
  23. package/dist/assets/builder-BmBKcDix.js +1 -0
  24. package/dist/assets/camera-DFF7YA3F.js +1 -0
  25. package/dist/assets/checks-DAvyO9Gf.js +1 -0
  26. package/dist/assets/contactSheet-N1NcaWxw.js +2 -0
  27. package/dist/assets/{core-BICrEt0y.js → core-DB-MkGjj.js} +3 -4
  28. package/dist/assets/coreTypes-DtnDATZ7.js +1 -0
  29. package/dist/assets/{cssMode-D7TXw57I.js → cssMode-DPVckSOc.js} +1 -1
  30. package/dist/assets/definitions-B227e7f_.js +1 -0
  31. package/dist/assets/definitions-D0WOUJd7.js +1 -0
  32. package/dist/assets/{editor.api-DvkCO3ko.js → editor.api-Dkutg_Z1.js} +1 -1
  33. package/dist/assets/expressions-X-Z-GrgW.js +5 -0
  34. package/dist/assets/file-DAL6IfjV.js +1 -0
  35. package/dist/assets/filters-D3k1LRMa.js +1 -0
  36. package/dist/assets/{freemarker2-BA2lxsF1.js → freemarker2-CLiLKk3B.js} +1 -1
  37. package/dist/assets/generators-BhDxRAFC.js +1 -0
  38. package/dist/assets/geo-BsMImJIJ.js +1 -0
  39. package/dist/assets/geo-C4YngDob.js +2 -0
  40. package/dist/assets/{handlebars-BcDiyPd8.js → handlebars-CcD3aMeL.js} +1 -1
  41. package/dist/assets/{html-C7O4TyhX.js → html-DvZWGfqb.js} +1 -1
  42. package/dist/assets/{htmlMode-WaDGF3Mj.js → htmlMode-DUD3VaoN.js} +1 -1
  43. package/dist/assets/image-0SfH-snh.js +1 -0
  44. package/dist/assets/image-Ck-RdGU4.js +2 -0
  45. package/dist/assets/index-BWnrmIB9.js +137 -0
  46. package/dist/assets/index-DIaxEONI.css +1 -0
  47. package/dist/assets/io-BGmt2g0D.js +1 -0
  48. package/dist/assets/{javascript-CpIEK7tz.js → javascript-Cap2ahgh.js} +1 -1
  49. package/dist/assets/{jsonMode-B2FltW0f.js → jsonMode-B0uLjp9C.js} +1 -1
  50. package/dist/assets/{liquid-CN6FFHZE.js → liquid-CnxwhfLn.js} +1 -1
  51. package/dist/assets/{lspLanguageFeatures-QhV9XtLp.js → lspLanguageFeatures-Dn9hlKX6.js} +1 -1
  52. package/dist/assets/math-GeULS-sk.js +1 -0
  53. package/dist/assets/{mdx-DE89pafV.js → mdx-BV3jbrVa.js} +1 -1
  54. package/dist/assets/pop-KMF_qloi.js +1 -0
  55. package/dist/assets/preload-helper-Czpn1I53.js +1 -0
  56. package/dist/assets/{python-ByTllTB9.js → python-CGyFk4XK.js} +1 -1
  57. package/dist/assets/{razor-USYlRRCE.js → razor-CQfzX3-N.js} +1 -1
  58. package/dist/assets/remap-5SjXQU-q.js +1 -0
  59. package/dist/assets/renderMarkdown-ork0Oir7.js +67 -0
  60. package/dist/assets/studioImages-CAyH-0Pl.js +1 -0
  61. package/dist/assets/text-ZpX7qNkx.js +2 -0
  62. package/dist/assets/time-P_OiBhcf.js +1 -0
  63. package/dist/assets/{tsMode-BXpN8iUz.js → tsMode-BOeXzO9H.js} +1 -1
  64. package/dist/assets/{typescript-DInBjGQQ.js → typescript-J1SnE_Tu.js} +1 -1
  65. package/dist/assets/{workers-C7Ye5B7D.js → workers-CYwwTiVF.js} +1 -1
  66. package/dist/assets/{xml-U0Uocqyo.js → xml-DnPRNzK9.js} +1 -1
  67. package/dist/assets/{yaml-CBw_PVYf.js → yaml-BmmYBNa9.js} +1 -1
  68. package/dist/cli/DefinitionNode-P7AA4TAU.js +15 -0
  69. package/dist/cli/Graph-OB6EWSGL.js +2975 -0
  70. package/dist/cli/assert-GROGQNCQ.js +25 -0
  71. package/dist/cli/checks-3WB44XBL.js +189 -0
  72. package/dist/cli/chunk-2JEU3MGB.js +1125 -0
  73. package/dist/cli/chunk-2KXB47FP.js +115 -0
  74. package/dist/cli/chunk-3RXL5GHB.js +816 -0
  75. package/dist/cli/chunk-53LGFPWX.js +138 -0
  76. package/dist/cli/chunk-5ORUQ6ES.js +267 -0
  77. package/dist/cli/chunk-73S4JZXK.js +336 -0
  78. package/dist/cli/chunk-7QE2ZNSN.js +121 -0
  79. package/dist/cli/chunk-BP7UKF36.js +376 -0
  80. package/dist/cli/chunk-BPODRH52.js +201 -0
  81. package/dist/cli/chunk-BZ63XQ3I.js +1301 -0
  82. package/dist/cli/chunk-CKS2FQWR.js +89 -0
  83. package/dist/cli/chunk-CO22XHNG.js +593 -0
  84. package/dist/cli/chunk-CTPLRSWF.js +2197 -0
  85. package/dist/cli/chunk-DEQSEFFH.js +153 -0
  86. package/dist/cli/chunk-EU4ZFZ7Y.js +122 -0
  87. package/dist/cli/chunk-EZRWJ4G2.js +104 -0
  88. package/dist/cli/chunk-FWMSGKEH.js +71 -0
  89. package/dist/cli/chunk-G23A2BZZ.js +705 -0
  90. package/dist/cli/chunk-GRIN2DDP.js +1465 -0
  91. package/dist/cli/chunk-HFHWYL55.js +139 -0
  92. package/dist/cli/chunk-HSNOBIVT.js +263 -0
  93. package/dist/cli/chunk-ICH7N5PN.js +26 -0
  94. package/dist/cli/chunk-J7VK7MNF.js +418 -0
  95. package/dist/cli/chunk-JVOCK5W4.js +83 -0
  96. package/dist/cli/chunk-KBGUO27Q.js +15 -0
  97. package/dist/cli/chunk-KH4MUBKF.js +1551 -0
  98. package/dist/cli/chunk-L6B6FUQP.js +330 -0
  99. package/dist/cli/chunk-LTXWBXOM.js +438 -0
  100. package/dist/cli/chunk-MI5JLQYA.js +220 -0
  101. package/dist/cli/chunk-MP653A5O.js +3766 -0
  102. package/dist/cli/chunk-NJFMDXHR.js +320 -0
  103. package/dist/cli/chunk-OM6BEME6.js +267 -0
  104. package/dist/cli/chunk-OWWBYLHC.js +136 -0
  105. package/dist/cli/chunk-QQPA2ZHH.js +273 -0
  106. package/dist/cli/chunk-RUU3YWXU.js +99 -0
  107. package/dist/cli/chunk-RXDIMXN5.js +98 -0
  108. package/dist/cli/chunk-ST6LROUG.js +267 -0
  109. package/dist/cli/chunk-TJW67QSI.js +225 -0
  110. package/dist/cli/chunk-WD62JIP6.js +118 -0
  111. package/dist/cli/chunk-WGKPJZDY.js +364 -0
  112. package/dist/cli/chunk-X4WKAOQP.js +634 -0
  113. package/dist/cli/chunk-X566FWBV.js +156 -0
  114. package/dist/cli/chunk-YH637PDL.js +317 -0
  115. package/dist/cli/chunk-ZL4JTG5Y.js +37 -0
  116. package/dist/cli/chunk-ZSCUZE6I.js +112 -0
  117. package/dist/cli/cliCommands-JJN5Z3ZC.js +3470 -0
  118. package/dist/cli/commands-PHYFYA3F.js +1374 -0
  119. package/dist/cli/contactSheet-2PQVRUFW.js +11 -0
  120. package/dist/cli/definitions-BPOPW52L.js +32 -0
  121. package/dist/cli/doctorCommand-ORWQRNYP.js +106 -0
  122. package/dist/cli/edit-S5KEQ5RA.js +28 -0
  123. package/dist/cli/editorCommands-NFR32JOY.js +41 -0
  124. package/dist/cli/encode-ICPEPMH5.js +17 -0
  125. package/dist/cli/file-3HTQHPYQ.js +41 -0
  126. package/dist/cli/filters-6ODFWYK4.js +379 -0
  127. package/dist/cli/frames-LSAUBZ7P.js +16 -0
  128. package/dist/cli/generators-YU7GB4MD.js +210 -0
  129. package/dist/cli/geo-6ZPGKZRU.js +43 -0
  130. package/dist/cli/graphCommands-EOUOBOIJ.js +184 -0
  131. package/dist/cli/graphFile-YTGJB5F5.js +292 -0
  132. package/dist/cli/headlessCanvas-ENRHC7GS.js +16 -0
  133. package/dist/cli/helpCommand-PULH6ZYB.js +157 -0
  134. package/dist/cli/image-TXELZZK5.js +55 -0
  135. package/dist/cli/index.js +100 -30756
  136. package/dist/cli/math-RSYRQ3C6.js +16 -0
  137. package/dist/cli/mcp-JVZFQOEX.js +164 -0
  138. package/dist/cli/parity-X6DEH2DV.js +334 -0
  139. package/dist/cli/pop-B7SIIYKN.js +39 -0
  140. package/dist/cli/project-VYGIA7JX.js +12 -0
  141. package/dist/cli/projectDefinition-F3D7UIHJ.js +27 -0
  142. package/dist/cli/projectRuntime-2Q4JKEH3.js +55 -0
  143. package/dist/cli/registerStandardNodes-BXJCB6E4.js +744 -0
  144. package/dist/cli/render-PT4MWI6U.js +438 -0
  145. package/dist/cli/runCommand-HNPQVZMS.js +198 -0
  146. package/dist/cli/runner-5XPXVW7J.js +43 -0
  147. package/dist/cli/runtime/image.ts +302 -0
  148. package/dist/cli/runtime/io.ts +78 -0
  149. package/dist/cli/runtime/stage.ts +4 -1
  150. package/dist/cli/share-MHF6TNRK.js +166 -0
  151. package/dist/cli/stageCache-TY2BFZJR.js +15 -0
  152. package/dist/cli/stageRunner-KY2EJP7X.js +10 -0
  153. package/dist/cli/text-MB3EKR4H.js +126 -0
  154. package/dist/cli/verbs-PI2JH5FI.js +19 -0
  155. package/dist/cli/webBuild-WPPMGGW7.js +44 -0
  156. package/dist/contracts/capabilities.d.ts +18 -1
  157. package/dist/contracts/color.d.ts +8 -0
  158. package/dist/contracts/color.js +23 -0
  159. package/dist/contracts/definition.d.ts +10 -0
  160. package/dist/contracts/documentFormat.d.ts +18 -0
  161. package/dist/contracts/documentFormat.js +195 -0
  162. package/dist/contracts/editor.d.ts +17 -0
  163. package/dist/contracts/index.d.ts +1 -0
  164. package/dist/contracts/index.js +1 -0
  165. package/dist/contracts/schema.d.ts +1 -1
  166. package/dist/contracts/schema.js +1 -0
  167. package/dist/contracts/validate.js +11 -1
  168. package/dist/contracts/values.d.ts +24 -0
  169. package/dist/image.d.ts +156 -0
  170. package/dist/image.js +161 -0
  171. package/dist/index.html +8 -2
  172. package/dist/io.d.ts +29 -0
  173. package/dist/io.js +40 -0
  174. package/dist/player/app.js +3 -2
  175. package/dist/player/editor.js +35 -9
  176. package/dist/player/editorEngine.js +3 -2
  177. package/dist/runtime/builtins/assert/checks.d.ts +18 -0
  178. package/dist/runtime/builtins/assert/checks.js +207 -0
  179. package/dist/runtime/builtins/assert/definitions.d.ts +130 -0
  180. package/dist/runtime/builtins/assert/definitions.js +54 -0
  181. package/dist/runtime/builtins/assert/index.d.ts +152 -0
  182. package/dist/runtime/builtins/assert/index.js +36 -0
  183. package/dist/runtime/builtins/image/contactSheet.d.ts +64 -0
  184. package/dist/runtime/builtins/image/contactSheet.js +150 -0
  185. package/dist/runtime/builtins/image/definitions.d.ts +950 -0
  186. package/dist/runtime/builtins/image/definitions.js +343 -0
  187. package/dist/runtime/builtins/image/file.d.ts +8 -0
  188. package/dist/runtime/builtins/image/file.js +37 -0
  189. package/dist/runtime/builtins/image/filters.d.ts +8 -0
  190. package/dist/runtime/builtins/image/filters.js +394 -0
  191. package/dist/runtime/builtins/image/generators.d.ts +7 -0
  192. package/dist/runtime/builtins/image/generators.js +144 -0
  193. package/dist/runtime/builtins/image/index.d.ts +952 -0
  194. package/dist/runtime/builtins/image/index.js +44 -0
  195. package/dist/runtime/builtins/image/io.d.ts +100 -0
  196. package/dist/runtime/builtins/image/io.js +142 -0
  197. package/dist/runtime/builtins/image/planar.d.ts +55 -0
  198. package/dist/runtime/builtins/image/planar.js +274 -0
  199. package/dist/runtime/builtins/image/simplex.d.ts +22 -0
  200. package/dist/runtime/builtins/image/simplex.js +92 -0
  201. package/dist/runtime/builtins/image/text.d.ts +4 -0
  202. package/dist/runtime/builtins/image/text.js +141 -0
  203. package/dist/runtime/builtins/index.d.ts +3 -0
  204. package/dist/runtime/builtins/index.js +7 -0
  205. package/dist/runtime/gpu.d.ts +25 -0
  206. package/dist/runtime/gpu.js +40 -0
  207. package/dist/runtime/runtime.d.ts +2 -0
  208. package/dist/runtime/runtime.js +268 -44
  209. package/dist/runtime/types.d.ts +30 -0
  210. package/dist/stage.js +4 -1
  211. package/doc/CLI_REFERENCE.md +91 -2
  212. package/doc/EDITOR_EXPORT.md +37 -2
  213. package/doc/MCP.md +18 -1
  214. package/doc/NODE_REFERENCE.md +424 -10
  215. package/doc/PROJECT_AUTHORING.md +222 -7
  216. package/package.json +19 -4
  217. package/dist/assets/CodePanel-sY6sZa7B.js +0 -2
  218. package/dist/assets/CustomNodeDialog-BRKnqZhp.js +0 -1
  219. package/dist/assets/ExportDialog-C7dVGua4.js +0 -358
  220. package/dist/assets/ExportDialog-DOq0flMV.css +0 -1
  221. package/dist/assets/OpenGraphDialog-ZsgL-iwr.js +0 -1
  222. package/dist/assets/ProjectSettings-Cw17nNe3.js +0 -2
  223. package/dist/assets/definitions-DUww2Rt-.js +0 -1
  224. package/dist/assets/geo-C2RHOVHC.js +0 -1
  225. package/dist/assets/image-DR4oeyJB.js +0 -2062
  226. package/dist/assets/index-7vae6YFC.css +0 -1
  227. package/dist/assets/index-DyvDC6pS.js +0 -219
  228. package/dist/assets/math-DaDEgWKt.js +0 -1
  229. package/dist/assets/pop-pLH7L0PW.js +0 -1
@@ -0,0 +1,267 @@
1
+ import {
2
+ setEmbeddedCompiler,
3
+ setProjectModuleCompiler
4
+ } from "./chunk-2KXB47FP.js";
5
+ import {
6
+ installHeadlessCanvas
7
+ } from "./chunk-EZRWJ4G2.js";
8
+ import {
9
+ parseFrameSpec
10
+ } from "./chunk-BP7UKF36.js";
11
+ import {
12
+ encodeMp4,
13
+ timelineFrameSpec
14
+ } from "./chunk-7QE2ZNSN.js";
15
+ import {
16
+ applyEnv
17
+ } from "./chunk-ZL4JTG5Y.js";
18
+ import {
19
+ RunTrace,
20
+ captureNodeConsole,
21
+ checkProjectGraph,
22
+ cookingNode,
23
+ inspectProjectGraph,
24
+ installProjectIo,
25
+ installProjectStages,
26
+ renderDeterministicProjectFrames,
27
+ runDeterministicProjectGraph,
28
+ saveStillImages,
29
+ validateProjectGraph
30
+ } from "./chunk-MP653A5O.js";
31
+ import {
32
+ graphSeed
33
+ } from "./chunk-L6B6FUQP.js";
34
+ import {
35
+ frameRange
36
+ } from "./chunk-CO22XHNG.js";
37
+ import {
38
+ compileEmbedded,
39
+ compileProjectModule
40
+ } from "./chunk-EU4ZFZ7Y.js";
41
+ import {
42
+ cascadeVersion,
43
+ formatStageTrace
44
+ } from "./chunk-5ORUQ6ES.js";
45
+ import {
46
+ ProjectRoot
47
+ } from "./chunk-2JEU3MGB.js";
48
+
49
+ // src/cli/runner.ts
50
+ import * as fs from "fs/promises";
51
+ import * as path from "path";
52
+ import { existsSync } from "node:fs";
53
+ var RENDER_SUGGESTION_FRAMES = 30;
54
+ function renderSuggestion(file, frames, count) {
55
+ if (count < RENDER_SUGGESTION_FRAMES) return null;
56
+ return `${count} frames in one process. cascade render ${file} --frames ${frames} renders them across worker processes (and encodes a preview mp4; --no-video for frames only).`;
57
+ }
58
+ async function runGraph(options) {
59
+ const { file, entryNode, nodes, validateOnly, checkOnly, inspectOnly, strict, verbose, frames, fps, out } = options;
60
+ if (verbose) {
61
+ console.log(`Loading graph from: ${file}`);
62
+ }
63
+ let graphData;
64
+ try {
65
+ const filePath = path.resolve(file);
66
+ const content = await fs.readFile(filePath, "utf-8");
67
+ graphData = JSON.parse(content);
68
+ if (options.seed !== void 0) graphData.seed = options.seed;
69
+ } catch (error) {
70
+ console.error(`Failed to load graph file: ${error.message}`);
71
+ process.exit(1);
72
+ }
73
+ const projectRoot = path.dirname(path.resolve(file));
74
+ if (validateOnly || checkOnly) {
75
+ const unresolved = checkOnly ? await checkProjectGraph(file, graphData, strict) : await validateProjectGraph(file, graphData, strict);
76
+ const headline = checkOnly ? "Static check passed" : "Graph validation passed";
77
+ console.log(
78
+ unresolved > 0 ? `${headline} with ${unresolved} unresolved ${unresolved === 1 ? "module" : "modules"} \u2014 run with --strict to fail on these` : `${headline}!`
79
+ );
80
+ return;
81
+ }
82
+ if (inspectOnly) {
83
+ console.log(JSON.stringify(await inspectProjectGraph(file, graphData), null, 2));
84
+ return;
85
+ }
86
+ let disposeStageBridge;
87
+ let disposeIoBridge;
88
+ let canvasHost;
89
+ let canvasError = null;
90
+ let project;
91
+ const trace = new RunTrace(path.resolve(file), cascadeVersion(), graphSeed(graphData));
92
+ const observe = (graph) => trace.attach(graph);
93
+ const writeTrace = () => {
94
+ if (!project || !existsSync(path.resolve(file))) return void 0;
95
+ try {
96
+ return trace.write(project.root);
97
+ } catch (error) {
98
+ console.warn(`Could not write the run trace: ${error instanceof Error ? error.message : error}`);
99
+ return void 0;
100
+ }
101
+ };
102
+ try {
103
+ project = new ProjectRoot(path.dirname(path.resolve(file)));
104
+ setProjectModuleCompiler(async (folderName) => (await compileProjectModule(project, folderName)).code);
105
+ setEmbeddedCompiler(async (code) => (await compileEmbedded(project, code)).code);
106
+ disposeStageBridge = installProjectStages(project, {
107
+ verbose,
108
+ // A render worker starts Python while the probe frames run.
109
+ prewarm: Boolean(options.worker),
110
+ // A render worker is already one of several processes: one Python each.
111
+ ...options.worker ? { poolSize: 1 } : {},
112
+ onTrace: (stage) => {
113
+ trace.recordStage(stage);
114
+ if (verbose) console.log(`[stage] ${formatStageTrace(stage)}`);
115
+ },
116
+ // What a stage prints goes into the trace against the node that called
117
+ // it, always; on the terminal too with --verbose (the console capture
118
+ // below records that copy, so it is not recorded twice).
119
+ onOutput: (stage, stream, line) => {
120
+ const text = `[${stage ?? "stage-worker"}] ${line}`;
121
+ if (verbose) (stream === "stderr" ? console.warn : console.log)(text);
122
+ else if (stage === null) cookingNode.exit(() => trace.recordLog(stream, text));
123
+ else trace.recordLog(stream, text);
124
+ }
125
+ });
126
+ disposeIoBridge = installProjectIo(project, { resident: true });
127
+ } catch (error) {
128
+ if (verbose) console.warn(`No project context for ${file}: ${error instanceof Error ? error.message : error}`);
129
+ }
130
+ try {
131
+ canvasHost = await installHeadlessCanvas();
132
+ if (verbose) console.log(`Canvas: ${canvasHost.renderer}`);
133
+ } catch (error) {
134
+ canvasError = error instanceof Error ? error : new Error(String(error));
135
+ if (verbose) console.warn(canvasError.message);
136
+ }
137
+ const frameSpec = frames ? frames === "timeline" ? timelineFrameSpec(graphData) : parseFrameSpec(frames) : null;
138
+ if (frameSpec && frames && !options.worker) {
139
+ const hint = renderSuggestion(file, frames, frameRange(frameSpec.start, frameSpec.end, frameSpec.step ?? 1).length);
140
+ if (hint) (options.json ? console.error : console.log)(hint);
141
+ }
142
+ const restoreConsole = captureNodeConsole(trace);
143
+ const restoreEnv = applyEnv(options.env);
144
+ if (options.env) trace.setEnvNames(Object.keys(options.env));
145
+ let hostReleased = false;
146
+ const releaseHost = () => {
147
+ if (hostReleased) return;
148
+ hostReleased = true;
149
+ const errors = [];
150
+ for (const dispose of [
151
+ restoreConsole,
152
+ disposeStageBridge,
153
+ restoreEnv,
154
+ disposeIoBridge,
155
+ canvasHost ? () => canvasHost.dispose() : void 0,
156
+ () => setProjectModuleCompiler(null),
157
+ () => setEmbeddedCompiler(null)
158
+ ]) {
159
+ try {
160
+ dispose?.();
161
+ } catch (error) {
162
+ errors.push(error);
163
+ }
164
+ }
165
+ if (errors.length) throw errors[0];
166
+ };
167
+ try {
168
+ if (frameSpec) {
169
+ const rendered = await renderDeterministicProjectFrames(file, graphData, {
170
+ ...frameSpec,
171
+ ...fps === void 0 ? {} : { fps },
172
+ out: out ?? "renders",
173
+ ...entryNode ? { entryNode } : {},
174
+ ...nodes?.length ? { nodes } : {},
175
+ ...options.outNodes?.length ? { outNodes: options.outNodes } : {},
176
+ ...verbose ? { verbose } : {},
177
+ ...options.fresh ? { fresh: true } : {},
178
+ ...options.worker ? { frameSource: options.worker.frameSource } : {},
179
+ onFrame: async (frame) => {
180
+ trace.recordSaved(frame.durationMs);
181
+ await options.worker?.onFrame(frame);
182
+ },
183
+ observe
184
+ });
185
+ if (rendered && options.worker) {
186
+ releaseHost();
187
+ return;
188
+ }
189
+ if (rendered) {
190
+ releaseHost();
191
+ const tracePath = writeTrace();
192
+ const video = options.mp4 && !rendered.aborted ? await encodeMp4({ root: projectRoot, files: rendered.files, frames: rendered.frames, fps: rendered.fps, file: options.mp4, ...options.crf === void 0 ? {} : { crf: options.crf } }) : void 0;
193
+ if (options.json) {
194
+ process.stdout.write(JSON.stringify({
195
+ status: "completed",
196
+ graph: path.resolve(file),
197
+ ...rendered,
198
+ ...video ? { video } : {},
199
+ seed: graphSeed(graphData),
200
+ ...tracePath ? { trace: tracePath } : {},
201
+ stages: trace.stages(),
202
+ logs: trace.logs()
203
+ }) + "\n");
204
+ } else {
205
+ console.log(`Rendered ${rendered.frames.length} frames, ${rendered.files.length} files -> ${out ?? "renders"}/`);
206
+ if (video) console.log(`Encoded ${video.file} (H.264 yuv420p, crf ${video.crf}, ${video.fps} fps)`);
207
+ }
208
+ return;
209
+ }
210
+ if (options.json) throw new Error("--json rendering requires a fully definition-v1 graph");
211
+ } else {
212
+ const started = performance.now();
213
+ let images = [];
214
+ const target = options.outNodes?.length || (nodes?.length ?? 0) > 1 ? void 0 : nodes?.[0] ?? entryNode;
215
+ const saveImages = async (graph) => {
216
+ if (!project) return;
217
+ const saving = performance.now();
218
+ images = await saveStillImages(graph, project, {
219
+ ...out ? { out } : {},
220
+ ...nodes?.length ? { nodes } : entryNode ? { nodes: [entryNode] } : {},
221
+ ...options.outNodes?.length ? { extra: options.outNodes } : {}
222
+ });
223
+ const runs = trace.toJSON().runs;
224
+ const cook = runs[runs.length - 1]?.ms;
225
+ if (cook !== void 0) trace.recordSaved(cook + performance.now() - saving);
226
+ };
227
+ if (await runDeterministicProjectGraph(file, graphData, target, observe, saveImages)) {
228
+ if (verbose) console.log("Graph execution completed");
229
+ releaseHost();
230
+ const tracePath = writeTrace();
231
+ if (options.json) {
232
+ process.stdout.write(JSON.stringify({
233
+ status: "completed",
234
+ graph: path.resolve(file),
235
+ durationMs: performance.now() - started,
236
+ seed: graphSeed(graphData),
237
+ images,
238
+ ...out ? { files: images.map((image) => image.file) } : {},
239
+ ...tracePath ? { trace: tracePath } : {},
240
+ stages: trace.stages(),
241
+ logs: trace.logs()
242
+ }) + "\n");
243
+ } else if (out) {
244
+ console.log(`Saved ${images.length} ${images.length === 1 ? "image" : "images"} -> ${out}/`);
245
+ }
246
+ return;
247
+ }
248
+ }
249
+ } catch (error) {
250
+ releaseHost();
251
+ const tracePath = options.worker ? void 0 : writeTrace();
252
+ if (tracePath && error instanceof Error) error.message += `
253
+ Run trace: ${tracePath}`;
254
+ throw error;
255
+ }
256
+ releaseHost();
257
+ throw new Error(
258
+ `${path.resolve(file)} has no definition-v1 nodes, and the dynamic executor was removed in 0.7. Every node needs a static \`export const definition\` \u2014 see doc/NODE_AUTHORING.md.`
259
+ );
260
+ }
261
+
262
+ export {
263
+ RENDER_SUGGESTION_FRAMES,
264
+ renderSuggestion,
265
+ runGraph
266
+ };
267
+ //# sourceMappingURL=chunk-ST6LROUG.js.map
@@ -0,0 +1,225 @@
1
+ import {
2
+ browserAssetCapability
3
+ } from "./chunk-ZSCUZE6I.js";
4
+ import {
5
+ attachDefinition
6
+ } from "./chunk-BPODRH52.js";
7
+ import {
8
+ createGpuHost,
9
+ requestDeviceWithAdapterLimits
10
+ } from "./chunk-L6B6FUQP.js";
11
+ import {
12
+ validateNodeDefinition
13
+ } from "./chunk-NJFMDXHR.js";
14
+
15
+ // src/nodes/definition/browserShellCapability.ts
16
+ var capability = null;
17
+ function forgetShellCapability() {
18
+ capability = null;
19
+ }
20
+ async function token() {
21
+ if (capability) return capability;
22
+ const response = await fetch("/api/shell/capability", { cache: "no-store" });
23
+ if (!response.ok) {
24
+ throw new Error(
25
+ response.status === 404 ? "The shell capability is disabled on this server. It is available on a loopback server, or one started with an explicit trusted host." : `Could not obtain the shell capability (${response.status})`
26
+ );
27
+ }
28
+ const { capability: issued } = await response.json();
29
+ if (typeof issued !== "string" || !issued) {
30
+ throw new Error("Server issued no shell capability");
31
+ }
32
+ capability = issued;
33
+ return issued;
34
+ }
35
+ function domSignal(signal) {
36
+ if (!signal) return void 0;
37
+ const controller = new AbortController();
38
+ if (signal.aborted) controller.abort(signal.reason);
39
+ else signal.addEventListener("abort", () => controller.abort(signal.reason), { once: true });
40
+ return controller.signal;
41
+ }
42
+ async function post(body, signal) {
43
+ const bridged = domSignal(signal);
44
+ const send = async (issued) => fetch("/api/shell", {
45
+ method: "POST",
46
+ headers: {
47
+ "Content-Type": "application/json",
48
+ "X-Cascade-Shell-Capability": issued
49
+ },
50
+ body: JSON.stringify(body),
51
+ ...bridged ? { signal: bridged } : {}
52
+ });
53
+ let response = await send(await token());
54
+ if (response.status === 403) {
55
+ capability = null;
56
+ response = await send(await token());
57
+ }
58
+ return response;
59
+ }
60
+ function requestBody(command, args, options) {
61
+ const { signal: _signal, ...rest } = options ?? {};
62
+ return {
63
+ command,
64
+ ...args ? { args } : {},
65
+ // `/api/shell` has always accepted execution options at the top level;
66
+ // keep the Studio adapter on that wire contract. Nesting them under
67
+ // `options` silently drops stdin, timeout, cwd and env in the route parser.
68
+ ...rest
69
+ };
70
+ }
71
+ async function fail(response) {
72
+ let payload = null;
73
+ try {
74
+ payload = await response.json();
75
+ } catch {
76
+ }
77
+ const message = typeof payload?.error === "string" ? payload.error : `Shell request failed (${response.status})`;
78
+ if (payload?.code === "COMMAND_NOT_ALLOWED") {
79
+ throw new Error(`${message}. Add it under Allowed Commands in Project Settings.`);
80
+ }
81
+ throw new Error(message);
82
+ }
83
+ var browserShellCapability = {
84
+ async run(command, args, options) {
85
+ const response = await post(requestBody(command, args, options), options?.signal);
86
+ if (!response.ok) await fail(response);
87
+ const { ok: _ok, ...result } = await response.json();
88
+ return result;
89
+ },
90
+ async runJson(command, args, options) {
91
+ const result = await this.run(command, args, options);
92
+ try {
93
+ return JSON.parse(result.stdout);
94
+ } catch {
95
+ throw new Error(`${command} did not print JSON`);
96
+ }
97
+ }
98
+ };
99
+ async function shellRouteAvailable() {
100
+ const forget = () => {
101
+ forgetShellCapability();
102
+ return false;
103
+ };
104
+ if (typeof fetch !== "function") return forget();
105
+ try {
106
+ const response = await fetch("/api/shell/capability", { cache: "no-store" });
107
+ if (!response.ok) return forget();
108
+ const { capability: issued } = await response.json();
109
+ if (typeof issued !== "string" || !issued) return forget();
110
+ capability = issued;
111
+ return true;
112
+ } catch {
113
+ return forget();
114
+ }
115
+ }
116
+
117
+ // src/browser/gpu.ts
118
+ function createBrowserGpuHost() {
119
+ const gpu = globalThis.navigator?.gpu;
120
+ if (!gpu) return void 0;
121
+ return createGpuHost({
122
+ async acquire() {
123
+ const adapter = await gpu.requestAdapter();
124
+ if (!adapter) {
125
+ throw new Error(
126
+ "No WebGPU adapter is available \u2014 the browser reports WebGPU but the system offers no GPU to render on."
127
+ );
128
+ }
129
+ const device = await requestDeviceWithAdapterLimits(adapter);
130
+ const info = adapter.info;
131
+ return {
132
+ device,
133
+ info: {
134
+ vendor: info?.vendor ?? "",
135
+ architecture: info?.architecture ?? "",
136
+ device: info?.device ?? "",
137
+ description: info?.description ?? ""
138
+ },
139
+ limits: device.limits ?? adapter.limits
140
+ };
141
+ }
142
+ });
143
+ }
144
+
145
+ // src/nodes/definition/projectDefinition.ts
146
+ var studioCapabilities = {
147
+ assets: browserAssetCapability,
148
+ // Installed only where the browser has WebGPU, and absent rather than
149
+ // stubbed otherwise. That is what makes the failure a sentence: the preflight
150
+ // reports `runtime/missing-capability` and the adapter throws "requires the
151
+ // Studio gpu capability", both naming what is missing. A stub that threw on
152
+ // first use would name nothing.
153
+ ...gpuCapabilityIfAvailable(),
154
+ /**
155
+ * Shell is present up front and withdrawn if the route turns out not to be
156
+ * there, which is the opposite way round from gpu and worth the sentence.
157
+ *
158
+ * `navigator.gpu` is a synchronous check, so gpu can be absent from the map
159
+ * before anything reads it. Whether the shell route exists takes a fetch, and
160
+ * this object is a module-level const read by three default parameters — so
161
+ * the choice is between delaying Studio's boot on a request, racing the
162
+ * probe, or installing and withdrawing. Installing is the one where the
163
+ * common case (a loopback server, where the route is always there) has no
164
+ * race at all and no cost.
165
+ *
166
+ * What it costs: on a server that does NOT offer the route, a cook in the
167
+ * first few milliseconds after boot sees the capability present and fails at
168
+ * the fetch instead of at the gate. That failure still names itself — *"the
169
+ * shell capability is disabled on this server"* — so it is a worse-shaped
170
+ * error rather than a silent one, and once the probe lands the preflight is
171
+ * correct from then on.
172
+ */
173
+ shell: browserShellCapability
174
+ };
175
+ void shellRouteAvailable().then((available) => {
176
+ if (!available) delete studioCapabilities.shell;
177
+ });
178
+ function gpuCapabilityIfAvailable() {
179
+ const gpu = createBrowserGpuHost();
180
+ return gpu ? { gpu } : {};
181
+ }
182
+ function declaresDefinitionV1(value) {
183
+ return typeof value === "object" && value !== null && value.apiVersion === 1;
184
+ }
185
+ function definitionAdapterError(value) {
186
+ const diagnostics = validateNodeDefinition(value);
187
+ if (!diagnostics.length) return null;
188
+ return diagnostics.map((item) => `${item.code}: ${item.message}`).join("\n");
189
+ }
190
+ function missingStudioCapabilities(definition, capabilities = studioCapabilities) {
191
+ const declared = definition?.capabilities;
192
+ if (!Array.isArray(declared)) return [];
193
+ return declared.filter((name) => typeof name === "string").filter((name) => !capabilities[name]);
194
+ }
195
+ function moduleNodeFunction(node, moduleId, modulePromise, capabilities = studioCapabilities) {
196
+ const prepared = modulePromise.then((module) => adaptModule(node, moduleId, module, capabilities));
197
+ prepared.catch(() => {
198
+ });
199
+ return (n, g) => prepared.then((fn) => fn(n, g));
200
+ }
201
+ function adaptModule(node, moduleId, module, capabilities = studioCapabilities) {
202
+ if (!declaresDefinitionV1(module.definition)) {
203
+ return (n, g) => module.execute(n, g);
204
+ }
205
+ const invalid = definitionAdapterError(module.definition);
206
+ if (invalid) throw new Error(`${moduleId} exports an invalid definition-v1 literal:
207
+ ${invalid}`);
208
+ const registration = {
209
+ kind: "definition-v1",
210
+ moduleId,
211
+ definition: module.definition,
212
+ loadExecute: async () => module.execute
213
+ };
214
+ return attachDefinition(node, registration, capabilities);
215
+ }
216
+
217
+ export {
218
+ studioCapabilities,
219
+ declaresDefinitionV1,
220
+ definitionAdapterError,
221
+ missingStudioCapabilities,
222
+ moduleNodeFunction,
223
+ adaptModule
224
+ };
225
+ //# sourceMappingURL=chunk-TJW67QSI.js.map
@@ -0,0 +1,118 @@
1
+ // server/src/pythonEnv.ts
2
+ import { execFile } from "node:child_process";
3
+ import crypto from "node:crypto";
4
+ import fs from "node:fs";
5
+ import path from "node:path";
6
+ var RELATIVE_DIRECTORY = path.join(".cascade-cache", "python", "venv");
7
+ var MARKER = "cascade-env.json";
8
+ function pythonEnvDirectory(root) {
9
+ return path.join(root, RELATIVE_DIRECTORY);
10
+ }
11
+ function pythonEnvInterpreter(root) {
12
+ const directory = pythonEnvDirectory(root);
13
+ return process.platform === "win32" ? path.join(directory, "Scripts", "python.exe") : path.join(directory, "bin", "python");
14
+ }
15
+ function readPythonEnv(root) {
16
+ let raw;
17
+ try {
18
+ raw = JSON.parse(fs.readFileSync(path.join(root, "cascade.json"), "utf8"));
19
+ } catch {
20
+ return null;
21
+ }
22
+ const value = raw?.python;
23
+ if (value === void 0) return null;
24
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
25
+ throw new Error('cascade.json python must be an object: { "requirements": ["numpy", ...] | "requirements.txt", "interpreter"?: "python3" }');
26
+ }
27
+ const { requirements, interpreter } = value;
28
+ const list = Array.isArray(requirements) && requirements.every((item) => typeof item === "string" && item.trim());
29
+ if (!list && !(typeof requirements === "string" && requirements.trim())) {
30
+ throw new Error("cascade.json python.requirements must be a list of pip requirements or a project-relative requirements file");
31
+ }
32
+ if (typeof requirements === "string") containedPath(root, requirements, "python.requirements");
33
+ if (interpreter !== void 0 && (typeof interpreter !== "string" || !/^[A-Za-z0-9._/-]+$/.test(interpreter))) {
34
+ throw new Error("cascade.json python.interpreter must be an executable name such as python3.12");
35
+ }
36
+ return { requirements, interpreter: interpreter ?? "python3" };
37
+ }
38
+ function pythonEnvKey(root, spec) {
39
+ const requirements = typeof spec.requirements === "string" ? `file ${spec.requirements}
40
+ ${fs.readFileSync(containedPath(root, spec.requirements, "python.requirements"), "utf8")}` : `list
41
+ ${spec.requirements.join("\n")}`;
42
+ return crypto.createHash("sha256").update(`${spec.interpreter}\0${requirements}`).digest("hex");
43
+ }
44
+ function pythonEnvStatus(root) {
45
+ const directory = pythonEnvDirectory(root);
46
+ const python = pythonEnvInterpreter(root);
47
+ const spec = readPythonEnv(root);
48
+ if (!spec) return { declared: false, directory, python, state: "none" };
49
+ if (!fs.existsSync(python)) return { declared: true, directory, python, state: "missing", reason: `no environment at ${path.relative(root, directory)}` };
50
+ let marker = {};
51
+ try {
52
+ marker = JSON.parse(fs.readFileSync(path.join(directory, MARKER), "utf8"));
53
+ } catch {
54
+ }
55
+ let key;
56
+ try {
57
+ key = pythonEnvKey(root, spec);
58
+ } catch (error) {
59
+ return { declared: true, directory, python, state: "stale", reason: error instanceof Error ? error.message : String(error) };
60
+ }
61
+ if (marker.key !== key) return { declared: true, directory, python, state: "stale", reason: "cascade.json python changed since the environment was built" };
62
+ return { declared: true, directory, python, state: "current" };
63
+ }
64
+ async function provisionPythonEnv(root, log = () => {
65
+ }) {
66
+ const spec = readPythonEnv(root);
67
+ if (!spec) throw new Error('cascade.json declares no python environment ("python": { "requirements": [...] })');
68
+ const before = pythonEnvStatus(root);
69
+ if (before.state === "current") return before;
70
+ const directory = pythonEnvDirectory(root);
71
+ if (fs.existsSync(directory)) {
72
+ const archive = path.join(path.dirname(directory), "_archive");
73
+ fs.mkdirSync(archive, { recursive: true });
74
+ fs.renameSync(directory, path.join(archive, `venv-${Date.now()}`));
75
+ }
76
+ fs.mkdirSync(path.dirname(directory), { recursive: true });
77
+ const file = typeof spec.requirements === "string" ? containedPath(root, spec.requirements, "python.requirements") : void 0;
78
+ const fileHasRequirements = file !== void 0 && fs.readFileSync(file, "utf8").split("\n").some((line) => line.trim() && !line.trim().startsWith("#"));
79
+ const requirements = file !== void 0 ? fileHasRequirements ? ["-r", file] : [] : [...spec.requirements];
80
+ log(`Creating ${path.relative(root, directory)} with ${spec.interpreter}`);
81
+ await run(spec.interpreter, ["-m", "venv", ...requirements.length ? [] : ["--without-pip"], directory], root);
82
+ if (requirements.length) {
83
+ log(`Installing ${typeof spec.requirements === "string" ? spec.requirements : spec.requirements.join(", ")}`);
84
+ await run(pythonEnvInterpreter(root), ["-m", "pip", "install", "--disable-pip-version-check", "--quiet", ...requirements], root);
85
+ }
86
+ fs.writeFileSync(path.join(directory, MARKER), `${JSON.stringify({ key: pythonEnvKey(root, spec), requirements: spec.requirements, interpreter: spec.interpreter, builtAt: (/* @__PURE__ */ new Date()).toISOString() }, null, 2)}
87
+ `);
88
+ return pythonEnvStatus(root);
89
+ }
90
+ function declaredStagePython(root) {
91
+ const status = pythonEnvStatus(root);
92
+ if (status.state === "none") return void 0;
93
+ if (status.state !== "current") {
94
+ throw new Error(`This project declares a Python environment (cascade.json "python") that is ${status.state}: ${status.reason}. Run \`cascade doctor --fix\` in ${root} to build it.`);
95
+ }
96
+ return status.python;
97
+ }
98
+ function containedPath(root, file, name) {
99
+ const resolved = path.resolve(root, file);
100
+ const relative = path.relative(root, resolved);
101
+ if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) throw new Error(`cascade.json ${name} must be a project-relative path`);
102
+ return resolved;
103
+ }
104
+ function run(command, args, cwd) {
105
+ return new Promise((resolve, reject) => {
106
+ execFile(command, [...args], { cwd, maxBuffer: 16 * 1024 * 1024, timeout: 20 * 6e4 }, (error, _stdout, stderr) => {
107
+ if (error) reject(new Error(`${path.basename(command)} ${args.slice(0, 3).join(" ")} failed: ${(stderr || error.message).trim().slice(-2e3)}`));
108
+ else resolve();
109
+ });
110
+ });
111
+ }
112
+
113
+ export {
114
+ pythonEnvStatus,
115
+ provisionPythonEnv,
116
+ declaredStagePython
117
+ };
118
+ //# sourceMappingURL=chunk-WD62JIP6.js.map