@field/cascade 0.10.0 → 0.11.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 (133) hide show
  1. package/AGENTS.md +20 -3
  2. package/CHANGELOG.md +187 -0
  3. package/README.md +1 -2
  4. package/dist/assets/{CodeEditor-DSZBpzdy.js → CodeEditor-4O7FQ__R.js} +4 -4
  5. package/dist/assets/{CodePanel-sY6sZa7B.js → CodePanel-BU-autmq.js} +2 -2
  6. package/dist/assets/{CustomNodeDialog-BRKnqZhp.js → CustomNodeDialog-BRpDfOV5.js} +1 -1
  7. package/dist/assets/{OpenGraphDialog-ZsgL-iwr.js → OpenGraphDialog-BwbRlC0X.js} +1 -1
  8. package/dist/assets/{ProjectSettings-Cw17nNe3.js → ProjectSettings-DLOXLxcA.js} +1 -1
  9. package/dist/assets/{SettingsDialog-p25h3tj0.js → SettingsDialog-Bz8JxISH.js} +1 -1
  10. package/dist/assets/{VersionHistoryDialog-DzFLUotB.js → VersionHistoryDialog-D5Z0nJ5-.js} +1 -1
  11. package/dist/assets/{core-BICrEt0y.js → core-Yqi0Ni61.js} +1 -1
  12. package/dist/assets/{cssMode-D7TXw57I.js → cssMode-CxUzc4vp.js} +1 -1
  13. package/dist/assets/{definitions-DUww2Rt-.js → definitions-GreEMXLV.js} +1 -1
  14. package/dist/assets/{editor.api-DvkCO3ko.js → editor.api-BNFb25CX.js} +1 -1
  15. package/dist/assets/{freemarker2-BA2lxsF1.js → freemarker2-0ZDnWxeM.js} +1 -1
  16. package/dist/assets/geo-u0n_jOsD.js +1 -0
  17. package/dist/assets/{handlebars-BcDiyPd8.js → handlebars-Cy18MoaD.js} +1 -1
  18. package/dist/assets/{html-C7O4TyhX.js → html-B-2Dx7nq.js} +1 -1
  19. package/dist/assets/{htmlMode-WaDGF3Mj.js → htmlMode-DUAc9B_d.js} +1 -1
  20. package/dist/assets/{image-DR4oeyJB.js → image-cAa-Nu9O.js} +1 -1
  21. package/dist/assets/{index-DyvDC6pS.js → index-5ZtDAuy2.js} +42 -41
  22. package/dist/assets/{javascript-CpIEK7tz.js → javascript-DeQsDqP5.js} +1 -1
  23. package/dist/assets/{jsonMode-B2FltW0f.js → jsonMode-BiFwJVSk.js} +1 -1
  24. package/dist/assets/{liquid-CN6FFHZE.js → liquid-BLoHH68a.js} +1 -1
  25. package/dist/assets/{lspLanguageFeatures-QhV9XtLp.js → lspLanguageFeatures-CNFgm1Kv.js} +1 -1
  26. package/dist/assets/math-DioIQOPJ.js +1 -0
  27. package/dist/assets/{mdx-DE89pafV.js → mdx-CFXQVNfs.js} +1 -1
  28. package/dist/assets/pop-DC_TQG9t.js +1 -0
  29. package/dist/assets/{python-ByTllTB9.js → python-Cf7S7p2E.js} +1 -1
  30. package/dist/assets/{razor-USYlRRCE.js → razor-CMHBcG-g.js} +1 -1
  31. package/dist/assets/{tsMode-BXpN8iUz.js → tsMode-BEih0GDj.js} +1 -1
  32. package/dist/assets/{typescript-DInBjGQQ.js → typescript-COEKYD_a.js} +1 -1
  33. package/dist/assets/{workers-C7Ye5B7D.js → workers-BNhAhMNn.js} +1 -1
  34. package/dist/assets/{xml-U0Uocqyo.js → xml-CPiMpnPl.js} +1 -1
  35. package/dist/assets/{yaml-CBw_PVYf.js → yaml-Bgp1ITLR.js} +1 -1
  36. package/dist/cli/Graph-ZJJTRQTM.js +2975 -0
  37. package/dist/cli/chunk-2KXB47FP.js +115 -0
  38. package/dist/cli/chunk-35D343DC.js +1447 -0
  39. package/dist/cli/chunk-36P5K3SD.js +631 -0
  40. package/dist/cli/chunk-3JTJHF7V.js +267 -0
  41. package/dist/cli/chunk-4UDFYF5G.js +132 -0
  42. package/dist/cli/chunk-53NMBJAO.js +35 -0
  43. package/dist/cli/chunk-A4OAMOZH.js +310 -0
  44. package/dist/cli/chunk-BIJO5OFS.js +71 -0
  45. package/dist/cli/chunk-BP7UKF36.js +376 -0
  46. package/dist/cli/chunk-CKS2FQWR.js +89 -0
  47. package/dist/cli/chunk-CO22XHNG.js +593 -0
  48. package/dist/cli/chunk-D6BUOHUG.js +634 -0
  49. package/dist/cli/chunk-DEQSEFFH.js +153 -0
  50. package/dist/cli/chunk-DUIUFVU2.js +241 -0
  51. package/dist/cli/chunk-EUIAC3U3.js +3006 -0
  52. package/dist/cli/chunk-FCDXJ6EK.js +2208 -0
  53. package/dist/cli/chunk-GFS6W3C5.js +308 -0
  54. package/dist/cli/chunk-HFHWYL55.js +139 -0
  55. package/dist/cli/chunk-HSNOBIVT.js +263 -0
  56. package/dist/cli/chunk-ICH7N5PN.js +26 -0
  57. package/dist/cli/chunk-J7VK7MNF.js +418 -0
  58. package/dist/cli/chunk-KH4MUBKF.js +1551 -0
  59. package/dist/cli/chunk-MIIEFUGG.js +623 -0
  60. package/dist/cli/chunk-OWWBYLHC.js +136 -0
  61. package/dist/cli/chunk-POMMNKZZ.js +672 -0
  62. package/dist/cli/chunk-QBBKRNGY.js +90 -0
  63. package/dist/cli/chunk-QQPA2ZHH.js +273 -0
  64. package/dist/cli/chunk-RLCAJBVR.js +224 -0
  65. package/dist/cli/chunk-RQZ4SX5R.js +1125 -0
  66. package/dist/cli/chunk-RUU3YWXU.js +99 -0
  67. package/dist/cli/chunk-RXDIMXN5.js +98 -0
  68. package/dist/cli/chunk-SGJAET2L.js +197 -0
  69. package/dist/cli/chunk-VVGSLVSF.js +151 -0
  70. package/dist/cli/chunk-WD62JIP6.js +118 -0
  71. package/dist/cli/chunk-WIH4TEIS.js +104 -0
  72. package/dist/cli/chunk-WKURDVYP.js +1119 -0
  73. package/dist/cli/chunk-WPUDGEFY.js +20 -0
  74. package/dist/cli/chunk-XWDSKG7N.js +1301 -0
  75. package/dist/cli/chunk-YAT5Y25U.js +121 -0
  76. package/dist/cli/chunk-ZL4JTG5Y.js +37 -0
  77. package/dist/cli/cliCommands-UQSFGEHF.js +3464 -0
  78. package/dist/cli/commands-Q23M26QM.js +1292 -0
  79. package/dist/cli/definitions-HF2FTHYU.js +32 -0
  80. package/dist/cli/doctorCommand-B7BPQMN6.js +76 -0
  81. package/dist/cli/edit-JHCPTQFT.js +28 -0
  82. package/dist/cli/editorCommands-UIF5YLUZ.js +33 -0
  83. package/dist/cli/encode-HAUZTYPT.js +17 -0
  84. package/dist/cli/frames-LSAUBZ7P.js +16 -0
  85. package/dist/cli/geo-4RNK3QVN.js +43 -0
  86. package/dist/cli/graphCommands-6BMAKGQE.js +184 -0
  87. package/dist/cli/graphFile-VPSQ6OF7.js +286 -0
  88. package/dist/cli/headlessCanvas-5XLDZT7M.js +15 -0
  89. package/dist/cli/helpCommand-3VRE3KVO.js +155 -0
  90. package/dist/cli/index.js +100 -30756
  91. package/dist/cli/math-ZRMLKU2D.js +16 -0
  92. package/dist/cli/mcp-ZLEEMYGF.js +164 -0
  93. package/dist/cli/parity-KINARCP2.js +328 -0
  94. package/dist/cli/pop-USDFXB7H.js +39 -0
  95. package/dist/cli/project-K3QIPGBB.js +12 -0
  96. package/dist/cli/projectDefinition-DMTZN2NE.js +27 -0
  97. package/dist/cli/projectRuntime-Z5PJOH47.js +48 -0
  98. package/dist/cli/registerStandardNodes-S2VLMF6J.js +3081 -0
  99. package/dist/cli/render-YURAPPUI.js +436 -0
  100. package/dist/cli/runCommand-PI764LEG.js +198 -0
  101. package/dist/cli/runner-RA6UKEPD.js +37 -0
  102. package/dist/cli/runtime/image.ts +277 -0
  103. package/dist/cli/runtime/stage.ts +4 -1
  104. package/dist/cli/stageRunner-KUNIMQKJ.js +10 -0
  105. package/dist/cli/verbs-RCE3K4VQ.js +19 -0
  106. package/dist/cli/webBuild-GT4PZT5D.js +37 -0
  107. package/dist/contracts/documentFormat.d.ts +18 -0
  108. package/dist/contracts/documentFormat.js +195 -0
  109. package/dist/contracts/editor.d.ts +17 -0
  110. package/dist/contracts/index.d.ts +1 -0
  111. package/dist/contracts/index.js +1 -0
  112. package/dist/image.d.ts +141 -0
  113. package/dist/image.js +127 -0
  114. package/dist/index.html +1 -1
  115. package/dist/player/editor.js +32 -7
  116. package/dist/runtime/builtins/headless.d.ts +19 -0
  117. package/dist/runtime/builtins/headless.js +25 -0
  118. package/dist/runtime/builtins/image/contactSheet.d.ts +226 -0
  119. package/dist/runtime/builtins/image/contactSheet.js +199 -0
  120. package/dist/runtime/builtins/image/index.d.ts +9 -0
  121. package/dist/runtime/builtins/image/index.js +9 -0
  122. package/dist/stage.js +4 -1
  123. package/doc/CLI_REFERENCE.md +73 -2
  124. package/doc/EDITOR_EXPORT.md +11 -2
  125. package/doc/MCP.md +17 -1
  126. package/doc/NODE_REFERENCE.md +142 -0
  127. package/doc/PROJECT_AUTHORING.md +127 -7
  128. package/package.json +16 -4
  129. package/dist/assets/ExportDialog-C7dVGua4.js +0 -358
  130. package/dist/assets/ExportDialog-DOq0flMV.css +0 -1
  131. package/dist/assets/geo-C2RHOVHC.js +0 -1
  132. package/dist/assets/math-DaDEgWKt.js +0 -1
  133. package/dist/assets/pop-pLH7L0PW.js +0 -1
@@ -0,0 +1,672 @@
1
+ // src/cli/verbs.ts
2
+ var graph = { type: "string", description: "Path to the .cascade file, relative to the project directory" };
3
+ var dryRun = { type: "boolean", description: "Validate without writing" };
4
+ var outNodes = { type: "array", items: { type: "string" }, description: "Mid-graph node ids whose image outputs are saved as well (--out-node)" };
5
+ var env = { type: "object", additionalProperties: { type: "string" }, description: "Environment variables for this run, its Python stages and workers (--env NAME=value)" };
6
+ var envArgs = (args) => Object.entries(args.env ?? {}).flatMap(([name, value]) => ["--env", `${name}=${String(value)}`]);
7
+ var object = (properties, required2 = []) => ({ type: "object", properties, required: required2, additionalProperties: false });
8
+ var edit = (operation) => ({ kind: "edit", operations: (args) => [operation(args)] });
9
+ var studio = { handler: "studio", help: "main", version: "main", section: "studio" };
10
+ var graphVerb = { handler: "graph", json: "envelope", help: "main", version: "handler", section: "graph" };
11
+ var lockVerb = { handler: "lock", json: "keep", help: "own", version: "handler", section: "locks" };
12
+ var runVerb = { handler: "run", help: "main", version: "main", section: "run" };
13
+ var editorVerb = { handler: "editor", json: "envelope", help: "main", version: "handler", section: "share" };
14
+ var VERBS = [
15
+ // Projects and Studio
16
+ { ...studio, name: "new", usage: "<name>", summary: "Create a Cascade project", json: "studio" },
17
+ { ...studio, name: "node", usage: "<Name> [project-directory]", summary: "Create a deterministic custom node", json: "envelope" },
18
+ { ...studio, name: "projects", usage: "[directory]", summary: "Show or set the default projects directory", json: "envelope" },
19
+ { ...studio, name: "serve", usage: "start|stop|restart|status|logs|install [project] [options]", summary: "Run Studio as a managed background service (cascade serve help)", json: "keep", help: "own", version: "handler" },
20
+ // Running
21
+ {
22
+ ...runVerb,
23
+ name: "run",
24
+ usage: "<graph-file> [options]",
25
+ summary: "Execute a graph file",
26
+ json: "keep",
27
+ mcp: [
28
+ {
29
+ name: "cook",
30
+ description: "Run a graph headlessly, optionally only the given node (cascade run). `data.images` lists each image output's file; `out` copies them there as <node>[.<port>].<ext>, `outNodes` adds mid-graph nodes.",
31
+ inputSchema: object({ graph, node: { type: "string" }, out: { type: "string", description: "Directory to save image outputs to, project-relative" }, outNodes, env, timeoutMs: { type: "number" } }, ["graph"]),
32
+ call: {
33
+ kind: "cli",
34
+ argv: (args, resolve) => [
35
+ "run",
36
+ resolve(args.graph),
37
+ ...args.node ? ["--node", args.node] : [],
38
+ ...args.out ? ["--out", resolve(args.out)] : [],
39
+ ...(args.outNodes ?? []).flatMap((node) => ["--out-node", node]),
40
+ ...envArgs(args)
41
+ ],
42
+ timeoutMs: (args) => args.timeoutMs
43
+ }
44
+ },
45
+ {
46
+ name: "render",
47
+ description: "Render frames to image files and return the manifest (cascade run --frames). `frames` is 1-100, 1-100x2 or 42.",
48
+ inputSchema: object({
49
+ graph,
50
+ frames: { type: "string" },
51
+ fps: { type: "number" },
52
+ out: { type: "string", description: "Output directory, project-relative (default renders)" },
53
+ nodes: { type: "array", items: { type: "string" } },
54
+ outNodes,
55
+ env,
56
+ scene: { type: "string", description: "A scene file to apply first" },
57
+ timeoutMs: { type: "number" }
58
+ }, ["graph", "frames"]),
59
+ call: {
60
+ kind: "cli",
61
+ argv: (args, resolve) => [
62
+ "run",
63
+ resolve(args.graph),
64
+ "--frames",
65
+ String(args.frames),
66
+ ...args.fps ? ["--fps", String(args.fps)] : [],
67
+ ...args.out ? ["--out", resolve(args.out)] : [],
68
+ ...args.scene ? ["--scene", resolve(args.scene)] : [],
69
+ ...(args.nodes ?? []).flatMap((node) => ["--node", node]),
70
+ ...(args.outNodes ?? []).flatMap((node) => ["--out-node", node]),
71
+ ...envArgs(args)
72
+ ],
73
+ timeoutMs: (args) => args.timeoutMs ?? 30 * 6e4
74
+ }
75
+ }
76
+ ]
77
+ },
78
+ { name: "render", usage: "<graph-file> [--frames <range>] [--node <id>] [--workers N] [--final] [--prores]", summary: "Render an animation across worker processes, then a preview video (cascade render --help)", section: "run", handler: "render", json: "keep", help: "own", version: "handler" },
79
+ { ...runVerb, name: "validate", usage: "<graph-file> [options]", summary: "Validate a graph file without executing", json: "envelope" },
80
+ {
81
+ ...runVerb,
82
+ name: "check",
83
+ usage: "<graph-file> [options]",
84
+ summary: "Statically check node definitions, types, and graph structure",
85
+ json: "envelope",
86
+ mcp: [{
87
+ name: "check",
88
+ description: "Statically check a graph: node definitions, port types and the host capabilities a run needs (cascade check).",
89
+ inputSchema: object({ graph, strict: { type: "boolean" } }, ["graph"]),
90
+ call: { kind: "cli", argv: (args, resolve) => ["check", resolve(args.graph), ...args.strict ? ["--strict"] : []] }
91
+ }]
92
+ },
93
+ {
94
+ ...runVerb,
95
+ name: "inspect",
96
+ usage: "<graph-file>",
97
+ summary: "Print a machine-readable graph and node-definition summary",
98
+ json: "envelope",
99
+ mcp: [{
100
+ name: "inspect",
101
+ description: "Every node with its classification and full definition (cascade inspect).",
102
+ inputSchema: object({ graph }, ["graph"]),
103
+ call: { kind: "cli", argv: (args, resolve) => ["inspect", resolve(args.graph)] }
104
+ }]
105
+ },
106
+ { name: "parity", usage: "<graph-file> [--node <id>] [--seed N] [--tolerance N] [--no-studio]", summary: "Cook headless twice and through Studio's path; report per-node differences", section: "run", handler: "parity", json: "keep", help: "main", version: "handler" },
107
+ // Sharing
108
+ { name: "build", usage: "<graph-file> --out <new-directory> [--asset <file>]...", summary: "Export a standalone browser player for static hosting or embedding", section: "share", handler: "build", json: "envelope", help: "main", version: "handler" },
109
+ {
110
+ ...editorVerb,
111
+ name: "export",
112
+ usage: "<graph-file> (--plan | --out <dir>) [--deploy vercel]",
113
+ summary: "Export the graph's published editor as a static site (doc/EDITOR_EXPORT.md)",
114
+ mcp: [{
115
+ name: "export",
116
+ description: "Export the graph's shareable editor (cascade export): `plan` lists what would ship, `out` writes it.",
117
+ inputSchema: object({ graph, plan: { type: "boolean" }, out: { type: "string" } }, ["graph"]),
118
+ call: { kind: "cli", argv: (args, resolve) => ["export", resolve(args.graph), ...args.out ? ["--out", resolve(args.out)] : ["--plan"]] }
119
+ }]
120
+ },
121
+ { ...editorVerb, name: "publish", usage: "<graph-file> <node>.<prop> [--label ...]", summary: "Publish a node prop as an editor parameter" },
122
+ { ...editorVerb, name: "freeze", usage: "<graph-file> <node> --fields <a,b>", summary: "Snapshot chosen fields of a live data source for export" },
123
+ { ...editorVerb, name: "scene", usage: "<graph-file> <scene.json> --out <new-graph-file>", summary: "Apply a scene file to a copy of the graph" },
124
+ // Agents
125
+ { name: "mcp", usage: "[project-directory]", summary: "Serve these commands as MCP tools over stdio (doc/MCP.md)", section: "agents", handler: "mcp", json: "envelope", help: "main", version: "handler" },
126
+ // Graph editing
127
+ {
128
+ ...graphVerb,
129
+ name: "graph create",
130
+ usage: "<graph> [--name <title>]",
131
+ summary: "Create a new, empty graph file; never overwrites",
132
+ options: { name: "value" },
133
+ mcp: [{
134
+ name: "graph_create",
135
+ description: "Create a new, empty .cascade graph file. Refuses to overwrite.",
136
+ inputSchema: object({ graph, name: { type: "string" } }, ["graph"]),
137
+ call: { kind: "graph", argv: (args, resolve) => ["graph", "create", resolve(args.graph), ...args.name ? ["--name", args.name] : []] }
138
+ }]
139
+ },
140
+ {
141
+ ...graphVerb,
142
+ name: "graph read",
143
+ usage: "<graph> [--in <subnet>]",
144
+ summary: "Nodes with stored parameters, subnets with their ports, and wires",
145
+ options: { in: "value" },
146
+ mcp: [{
147
+ name: "graph_read",
148
+ description: 'Read a graph: nodes with their stored parameters, subnets with their ports, and wires as "a.out -> b.in". Pass `in` to read one subnet.',
149
+ inputSchema: object({ graph, in: { type: "string", description: "A subnet id" } }, ["graph"]),
150
+ call: { kind: "graph", argv: (args, resolve) => ["graph", "read", resolve(args.graph), ...args.in ? ["--in", args.in] : []] }
151
+ }]
152
+ },
153
+ {
154
+ ...graphVerb,
155
+ name: "graph list",
156
+ usage: "[directory]",
157
+ summary: "Every .cascade under a directory, with node and wire counts",
158
+ mcp: [{
159
+ name: "graph_list",
160
+ description: "List the .cascade graphs in the project (or a subdirectory), with node and wire counts.",
161
+ inputSchema: object({ directory: { type: "string" } }),
162
+ call: { kind: "graph", argv: (args, resolve) => ["graph", "list", resolve(args.directory ?? ".")] }
163
+ }]
164
+ },
165
+ {
166
+ ...graphVerb,
167
+ name: "node add",
168
+ usage: "<graph> <type> [--name <id>] [--in <subnet>] [--set k=v]... [--at x,y] [--comment <text>]",
169
+ summary: "Add a node by module id or short name",
170
+ options: { name: "value", in: "value", set: "list", at: "value", comment: "value" },
171
+ operation: (operands, { values, lists }) => {
172
+ const module = required(operands[0], "cascade node add <graph> <type>");
173
+ const set = Object.fromEntries((lists.set ?? []).map((pair) => splitPair(pair, "--set", "=")));
174
+ return {
175
+ op: "node.add",
176
+ module,
177
+ ...values.name ? { id: values.name } : {},
178
+ ...values.in ? { in: values.in } : {},
179
+ ...values.at ? { position: point(values.at) } : {},
180
+ ...values.comment ? { comment: values.comment } : {},
181
+ ...Object.keys(set).length ? { set, fromText: true } : {}
182
+ };
183
+ },
184
+ mcp: [{
185
+ name: "node_add",
186
+ description: "Add a node. `type` is a module id (cascade.geo.Circle, project.InkVocabulary) or a short name (Circle, InkVocabulary). The node gets a stable kebab-case id unless `id` is given, and is placed next to what it is later wired to.",
187
+ inputSchema: object({
188
+ graph,
189
+ type: { type: "string" },
190
+ id: { type: "string" },
191
+ in: { type: "string", description: "Subnet to create the node in" },
192
+ set: { type: "object", description: "Parameter values by name, as JSON values" },
193
+ position: object({ x: { type: "number" }, y: { type: "number" } }, ["x", "y"]),
194
+ comment: { type: "string" },
195
+ dryRun
196
+ }, ["graph", "type"]),
197
+ call: edit((args) => ({
198
+ op: "node.add",
199
+ module: args.type,
200
+ ...args.id ? { id: args.id } : {},
201
+ ...args.in ? { in: args.in } : {},
202
+ ...args.set ? { set: args.set } : {},
203
+ ...args.position ? { position: args.position } : {},
204
+ ...args.comment ? { comment: args.comment } : {}
205
+ }))
206
+ }]
207
+ },
208
+ {
209
+ ...graphVerb,
210
+ name: "node rm",
211
+ aliases: ["node remove"],
212
+ usage: "<graph> <node> [--recursive]",
213
+ summary: "Remove a node and its wires",
214
+ options: { recursive: "flag" },
215
+ operation: (operands, { flags }) => ({ op: "node.remove", id: required(operands[0], "cascade node rm <graph> <node>"), recursive: flags.has("recursive") }),
216
+ mcp: [{
217
+ name: "node_remove",
218
+ description: "Remove a node and the wires touching it. A subnet with children needs `recursive`.",
219
+ inputSchema: object({ graph, id: { type: "string" }, recursive: { type: "boolean" }, dryRun }, ["graph", "id"]),
220
+ call: edit((args) => ({ op: "node.remove", id: args.id, recursive: args.recursive === true }))
221
+ }]
222
+ },
223
+ {
224
+ ...graphVerb,
225
+ name: "connect",
226
+ usage: "<graph> <node.output> <node.input> [--no-layout]",
227
+ summary: "Wire two ports",
228
+ options: { "no-layout": "flag" },
229
+ operation: (operands, { flags }) => ({
230
+ op: "connect",
231
+ from: required(operands[0], "cascade connect <graph> <node.output> <node.input>"),
232
+ to: required(operands[1], "cascade connect <graph> <node.output> <node.input>"),
233
+ ...flags.has("no-layout") ? { layout: false } : {}
234
+ }),
235
+ mcp: [{
236
+ name: "connect",
237
+ description: "Wire node.output to node.input. A port name may be omitted when the node has only one on that side. An input already fed is rewired. A node wired for the first time is moved next to its neighbour unless `layout` is false.",
238
+ inputSchema: object({ graph, from: { type: "string" }, to: { type: "string" }, layout: { type: "boolean" }, dryRun }, ["graph", "from", "to"]),
239
+ call: edit((args) => ({ op: "connect", from: args.from, to: args.to, ...args.layout === false ? { layout: false } : {} }))
240
+ }]
241
+ },
242
+ {
243
+ ...graphVerb,
244
+ name: "disconnect",
245
+ usage: "<graph> <node.output> <node.input> | <node.input>",
246
+ summary: "Remove a wire",
247
+ operation: (operands) => operands[1] ? { op: "disconnect", from: operands[0], to: operands[1] } : { op: "disconnect", to: required(operands[0], "cascade disconnect <graph> <node.output> <node.input>") },
248
+ mcp: [{
249
+ name: "disconnect",
250
+ description: "Remove the wire from `from` to `to`, or with only `to`, whatever feeds that input.",
251
+ inputSchema: object({ graph, from: { type: "string" }, to: { type: "string" }, dryRun }, ["graph", "to"]),
252
+ call: edit((args) => args.from ? { op: "disconnect", from: args.from, to: args.to } : { op: "disconnect", to: args.to })
253
+ }]
254
+ },
255
+ {
256
+ ...graphVerb,
257
+ name: "set",
258
+ usage: "<graph> <node.param> [<value>] [--expr <e> | --clear-expr]\n [--key <frame>=<value>]... [--remove-key <frame>]... [--interp constant|linear|smooth] [--unset]",
259
+ summary: "Set a parameter value, expression or keyframes",
260
+ options: { expr: "value", "clear-expr": "flag", key: "list", "remove-key": "list", interp: "value", unset: "flag" },
261
+ operation: (operands, { values, lists, flags }) => {
262
+ const target = required(operands[0], "cascade set <graph> <node.param> <value>");
263
+ const keys = (lists.key ?? []).map((pair) => {
264
+ const [frame, value] = splitPair(pair, "--key", "=");
265
+ return { frame: Number(frame), value: Number(value) };
266
+ });
267
+ const operation = {
268
+ op: "set",
269
+ target,
270
+ ...operands[1] !== void 0 ? { value: operands[1], fromText: true } : {},
271
+ ...values.expr !== void 0 ? { expression: values.expr } : {},
272
+ ...flags.has("clear-expr") ? { expression: null } : {},
273
+ ...keys.length ? { keys } : {},
274
+ ...lists["remove-key"]?.length ? { removeKeys: lists["remove-key"].map(Number) } : {},
275
+ ...values.interp ? { interpolation: interpolation(values.interp) } : {},
276
+ ...flags.has("unset") ? { unset: true } : {}
277
+ };
278
+ if (Object.keys(operation).length === 2) throw new Error("cascade set needs a value, --expr, --clear-expr, --key, --remove-key or --unset");
279
+ return operation;
280
+ },
281
+ mcp: [{
282
+ name: "set",
283
+ description: "Set node.param: a value (JSON, checked against the declared type), an expression (null removes it), keyframes, or `unset` to return to the default. A value equal to the default is not stored.",
284
+ inputSchema: object({
285
+ graph,
286
+ target: { type: "string", description: "node.param" },
287
+ value: {},
288
+ expression: { type: ["string", "null"] },
289
+ keys: { type: "array", items: object({ frame: { type: "number" }, value: { type: "number" } }, ["frame", "value"]) },
290
+ removeKeys: { type: "array", items: { type: "number" } },
291
+ interpolation: { enum: ["constant", "linear", "smooth"] },
292
+ unset: { type: "boolean" },
293
+ dryRun
294
+ }, ["graph", "target"]),
295
+ call: edit((args) => {
296
+ const { graph: _graph, dryRun: _dryRun, ...change } = args;
297
+ return { op: "set", ...change };
298
+ })
299
+ }]
300
+ },
301
+ {
302
+ ...graphVerb,
303
+ name: "subnet create",
304
+ usage: "<graph> <node>... [--name <id>] [--label <text>]",
305
+ summary: "Group sibling nodes into a subnet",
306
+ options: { name: "value", label: "value" },
307
+ operation: (operands, { values }) => ({
308
+ op: "subnet.create",
309
+ nodes: nonEmpty(operands, "cascade subnet create <graph> <node>..."),
310
+ ...values.name ? { id: values.name } : {},
311
+ ...values.label ? { label: values.label } : {}
312
+ }),
313
+ mcp: [{
314
+ name: "subnet_create",
315
+ description: "Group sibling nodes into a new subnet; wires crossing its boundary go through new Input/Output nodes named after the port they carry.",
316
+ inputSchema: object({ graph, nodes: { type: "array", items: { type: "string" } }, id: { type: "string" }, label: { type: "string" }, dryRun }, ["graph", "nodes"]),
317
+ call: edit((args) => ({ op: "subnet.create", nodes: args.nodes, ...args.id ? { id: args.id } : {}, ...args.label ? { label: args.label } : {} }))
318
+ }]
319
+ },
320
+ {
321
+ ...graphVerb,
322
+ name: "subnet move",
323
+ usage: "<graph> <node>... --to <subnet|root>",
324
+ summary: "Move sibling nodes into a subnet or to the top level",
325
+ options: { to: "value" },
326
+ operation: (operands, { values }) => ({ op: "subnet.move", nodes: nonEmpty(operands, "cascade subnet move <graph> <node>... --to <subnet|root>"), to: required(values.to, "--to <subnet|root>") }),
327
+ mcp: [{
328
+ name: "subnet_move",
329
+ description: 'Move sibling nodes into another subnet, or to "root", rewiring through the subnet boundaries on the way.',
330
+ inputSchema: object({ graph, nodes: { type: "array", items: { type: "string" } }, to: { type: "string" }, dryRun }, ["graph", "nodes", "to"]),
331
+ call: edit((args) => ({ op: "subnet.move", nodes: args.nodes, to: args.to }))
332
+ }]
333
+ },
334
+ {
335
+ ...graphVerb,
336
+ name: "subnet dissolve",
337
+ usage: "<graph> <subnet>",
338
+ summary: "Lift a subnet's nodes back out and remove it",
339
+ operation: (operands) => ({ op: "subnet.dissolve", id: required(operands[0], "cascade subnet dissolve <graph> <subnet>") }),
340
+ mcp: [{
341
+ name: "subnet_dissolve",
342
+ description: "Lift a subnet's nodes back into its parent and remove the subnet.",
343
+ inputSchema: object({ graph, id: { type: "string" }, dryRun }, ["graph", "id"]),
344
+ call: edit((args) => ({ op: "subnet.dissolve", id: args.id }))
345
+ }]
346
+ },
347
+ {
348
+ ...graphVerb,
349
+ name: "edit",
350
+ usage: "<graph> <operations.json | -> apply a batch, all or nothing",
351
+ summary: "Apply a batch of operations, all or nothing",
352
+ mcp: [{
353
+ name: "edit",
354
+ description: 'Apply several operations at once, all or nothing. Each is { "op": "node.add" | "node.remove" | "connect" | "disconnect" | "set" | "subnet.create" | "subnet.move" | "subnet.dissolve", ... } with the fields of the matching tool (node.add takes `module` and `in`).',
355
+ inputSchema: object({ graph, operations: { type: "array", items: { type: "object" } }, dryRun }, ["graph", "operations"]),
356
+ call: { kind: "edit", operations: (args) => args.operations }
357
+ }]
358
+ },
359
+ // Locks
360
+ {
361
+ ...lockVerb,
362
+ name: "lock",
363
+ usage: "<graph-file> <node> --name <name> [--note ...]",
364
+ summary: "Record a render (graph, params, seeds, inputs, code) in locks/<id>.json",
365
+ mcp: [{
366
+ name: "lock",
367
+ description: "Record a render as a lock (cascade lock): cooks `node`, writes locks/<id>.json with the graph, params, seeds, input hashes, stored code and output fingerprints.",
368
+ inputSchema: object({
369
+ graph,
370
+ node: { type: "string" },
371
+ name: { type: "string" },
372
+ note: { type: "string" },
373
+ scene: { type: "string" },
374
+ seed: { type: "integer" },
375
+ supersedes: { type: "string" },
376
+ preview: { type: "string" },
377
+ live: { type: "boolean" }
378
+ }, ["graph", "node", "name"]),
379
+ call: {
380
+ kind: "cli",
381
+ argv: (args, resolve) => [
382
+ "lock",
383
+ resolve(args.graph),
384
+ args.node,
385
+ "--name",
386
+ args.name,
387
+ ...args.note ? ["--note", args.note] : [],
388
+ ...args.scene ? ["--scene", resolve(args.scene)] : [],
389
+ ...args.seed !== void 0 ? ["--seed", String(args.seed)] : [],
390
+ ...args.supersedes ? ["--supersedes", args.supersedes] : [],
391
+ ...args.preview ? ["--preview", resolve(args.preview)] : [],
392
+ ...args.live ? ["--live"] : []
393
+ ],
394
+ timeoutMs: () => 60 * 6e4
395
+ }
396
+ }]
397
+ },
398
+ {
399
+ ...lockVerb,
400
+ name: "replay",
401
+ usage: "<lock-id> [--out <dir>]",
402
+ summary: "Re-render a lock from its stored code and compare fingerprints",
403
+ mcp: [{
404
+ name: "replay",
405
+ description: "Re-render a lock from its stored code and compare fingerprints (cascade replay). `data.status` is match or mismatch; `live` uses the current code.",
406
+ inputSchema: object({ lock: { type: "string" }, out: { type: "string" }, live: { type: "boolean" } }, ["lock"]),
407
+ call: {
408
+ kind: "cli",
409
+ argv: (args, resolve) => [
410
+ "replay",
411
+ String(args.lock).endsWith(".json") ? resolve(args.lock) : args.lock,
412
+ "--project",
413
+ resolve("."),
414
+ ...args.out ? ["--out", resolve(args.out)] : [],
415
+ ...args.live ? ["--live"] : []
416
+ ],
417
+ timeoutMs: () => 60 * 6e4
418
+ }
419
+ }]
420
+ },
421
+ { ...lockVerb, name: "lock verify", usage: "[<lock-id>...]", summary: "Replay locks and report which still match" },
422
+ {
423
+ ...lockVerb,
424
+ name: "locks",
425
+ usage: "",
426
+ summary: "List the project's locks (cascade lock --help for all options)",
427
+ mcp: [{
428
+ name: "locks",
429
+ description: "List the project's locks (cascade locks).",
430
+ inputSchema: object({}, []),
431
+ call: { kind: "cli", argv: (_args, resolve) => ["locks", resolve(".")] }
432
+ }]
433
+ },
434
+ {
435
+ ...lockVerb,
436
+ name: "ladder",
437
+ usage: "<graph-file> <node> --name <name> --vary <node.prop>=<a,b,c> | --seeds <a,b,c>",
438
+ summary: "Cook 3-5 variants, write a labelled contact sheet and ladders/<id>.json",
439
+ mcp: [{
440
+ name: "ladder",
441
+ description: "Cook `node` once per variant (cascade ladder) and return the contact sheet, each variant image and the record ladders/<id>.json. `vary` maps node.prop to a list of values (zipped; one value applies to all); `seeds` varies the graph seed. Three to five variants is the usual.",
442
+ inputSchema: object({
443
+ graph,
444
+ node: { type: "string" },
445
+ name: { type: "string" },
446
+ vary: { type: "object", description: "node.prop to an array of values, one per variant" },
447
+ seeds: { type: "array", items: { type: "integer" } },
448
+ columns: { type: "integer" },
449
+ note: { type: "string" },
450
+ live: { type: "boolean" }
451
+ }, ["graph", "node", "name"]),
452
+ call: {
453
+ kind: "cli",
454
+ argv: (args, resolve) => [
455
+ "ladder",
456
+ resolve(args.graph),
457
+ args.node,
458
+ "--name",
459
+ args.name,
460
+ ...Object.entries(args.vary ?? {}).flatMap(([key, values]) => [
461
+ "--vary",
462
+ `${key}=${(Array.isArray(values) ? values : [values]).map((value) => JSON.stringify(value)).join(",")}`
463
+ ]),
464
+ ...args.seeds?.length ? ["--seeds", args.seeds.join(",")] : [],
465
+ ...args.columns ? ["--columns", String(args.columns)] : [],
466
+ ...args.note ? ["--note", args.note] : [],
467
+ ...args.live ? ["--live"] : []
468
+ ],
469
+ timeoutMs: () => 60 * 6e4
470
+ }
471
+ }]
472
+ },
473
+ {
474
+ ...lockVerb,
475
+ name: "pick",
476
+ usage: "<ladder-id> <n> [--name <lock name>] [--note ...]",
477
+ summary: "Lock variant <n> of a ladder at approval (commit, seed, params, image)",
478
+ mcp: [{
479
+ name: "pick",
480
+ description: "Write the lock for variant `variant` of a ladder (cascade pick), from what the ladder recorded, without cooking again. `cascade replay` then reproduces that image.",
481
+ inputSchema: object({
482
+ ladder: { type: "string" },
483
+ variant: { type: "integer" },
484
+ name: { type: "string" },
485
+ note: { type: "string" },
486
+ supersedes: { type: "string" }
487
+ }, ["ladder", "variant"]),
488
+ call: {
489
+ kind: "cli",
490
+ argv: (args, resolve) => [
491
+ "pick",
492
+ String(args.ladder).endsWith(".json") ? resolve(args.ladder) : args.ladder,
493
+ String(args.variant),
494
+ "--project",
495
+ resolve("."),
496
+ ...args.name ? ["--name", args.name] : [],
497
+ ...args.note ? ["--note", args.note] : [],
498
+ ...args.supersedes ? ["--supersedes", args.supersedes] : []
499
+ ]
500
+ }
501
+ }]
502
+ },
503
+ // One of `cascade render --workers`' processes, forked by it with its spec.
504
+ { name: "__render-worker", usage: "<spec>", summary: "internal", section: "internal", handler: "renderWorker", json: "studio", help: "own", version: "handler" },
505
+ // A node's declaration from the installed package (helpCommand.ts).
506
+ {
507
+ name: "help node",
508
+ usage: "[<type>] [--project <dir>]",
509
+ summary: "Print a node type's inputs, props, outputs and description, or list every node type",
510
+ section: "agents",
511
+ handler: "help",
512
+ json: "keep",
513
+ help: "own",
514
+ version: "handler",
515
+ mcp: [{
516
+ name: "node_help",
517
+ description: "A node type's declaration: inputs, props and outputs with types, defaults and ranges, and its description (cascade help node). `type` is a module id or short name; without it, every node type this build and the project know.",
518
+ inputSchema: object({ type: { type: "string" } }, []),
519
+ call: { kind: "cli", argv: (args, resolve) => ["help", "node", ...args.type ? [String(args.type)] : [], "--project", resolve(".")] }
520
+ }]
521
+ },
522
+ // What a headless run here will find; --fix builds the declared Python environment (doctorCommand.ts).
523
+ {
524
+ name: "doctor",
525
+ usage: "[project-directory] [--fix]",
526
+ summary: "Check the project's Python environment, stage interpreter, ffmpeg and canvas; --fix builds the declared environment",
527
+ section: "run",
528
+ handler: "doctor",
529
+ json: "keep",
530
+ help: "own",
531
+ version: "handler",
532
+ mcp: [{
533
+ name: "doctor",
534
+ description: 'Check what a headless run in the project will find (cascade doctor): the declared Python environment (cascade.json "python"), the stage interpreter, ffmpeg and the canvas. `fix` creates or updates the environment in .cascade-cache/python/venv.',
535
+ inputSchema: object({ fix: { type: "boolean" } }, []),
536
+ call: { kind: "cli", argv: (args, resolve) => ["doctor", resolve("."), ...args.fix ? ["--fix"] : []], timeoutMs: () => 30 * 6e4 }
537
+ }]
538
+ }
539
+ ];
540
+ function verbFor(args) {
541
+ const [first, second] = args;
542
+ if (!first) return void 0;
543
+ const two = `${first} ${second ?? ""}`;
544
+ return VERBS.find((verb) => verb.name === two || verb.aliases?.includes(two)) ?? VERBS.find((verb) => verb.name === first) ?? VERBS.find((verb) => verb.name.split(" ")[0] === first);
545
+ }
546
+ function isGraphCommand(args) {
547
+ return verbFor(args)?.handler === "graph";
548
+ }
549
+ function graphOptions() {
550
+ const sets = { value: /* @__PURE__ */ new Set(), list: /* @__PURE__ */ new Set(), flag: /* @__PURE__ */ new Set(["dry-run", "json"]) };
551
+ for (const verb of VERBS) {
552
+ if (verb.handler !== "graph") continue;
553
+ for (const [name, kind] of Object.entries(verb.options ?? {})) sets[kind].add(name);
554
+ }
555
+ return sets;
556
+ }
557
+ function mcpTools() {
558
+ return VERBS.flatMap((verb) => (verb.mcp ?? []).map((tool) => ({ verb, tool })));
559
+ }
560
+ var usageLine = (verb) => ` cascade ${verb.name}${verb.usage ? ` ${verb.usage}` : ""}`;
561
+ function graphCommandsHelp() {
562
+ return `
563
+ Graph editing (every command takes --json; edits take --dry-run):
564
+ ${VERBS.filter((verb) => verb.section === "graph").map(usageLine).join("\n")}
565
+ `;
566
+ }
567
+ function mainHelp() {
568
+ const listed = VERBS.filter((verb) => verb.section !== "internal");
569
+ const before = listed.filter((verb) => verb.section !== "graph" && verb.section !== "locks" && verb.name !== "parity");
570
+ const after = listed.filter((verb) => verb.section === "locks" || verb.name === "parity");
571
+ const summaries = listed.filter((verb) => verb.section !== "graph");
572
+ const width = Math.max(...summaries.map((verb) => verb.name.length)) + 2;
573
+ return `
574
+ Cascade Graph Execution CLI
575
+
576
+ Usage:
577
+ cascade [project-directory | graph-file] [Studio options]
578
+ ${before.map(usageLine).join("\n")}
579
+ ${graphCommandsHelp()}
580
+ ${after.map(usageLine).join("\n")}
581
+
582
+ Commands:
583
+ ${summaries.map((verb) => ` ${verb.name.padEnd(width)}${verb.summary}`).join("\n")}
584
+
585
+ Options:
586
+ --host <address> Bind Studio to an address or hostname
587
+ --port <number> Bind Studio HTTP to an explicit port
588
+ --trusted-host <name> Allow an exact remote browser hostname (repeatable)
589
+ --fallback-host <addr> Bind this instead when --host does not resolve or is
590
+ not on this machine (repeatable; default 127.0.0.1).
591
+ The preferred host is retried and rebound when it returns
592
+ --no-open Do not open Studio in a browser
593
+ --node <id> Render only this node's output. Repeatable.
594
+ --entry-node <id> Older spelling of --node, single valued
595
+ --frames <range> Render a frame sequence: 1-100, 1-100x2 (step), 42, or
596
+ timeline (the range saved with the graph)
597
+ --fps <number> Frame rate to evaluate the range at (default: the graph's timeline fps)
598
+ --mp4 <file> After --frames, encode the sequence as H.264 yuv420p (needs ffmpeg)
599
+ --crf <n> Quality for --mp4, 0-51, lower is better (default 18)
600
+ --out <dir> Save image outputs here, project-relative or absolute: the
601
+ sequence with --frames (default: renders), else the still
602
+ --out-node <id> Also save this (mid-graph) node's image outputs. Repeatable.
603
+ --env NAME=value Set a variable for the run and its Python stages. Repeatable.
604
+ --json Print one { ok, data, warnings } envelope (any command)
605
+ --seed <n> Cook with this graph seed instead of the document's
606
+ --fresh With --frames, cook every node on every frame (default: only what the frame changed)
607
+ --timeout <ms> Bound a run in a supervised child process (exit 124 on timeout)
608
+ --validate-only Validate only (same as 'validate' command)
609
+ --strict Fail when a node names a module with no file (default: warn)
610
+ --verbose, -v Show verbose output
611
+ --version Show version
612
+ --help, -h Show this help message
613
+
614
+ Examples:
615
+ cascade .
616
+ cascade . --host KURO --port 3030
617
+ cascade new my-artwork
618
+ cascade node Multiply ./my-artwork
619
+ cascade run graph.cascade
620
+ cascade run graph.cascade --entry-node node_123
621
+ cascade validate graph.cascade
622
+ cascade run graph.cascade --frames 1-100
623
+ cascade run graph.cascade --frames 1-100x2 --fps 25 --out frames
624
+ cascade run graph.cascade --verbose
625
+
626
+ A frame sequence is written as <out>/<node id>.<frame>.<ext> \u2014 one file per
627
+ image output nothing downstream consumes (a Null passing an image counts), or
628
+ per output of --node, plus those of each --out-node. A node with several image
629
+ outputs adds the port: <node id>.<port>.<frame>.<ext>. Without --frames, --out
630
+ writes <out>/<node id>[.<port>].<ext>, and run --json lists every image output
631
+ in data.images either way.
632
+
633
+ Every run writes a trace to .cascade/runs/: each node's time and every Python
634
+ stage call with its cache key and state (hit, miss, off), and what each node
635
+ and its stages printed while it cooked (also in run --json as data.logs). A
636
+ failed run writes its trace too, and names it in the error. --verbose prints
637
+ the stage lines as they happen.
638
+ `;
639
+ }
640
+ function required(value, usage) {
641
+ if (!value) throw new Error(`usage: ${usage}`);
642
+ return value;
643
+ }
644
+ function nonEmpty(values, usage) {
645
+ if (!values.length) throw new Error(`usage: ${usage}`);
646
+ return [...values];
647
+ }
648
+ function splitPair(pair, flag, separator) {
649
+ const at = pair.indexOf(separator);
650
+ if (at <= 0) throw new Error(`${flag} takes name${separator}value (got "${pair}")`);
651
+ return [pair.slice(0, at), pair.slice(at + 1)];
652
+ }
653
+ function point(text) {
654
+ const [x, y] = text.split(",").map(Number);
655
+ if (!Number.isFinite(x) || !Number.isFinite(y)) throw new Error(`--at takes x,y (got "${text}")`);
656
+ return { x, y };
657
+ }
658
+ function interpolation(text) {
659
+ if (text === "constant" || text === "linear" || text === "smooth") return text;
660
+ throw new Error(`--interp is constant, linear or smooth (got "${text}")`);
661
+ }
662
+
663
+ export {
664
+ VERBS,
665
+ verbFor,
666
+ isGraphCommand,
667
+ graphOptions,
668
+ mcpTools,
669
+ graphCommandsHelp,
670
+ mainHelp
671
+ };
672
+ //# sourceMappingURL=chunk-POMMNKZZ.js.map