@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.
- package/AGENTS.md +25 -5
- package/ARCHITECTURE.md +5 -4
- package/CHANGELOG.md +382 -0
- package/README.md +1 -2
- package/dist/assets/AgentPanel-1JugFjE4.css +1 -0
- package/dist/assets/AgentPanel-BDL5MddA.js +5 -0
- package/dist/assets/{CodeEditor-DSZBpzdy.js → CodeEditor-C-xYG-Wn.js} +120 -120
- package/dist/assets/CodePanel-CtXd50m6.js +2 -0
- package/dist/assets/CookInfoPanel-B3NMhKJF.css +1 -0
- package/dist/assets/CookInfoPanel-DG4_TRsX.js +2 -0
- package/dist/assets/CustomNodeDialog-CHfZUs-r.js +1 -0
- package/dist/assets/DefinitionNode-CaRTXQ_b.js +1 -0
- package/dist/assets/DefinitionPanel-C3CxZY9r.js +1 -0
- package/dist/assets/DefinitionPanel-VNLnAiQD.css +1 -0
- package/dist/assets/OpenGraphDialog-BLdr6k_q.js +1 -0
- package/dist/assets/ProjectSettings-ZnvmIAir.js +2 -0
- package/dist/assets/{SettingsDialog-p25h3tj0.js → SettingsDialog-CBX3Wa87.js} +1 -1
- package/dist/assets/TimelinePanel-CDeR0arY.css +1 -0
- package/dist/assets/TimelinePanel-CWgnFH1e.js +1 -0
- package/dist/assets/{VersionHistoryDialog-DzFLUotB.js → VersionHistoryDialog-BOSoW1R-.js} +1 -1
- package/dist/assets/assert-BO2_erPD.js +1 -0
- package/dist/assets/assert-hvykGCnP.js +1 -0
- package/dist/assets/builder-BmBKcDix.js +1 -0
- package/dist/assets/camera-DFF7YA3F.js +1 -0
- package/dist/assets/checks-DAvyO9Gf.js +1 -0
- package/dist/assets/contactSheet-N1NcaWxw.js +2 -0
- package/dist/assets/{core-BICrEt0y.js → core-DB-MkGjj.js} +3 -4
- package/dist/assets/coreTypes-DtnDATZ7.js +1 -0
- package/dist/assets/{cssMode-D7TXw57I.js → cssMode-DPVckSOc.js} +1 -1
- package/dist/assets/definitions-B227e7f_.js +1 -0
- package/dist/assets/definitions-D0WOUJd7.js +1 -0
- package/dist/assets/{editor.api-DvkCO3ko.js → editor.api-Dkutg_Z1.js} +1 -1
- package/dist/assets/expressions-X-Z-GrgW.js +5 -0
- package/dist/assets/file-DAL6IfjV.js +1 -0
- package/dist/assets/filters-D3k1LRMa.js +1 -0
- package/dist/assets/{freemarker2-BA2lxsF1.js → freemarker2-CLiLKk3B.js} +1 -1
- package/dist/assets/generators-BhDxRAFC.js +1 -0
- package/dist/assets/geo-BsMImJIJ.js +1 -0
- package/dist/assets/geo-C4YngDob.js +2 -0
- package/dist/assets/{handlebars-BcDiyPd8.js → handlebars-CcD3aMeL.js} +1 -1
- package/dist/assets/{html-C7O4TyhX.js → html-DvZWGfqb.js} +1 -1
- package/dist/assets/{htmlMode-WaDGF3Mj.js → htmlMode-DUD3VaoN.js} +1 -1
- package/dist/assets/image-0SfH-snh.js +1 -0
- package/dist/assets/image-Ck-RdGU4.js +2 -0
- package/dist/assets/index-BWnrmIB9.js +137 -0
- package/dist/assets/index-DIaxEONI.css +1 -0
- package/dist/assets/io-BGmt2g0D.js +1 -0
- package/dist/assets/{javascript-CpIEK7tz.js → javascript-Cap2ahgh.js} +1 -1
- package/dist/assets/{jsonMode-B2FltW0f.js → jsonMode-B0uLjp9C.js} +1 -1
- package/dist/assets/{liquid-CN6FFHZE.js → liquid-CnxwhfLn.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-QhV9XtLp.js → lspLanguageFeatures-Dn9hlKX6.js} +1 -1
- package/dist/assets/math-GeULS-sk.js +1 -0
- package/dist/assets/{mdx-DE89pafV.js → mdx-BV3jbrVa.js} +1 -1
- package/dist/assets/pop-KMF_qloi.js +1 -0
- package/dist/assets/preload-helper-Czpn1I53.js +1 -0
- package/dist/assets/{python-ByTllTB9.js → python-CGyFk4XK.js} +1 -1
- package/dist/assets/{razor-USYlRRCE.js → razor-CQfzX3-N.js} +1 -1
- package/dist/assets/remap-5SjXQU-q.js +1 -0
- package/dist/assets/renderMarkdown-ork0Oir7.js +67 -0
- package/dist/assets/studioImages-CAyH-0Pl.js +1 -0
- package/dist/assets/text-ZpX7qNkx.js +2 -0
- package/dist/assets/time-P_OiBhcf.js +1 -0
- package/dist/assets/{tsMode-BXpN8iUz.js → tsMode-BOeXzO9H.js} +1 -1
- package/dist/assets/{typescript-DInBjGQQ.js → typescript-J1SnE_Tu.js} +1 -1
- package/dist/assets/{workers-C7Ye5B7D.js → workers-CYwwTiVF.js} +1 -1
- package/dist/assets/{xml-U0Uocqyo.js → xml-DnPRNzK9.js} +1 -1
- package/dist/assets/{yaml-CBw_PVYf.js → yaml-BmmYBNa9.js} +1 -1
- package/dist/cli/DefinitionNode-P7AA4TAU.js +15 -0
- package/dist/cli/Graph-OB6EWSGL.js +2975 -0
- package/dist/cli/assert-GROGQNCQ.js +25 -0
- package/dist/cli/checks-3WB44XBL.js +189 -0
- package/dist/cli/chunk-2JEU3MGB.js +1125 -0
- package/dist/cli/chunk-2KXB47FP.js +115 -0
- package/dist/cli/chunk-3RXL5GHB.js +816 -0
- package/dist/cli/chunk-53LGFPWX.js +138 -0
- package/dist/cli/chunk-5ORUQ6ES.js +267 -0
- package/dist/cli/chunk-73S4JZXK.js +336 -0
- package/dist/cli/chunk-7QE2ZNSN.js +121 -0
- package/dist/cli/chunk-BP7UKF36.js +376 -0
- package/dist/cli/chunk-BPODRH52.js +201 -0
- package/dist/cli/chunk-BZ63XQ3I.js +1301 -0
- package/dist/cli/chunk-CKS2FQWR.js +89 -0
- package/dist/cli/chunk-CO22XHNG.js +593 -0
- package/dist/cli/chunk-CTPLRSWF.js +2197 -0
- package/dist/cli/chunk-DEQSEFFH.js +153 -0
- package/dist/cli/chunk-EU4ZFZ7Y.js +122 -0
- package/dist/cli/chunk-EZRWJ4G2.js +104 -0
- package/dist/cli/chunk-FWMSGKEH.js +71 -0
- package/dist/cli/chunk-G23A2BZZ.js +705 -0
- package/dist/cli/chunk-GRIN2DDP.js +1465 -0
- package/dist/cli/chunk-HFHWYL55.js +139 -0
- package/dist/cli/chunk-HSNOBIVT.js +263 -0
- package/dist/cli/chunk-ICH7N5PN.js +26 -0
- package/dist/cli/chunk-J7VK7MNF.js +418 -0
- package/dist/cli/chunk-JVOCK5W4.js +83 -0
- package/dist/cli/chunk-KBGUO27Q.js +15 -0
- package/dist/cli/chunk-KH4MUBKF.js +1551 -0
- package/dist/cli/chunk-L6B6FUQP.js +330 -0
- package/dist/cli/chunk-LTXWBXOM.js +438 -0
- package/dist/cli/chunk-MI5JLQYA.js +220 -0
- package/dist/cli/chunk-MP653A5O.js +3766 -0
- package/dist/cli/chunk-NJFMDXHR.js +320 -0
- package/dist/cli/chunk-OM6BEME6.js +267 -0
- package/dist/cli/chunk-OWWBYLHC.js +136 -0
- package/dist/cli/chunk-QQPA2ZHH.js +273 -0
- package/dist/cli/chunk-RUU3YWXU.js +99 -0
- package/dist/cli/chunk-RXDIMXN5.js +98 -0
- package/dist/cli/chunk-ST6LROUG.js +267 -0
- package/dist/cli/chunk-TJW67QSI.js +225 -0
- package/dist/cli/chunk-WD62JIP6.js +118 -0
- package/dist/cli/chunk-WGKPJZDY.js +364 -0
- package/dist/cli/chunk-X4WKAOQP.js +634 -0
- package/dist/cli/chunk-X566FWBV.js +156 -0
- package/dist/cli/chunk-YH637PDL.js +317 -0
- package/dist/cli/chunk-ZL4JTG5Y.js +37 -0
- package/dist/cli/chunk-ZSCUZE6I.js +112 -0
- package/dist/cli/cliCommands-JJN5Z3ZC.js +3470 -0
- package/dist/cli/commands-PHYFYA3F.js +1374 -0
- package/dist/cli/contactSheet-2PQVRUFW.js +11 -0
- package/dist/cli/definitions-BPOPW52L.js +32 -0
- package/dist/cli/doctorCommand-ORWQRNYP.js +106 -0
- package/dist/cli/edit-S5KEQ5RA.js +28 -0
- package/dist/cli/editorCommands-NFR32JOY.js +41 -0
- package/dist/cli/encode-ICPEPMH5.js +17 -0
- package/dist/cli/file-3HTQHPYQ.js +41 -0
- package/dist/cli/filters-6ODFWYK4.js +379 -0
- package/dist/cli/frames-LSAUBZ7P.js +16 -0
- package/dist/cli/generators-YU7GB4MD.js +210 -0
- package/dist/cli/geo-6ZPGKZRU.js +43 -0
- package/dist/cli/graphCommands-EOUOBOIJ.js +184 -0
- package/dist/cli/graphFile-YTGJB5F5.js +292 -0
- package/dist/cli/headlessCanvas-ENRHC7GS.js +16 -0
- package/dist/cli/helpCommand-PULH6ZYB.js +157 -0
- package/dist/cli/image-TXELZZK5.js +55 -0
- package/dist/cli/index.js +100 -30756
- package/dist/cli/math-RSYRQ3C6.js +16 -0
- package/dist/cli/mcp-JVZFQOEX.js +164 -0
- package/dist/cli/parity-X6DEH2DV.js +334 -0
- package/dist/cli/pop-B7SIIYKN.js +39 -0
- package/dist/cli/project-VYGIA7JX.js +12 -0
- package/dist/cli/projectDefinition-F3D7UIHJ.js +27 -0
- package/dist/cli/projectRuntime-2Q4JKEH3.js +55 -0
- package/dist/cli/registerStandardNodes-BXJCB6E4.js +744 -0
- package/dist/cli/render-PT4MWI6U.js +438 -0
- package/dist/cli/runCommand-HNPQVZMS.js +198 -0
- package/dist/cli/runner-5XPXVW7J.js +43 -0
- package/dist/cli/runtime/image.ts +302 -0
- package/dist/cli/runtime/io.ts +78 -0
- package/dist/cli/runtime/stage.ts +4 -1
- package/dist/cli/share-MHF6TNRK.js +166 -0
- package/dist/cli/stageCache-TY2BFZJR.js +15 -0
- package/dist/cli/stageRunner-KY2EJP7X.js +10 -0
- package/dist/cli/text-MB3EKR4H.js +126 -0
- package/dist/cli/verbs-PI2JH5FI.js +19 -0
- package/dist/cli/webBuild-WPPMGGW7.js +44 -0
- package/dist/contracts/capabilities.d.ts +18 -1
- package/dist/contracts/color.d.ts +8 -0
- package/dist/contracts/color.js +23 -0
- package/dist/contracts/definition.d.ts +10 -0
- package/dist/contracts/documentFormat.d.ts +18 -0
- package/dist/contracts/documentFormat.js +195 -0
- package/dist/contracts/editor.d.ts +17 -0
- package/dist/contracts/index.d.ts +1 -0
- package/dist/contracts/index.js +1 -0
- package/dist/contracts/schema.d.ts +1 -1
- package/dist/contracts/schema.js +1 -0
- package/dist/contracts/validate.js +11 -1
- package/dist/contracts/values.d.ts +24 -0
- package/dist/image.d.ts +156 -0
- package/dist/image.js +161 -0
- package/dist/index.html +8 -2
- package/dist/io.d.ts +29 -0
- package/dist/io.js +40 -0
- package/dist/player/app.js +3 -2
- package/dist/player/editor.js +35 -9
- package/dist/player/editorEngine.js +3 -2
- package/dist/runtime/builtins/assert/checks.d.ts +18 -0
- package/dist/runtime/builtins/assert/checks.js +207 -0
- package/dist/runtime/builtins/assert/definitions.d.ts +130 -0
- package/dist/runtime/builtins/assert/definitions.js +54 -0
- package/dist/runtime/builtins/assert/index.d.ts +152 -0
- package/dist/runtime/builtins/assert/index.js +36 -0
- package/dist/runtime/builtins/image/contactSheet.d.ts +64 -0
- package/dist/runtime/builtins/image/contactSheet.js +150 -0
- package/dist/runtime/builtins/image/definitions.d.ts +950 -0
- package/dist/runtime/builtins/image/definitions.js +343 -0
- package/dist/runtime/builtins/image/file.d.ts +8 -0
- package/dist/runtime/builtins/image/file.js +37 -0
- package/dist/runtime/builtins/image/filters.d.ts +8 -0
- package/dist/runtime/builtins/image/filters.js +394 -0
- package/dist/runtime/builtins/image/generators.d.ts +7 -0
- package/dist/runtime/builtins/image/generators.js +144 -0
- package/dist/runtime/builtins/image/index.d.ts +952 -0
- package/dist/runtime/builtins/image/index.js +44 -0
- package/dist/runtime/builtins/image/io.d.ts +100 -0
- package/dist/runtime/builtins/image/io.js +142 -0
- package/dist/runtime/builtins/image/planar.d.ts +55 -0
- package/dist/runtime/builtins/image/planar.js +274 -0
- package/dist/runtime/builtins/image/simplex.d.ts +22 -0
- package/dist/runtime/builtins/image/simplex.js +92 -0
- package/dist/runtime/builtins/image/text.d.ts +4 -0
- package/dist/runtime/builtins/image/text.js +141 -0
- package/dist/runtime/builtins/index.d.ts +3 -0
- package/dist/runtime/builtins/index.js +7 -0
- package/dist/runtime/gpu.d.ts +25 -0
- package/dist/runtime/gpu.js +40 -0
- package/dist/runtime/runtime.d.ts +2 -0
- package/dist/runtime/runtime.js +268 -44
- package/dist/runtime/types.d.ts +30 -0
- package/dist/stage.js +4 -1
- package/doc/CLI_REFERENCE.md +91 -2
- package/doc/EDITOR_EXPORT.md +37 -2
- package/doc/MCP.md +18 -1
- package/doc/NODE_REFERENCE.md +424 -10
- package/doc/PROJECT_AUTHORING.md +222 -7
- package/package.json +19 -4
- package/dist/assets/CodePanel-sY6sZa7B.js +0 -2
- package/dist/assets/CustomNodeDialog-BRKnqZhp.js +0 -1
- package/dist/assets/ExportDialog-C7dVGua4.js +0 -358
- package/dist/assets/ExportDialog-DOq0flMV.css +0 -1
- package/dist/assets/OpenGraphDialog-ZsgL-iwr.js +0 -1
- package/dist/assets/ProjectSettings-Cw17nNe3.js +0 -2
- package/dist/assets/definitions-DUww2Rt-.js +0 -1
- package/dist/assets/geo-C2RHOVHC.js +0 -1
- package/dist/assets/image-DR4oeyJB.js +0 -2062
- package/dist/assets/index-7vae6YFC.css +0 -1
- package/dist/assets/index-DyvDC6pS.js +0 -219
- package/dist/assets/math-DaDEgWKt.js +0 -1
- package/dist/assets/pop-pLH7L0PW.js +0 -1
|
@@ -0,0 +1,705 @@
|
|
|
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
|
+
{
|
|
122
|
+
...editorVerb,
|
|
123
|
+
name: "share",
|
|
124
|
+
json: "keep",
|
|
125
|
+
usage: "<graph-file | site-directory> [--name <slug>] [--scope <team>] [--protected]",
|
|
126
|
+
summary: "Export and deploy to Vercel as cascade-<slug>; print the link and record it in shares.json",
|
|
127
|
+
mcp: [{
|
|
128
|
+
name: "share",
|
|
129
|
+
description: "Share a graph's editor, or a static directory with an index.html, as one link (cascade share): export, deploy to the Vercel project cascade-<name> and return `url`, recorded in the project's shares.json. Public unless `protected`.",
|
|
130
|
+
inputSchema: object({
|
|
131
|
+
graph: { type: "string", description: "The .cascade file or site directory, relative to the project directory" },
|
|
132
|
+
name: { type: "string", description: "Slug; the Vercel project is cascade-<name> (default: the editor id or directory name)" },
|
|
133
|
+
scope: { type: "string", description: "Vercel team (default field-io)" },
|
|
134
|
+
protected: { type: "boolean", description: "Require Vercel Authentication instead of a public link" }
|
|
135
|
+
}, ["graph"]),
|
|
136
|
+
call: {
|
|
137
|
+
kind: "cli",
|
|
138
|
+
argv: (args, resolve) => [
|
|
139
|
+
"share",
|
|
140
|
+
resolve(args.graph),
|
|
141
|
+
...args.name ? ["--name", String(args.name)] : [],
|
|
142
|
+
...args.scope ? ["--scope", String(args.scope)] : [],
|
|
143
|
+
...args.protected ? ["--protected"] : []
|
|
144
|
+
],
|
|
145
|
+
timeoutMs: () => 10 * 6e4
|
|
146
|
+
}
|
|
147
|
+
}]
|
|
148
|
+
},
|
|
149
|
+
{ ...editorVerb, name: "publish", usage: "<graph-file> <node>.<prop> [--label ...]", summary: "Publish a node prop as an editor parameter" },
|
|
150
|
+
{ ...editorVerb, name: "freeze", usage: "<graph-file> <node> --fields <a,b>", summary: "Snapshot chosen fields of a live data source for export" },
|
|
151
|
+
{ ...editorVerb, name: "scene", usage: "<graph-file> <scene.json> --out <new-graph-file>", summary: "Apply a scene file to a copy of the graph" },
|
|
152
|
+
// Agents
|
|
153
|
+
{ 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" },
|
|
154
|
+
// Graph editing
|
|
155
|
+
{
|
|
156
|
+
...graphVerb,
|
|
157
|
+
name: "graph create",
|
|
158
|
+
usage: "<graph> [--name <title>]",
|
|
159
|
+
summary: "Create a new, empty graph file; never overwrites",
|
|
160
|
+
options: { name: "value" },
|
|
161
|
+
mcp: [{
|
|
162
|
+
name: "graph_create",
|
|
163
|
+
description: "Create a new, empty .cascade graph file. Refuses to overwrite.",
|
|
164
|
+
inputSchema: object({ graph, name: { type: "string" } }, ["graph"]),
|
|
165
|
+
call: { kind: "graph", argv: (args, resolve) => ["graph", "create", resolve(args.graph), ...args.name ? ["--name", args.name] : []] }
|
|
166
|
+
}]
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
...graphVerb,
|
|
170
|
+
name: "graph read",
|
|
171
|
+
usage: "<graph> [--in <subnet>]",
|
|
172
|
+
summary: "Nodes with stored parameters, subnets with their ports, and wires",
|
|
173
|
+
options: { in: "value" },
|
|
174
|
+
mcp: [{
|
|
175
|
+
name: "graph_read",
|
|
176
|
+
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.',
|
|
177
|
+
inputSchema: object({ graph, in: { type: "string", description: "A subnet id" } }, ["graph"]),
|
|
178
|
+
call: { kind: "graph", argv: (args, resolve) => ["graph", "read", resolve(args.graph), ...args.in ? ["--in", args.in] : []] }
|
|
179
|
+
}]
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
...graphVerb,
|
|
183
|
+
name: "graph list",
|
|
184
|
+
usage: "[directory]",
|
|
185
|
+
summary: "Every .cascade under a directory, with node and wire counts",
|
|
186
|
+
mcp: [{
|
|
187
|
+
name: "graph_list",
|
|
188
|
+
description: "List the .cascade graphs in the project (or a subdirectory), with node and wire counts.",
|
|
189
|
+
inputSchema: object({ directory: { type: "string" } }),
|
|
190
|
+
call: { kind: "graph", argv: (args, resolve) => ["graph", "list", resolve(args.directory ?? ".")] }
|
|
191
|
+
}]
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
...graphVerb,
|
|
195
|
+
name: "node add",
|
|
196
|
+
usage: "<graph> <type> [--name <id>] [--in <subnet>] [--set k=v]... [--at x,y] [--comment <text>]",
|
|
197
|
+
summary: "Add a node by module id or short name",
|
|
198
|
+
options: { name: "value", in: "value", set: "list", at: "value", comment: "value" },
|
|
199
|
+
operation: (operands, { values, lists }) => {
|
|
200
|
+
const module = required(operands[0], "cascade node add <graph> <type>");
|
|
201
|
+
const set = Object.fromEntries((lists.set ?? []).map((pair) => splitPair(pair, "--set", "=")));
|
|
202
|
+
return {
|
|
203
|
+
op: "node.add",
|
|
204
|
+
module,
|
|
205
|
+
...values.name ? { id: values.name } : {},
|
|
206
|
+
...values.in ? { in: values.in } : {},
|
|
207
|
+
...values.at ? { position: point(values.at) } : {},
|
|
208
|
+
...values.comment ? { comment: values.comment } : {},
|
|
209
|
+
...Object.keys(set).length ? { set, fromText: true } : {}
|
|
210
|
+
};
|
|
211
|
+
},
|
|
212
|
+
mcp: [{
|
|
213
|
+
name: "node_add",
|
|
214
|
+
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.",
|
|
215
|
+
inputSchema: object({
|
|
216
|
+
graph,
|
|
217
|
+
type: { type: "string" },
|
|
218
|
+
id: { type: "string" },
|
|
219
|
+
in: { type: "string", description: "Subnet to create the node in" },
|
|
220
|
+
set: { type: "object", description: "Parameter values by name, as JSON values" },
|
|
221
|
+
position: object({ x: { type: "number" }, y: { type: "number" } }, ["x", "y"]),
|
|
222
|
+
comment: { type: "string" },
|
|
223
|
+
dryRun
|
|
224
|
+
}, ["graph", "type"]),
|
|
225
|
+
call: edit((args) => ({
|
|
226
|
+
op: "node.add",
|
|
227
|
+
module: args.type,
|
|
228
|
+
...args.id ? { id: args.id } : {},
|
|
229
|
+
...args.in ? { in: args.in } : {},
|
|
230
|
+
...args.set ? { set: args.set } : {},
|
|
231
|
+
...args.position ? { position: args.position } : {},
|
|
232
|
+
...args.comment ? { comment: args.comment } : {}
|
|
233
|
+
}))
|
|
234
|
+
}]
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
...graphVerb,
|
|
238
|
+
name: "node rm",
|
|
239
|
+
aliases: ["node remove"],
|
|
240
|
+
usage: "<graph> <node> [--recursive]",
|
|
241
|
+
summary: "Remove a node and its wires",
|
|
242
|
+
options: { recursive: "flag" },
|
|
243
|
+
operation: (operands, { flags }) => ({ op: "node.remove", id: required(operands[0], "cascade node rm <graph> <node>"), recursive: flags.has("recursive") }),
|
|
244
|
+
mcp: [{
|
|
245
|
+
name: "node_remove",
|
|
246
|
+
description: "Remove a node and the wires touching it. A subnet with children needs `recursive`.",
|
|
247
|
+
inputSchema: object({ graph, id: { type: "string" }, recursive: { type: "boolean" }, dryRun }, ["graph", "id"]),
|
|
248
|
+
call: edit((args) => ({ op: "node.remove", id: args.id, recursive: args.recursive === true }))
|
|
249
|
+
}]
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
...graphVerb,
|
|
253
|
+
name: "connect",
|
|
254
|
+
usage: "<graph> <node.output> <node.input> [--no-layout]",
|
|
255
|
+
summary: "Wire two ports",
|
|
256
|
+
options: { "no-layout": "flag" },
|
|
257
|
+
operation: (operands, { flags }) => ({
|
|
258
|
+
op: "connect",
|
|
259
|
+
from: required(operands[0], "cascade connect <graph> <node.output> <node.input>"),
|
|
260
|
+
to: required(operands[1], "cascade connect <graph> <node.output> <node.input>"),
|
|
261
|
+
...flags.has("no-layout") ? { layout: false } : {}
|
|
262
|
+
}),
|
|
263
|
+
mcp: [{
|
|
264
|
+
name: "connect",
|
|
265
|
+
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.",
|
|
266
|
+
inputSchema: object({ graph, from: { type: "string" }, to: { type: "string" }, layout: { type: "boolean" }, dryRun }, ["graph", "from", "to"]),
|
|
267
|
+
call: edit((args) => ({ op: "connect", from: args.from, to: args.to, ...args.layout === false ? { layout: false } : {} }))
|
|
268
|
+
}]
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
...graphVerb,
|
|
272
|
+
name: "disconnect",
|
|
273
|
+
usage: "<graph> <node.output> <node.input> | <node.input>",
|
|
274
|
+
summary: "Remove a wire",
|
|
275
|
+
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>") },
|
|
276
|
+
mcp: [{
|
|
277
|
+
name: "disconnect",
|
|
278
|
+
description: "Remove the wire from `from` to `to`, or with only `to`, whatever feeds that input.",
|
|
279
|
+
inputSchema: object({ graph, from: { type: "string" }, to: { type: "string" }, dryRun }, ["graph", "to"]),
|
|
280
|
+
call: edit((args) => args.from ? { op: "disconnect", from: args.from, to: args.to } : { op: "disconnect", to: args.to })
|
|
281
|
+
}]
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
...graphVerb,
|
|
285
|
+
name: "set",
|
|
286
|
+
usage: "<graph> <node.param> [<value>] [--expr <e> | --clear-expr]\n [--key <frame>=<value>]... [--remove-key <frame>]... [--interp constant|linear|smooth] [--unset]",
|
|
287
|
+
summary: "Set a parameter value, expression or keyframes",
|
|
288
|
+
options: { expr: "value", "clear-expr": "flag", key: "list", "remove-key": "list", interp: "value", unset: "flag" },
|
|
289
|
+
operation: (operands, { values, lists, flags }) => {
|
|
290
|
+
const target = required(operands[0], "cascade set <graph> <node.param> <value>");
|
|
291
|
+
const keys = (lists.key ?? []).map((pair) => {
|
|
292
|
+
const [frame, value] = splitPair(pair, "--key", "=");
|
|
293
|
+
return { frame: Number(frame), value: Number(value) };
|
|
294
|
+
});
|
|
295
|
+
const operation = {
|
|
296
|
+
op: "set",
|
|
297
|
+
target,
|
|
298
|
+
...operands[1] !== void 0 ? { value: operands[1], fromText: true } : {},
|
|
299
|
+
...values.expr !== void 0 ? { expression: values.expr } : {},
|
|
300
|
+
...flags.has("clear-expr") ? { expression: null } : {},
|
|
301
|
+
...keys.length ? { keys } : {},
|
|
302
|
+
...lists["remove-key"]?.length ? { removeKeys: lists["remove-key"].map(Number) } : {},
|
|
303
|
+
...values.interp ? { interpolation: interpolation(values.interp) } : {},
|
|
304
|
+
...flags.has("unset") ? { unset: true } : {}
|
|
305
|
+
};
|
|
306
|
+
if (Object.keys(operation).length === 2) throw new Error("cascade set needs a value, --expr, --clear-expr, --key, --remove-key or --unset");
|
|
307
|
+
return operation;
|
|
308
|
+
},
|
|
309
|
+
mcp: [{
|
|
310
|
+
name: "set",
|
|
311
|
+
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.",
|
|
312
|
+
inputSchema: object({
|
|
313
|
+
graph,
|
|
314
|
+
target: { type: "string", description: "node.param" },
|
|
315
|
+
value: {},
|
|
316
|
+
expression: { type: ["string", "null"] },
|
|
317
|
+
keys: { type: "array", items: object({ frame: { type: "number" }, value: { type: "number" } }, ["frame", "value"]) },
|
|
318
|
+
removeKeys: { type: "array", items: { type: "number" } },
|
|
319
|
+
interpolation: { enum: ["constant", "linear", "smooth"] },
|
|
320
|
+
unset: { type: "boolean" },
|
|
321
|
+
dryRun
|
|
322
|
+
}, ["graph", "target"]),
|
|
323
|
+
call: edit((args) => {
|
|
324
|
+
const { graph: _graph, dryRun: _dryRun, ...change } = args;
|
|
325
|
+
return { op: "set", ...change };
|
|
326
|
+
})
|
|
327
|
+
}]
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
...graphVerb,
|
|
331
|
+
name: "subnet create",
|
|
332
|
+
usage: "<graph> <node>... [--name <id>] [--label <text>]",
|
|
333
|
+
summary: "Group sibling nodes into a subnet",
|
|
334
|
+
options: { name: "value", label: "value" },
|
|
335
|
+
operation: (operands, { values }) => ({
|
|
336
|
+
op: "subnet.create",
|
|
337
|
+
nodes: nonEmpty(operands, "cascade subnet create <graph> <node>..."),
|
|
338
|
+
...values.name ? { id: values.name } : {},
|
|
339
|
+
...values.label ? { label: values.label } : {}
|
|
340
|
+
}),
|
|
341
|
+
mcp: [{
|
|
342
|
+
name: "subnet_create",
|
|
343
|
+
description: "Group sibling nodes into a new subnet; wires crossing its boundary go through new Input/Output nodes named after the port they carry.",
|
|
344
|
+
inputSchema: object({ graph, nodes: { type: "array", items: { type: "string" } }, id: { type: "string" }, label: { type: "string" }, dryRun }, ["graph", "nodes"]),
|
|
345
|
+
call: edit((args) => ({ op: "subnet.create", nodes: args.nodes, ...args.id ? { id: args.id } : {}, ...args.label ? { label: args.label } : {} }))
|
|
346
|
+
}]
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
...graphVerb,
|
|
350
|
+
name: "subnet move",
|
|
351
|
+
usage: "<graph> <node>... --to <subnet|root>",
|
|
352
|
+
summary: "Move sibling nodes into a subnet or to the top level",
|
|
353
|
+
options: { to: "value" },
|
|
354
|
+
operation: (operands, { values }) => ({ op: "subnet.move", nodes: nonEmpty(operands, "cascade subnet move <graph> <node>... --to <subnet|root>"), to: required(values.to, "--to <subnet|root>") }),
|
|
355
|
+
mcp: [{
|
|
356
|
+
name: "subnet_move",
|
|
357
|
+
description: 'Move sibling nodes into another subnet, or to "root", rewiring through the subnet boundaries on the way.',
|
|
358
|
+
inputSchema: object({ graph, nodes: { type: "array", items: { type: "string" } }, to: { type: "string" }, dryRun }, ["graph", "nodes", "to"]),
|
|
359
|
+
call: edit((args) => ({ op: "subnet.move", nodes: args.nodes, to: args.to }))
|
|
360
|
+
}]
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
...graphVerb,
|
|
364
|
+
name: "subnet dissolve",
|
|
365
|
+
usage: "<graph> <subnet>",
|
|
366
|
+
summary: "Lift a subnet's nodes back out and remove it",
|
|
367
|
+
operation: (operands) => ({ op: "subnet.dissolve", id: required(operands[0], "cascade subnet dissolve <graph> <subnet>") }),
|
|
368
|
+
mcp: [{
|
|
369
|
+
name: "subnet_dissolve",
|
|
370
|
+
description: "Lift a subnet's nodes back into its parent and remove the subnet.",
|
|
371
|
+
inputSchema: object({ graph, id: { type: "string" }, dryRun }, ["graph", "id"]),
|
|
372
|
+
call: edit((args) => ({ op: "subnet.dissolve", id: args.id }))
|
|
373
|
+
}]
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
...graphVerb,
|
|
377
|
+
name: "edit",
|
|
378
|
+
usage: "<graph> <operations.json | -> apply a batch, all or nothing",
|
|
379
|
+
summary: "Apply a batch of operations, all or nothing",
|
|
380
|
+
mcp: [{
|
|
381
|
+
name: "edit",
|
|
382
|
+
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`).',
|
|
383
|
+
inputSchema: object({ graph, operations: { type: "array", items: { type: "object" } }, dryRun }, ["graph", "operations"]),
|
|
384
|
+
call: { kind: "edit", operations: (args) => args.operations }
|
|
385
|
+
}]
|
|
386
|
+
},
|
|
387
|
+
// Locks
|
|
388
|
+
{
|
|
389
|
+
...lockVerb,
|
|
390
|
+
name: "lock",
|
|
391
|
+
usage: "<graph-file> <node> --name <name> [--note ...]",
|
|
392
|
+
summary: "Record a render (graph, params, seeds, inputs, code) in locks/<id>.json",
|
|
393
|
+
mcp: [{
|
|
394
|
+
name: "lock",
|
|
395
|
+
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.",
|
|
396
|
+
inputSchema: object({
|
|
397
|
+
graph,
|
|
398
|
+
node: { type: "string" },
|
|
399
|
+
name: { type: "string" },
|
|
400
|
+
note: { type: "string" },
|
|
401
|
+
scene: { type: "string" },
|
|
402
|
+
seed: { type: "integer" },
|
|
403
|
+
supersedes: { type: "string" },
|
|
404
|
+
preview: { type: "string" },
|
|
405
|
+
live: { type: "boolean" }
|
|
406
|
+
}, ["graph", "node", "name"]),
|
|
407
|
+
call: {
|
|
408
|
+
kind: "cli",
|
|
409
|
+
argv: (args, resolve) => [
|
|
410
|
+
"lock",
|
|
411
|
+
resolve(args.graph),
|
|
412
|
+
args.node,
|
|
413
|
+
"--name",
|
|
414
|
+
args.name,
|
|
415
|
+
...args.note ? ["--note", args.note] : [],
|
|
416
|
+
...args.scene ? ["--scene", resolve(args.scene)] : [],
|
|
417
|
+
...args.seed !== void 0 ? ["--seed", String(args.seed)] : [],
|
|
418
|
+
...args.supersedes ? ["--supersedes", args.supersedes] : [],
|
|
419
|
+
...args.preview ? ["--preview", resolve(args.preview)] : [],
|
|
420
|
+
...args.live ? ["--live"] : []
|
|
421
|
+
],
|
|
422
|
+
timeoutMs: () => 60 * 6e4
|
|
423
|
+
}
|
|
424
|
+
}]
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
...lockVerb,
|
|
428
|
+
name: "replay",
|
|
429
|
+
usage: "<lock-id> [--out <dir>]",
|
|
430
|
+
summary: "Re-render a lock from its stored code and compare fingerprints",
|
|
431
|
+
mcp: [{
|
|
432
|
+
name: "replay",
|
|
433
|
+
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.",
|
|
434
|
+
inputSchema: object({ lock: { type: "string" }, out: { type: "string" }, live: { type: "boolean" } }, ["lock"]),
|
|
435
|
+
call: {
|
|
436
|
+
kind: "cli",
|
|
437
|
+
argv: (args, resolve) => [
|
|
438
|
+
"replay",
|
|
439
|
+
String(args.lock).endsWith(".json") ? resolve(args.lock) : args.lock,
|
|
440
|
+
"--project",
|
|
441
|
+
resolve("."),
|
|
442
|
+
...args.out ? ["--out", resolve(args.out)] : [],
|
|
443
|
+
...args.live ? ["--live"] : []
|
|
444
|
+
],
|
|
445
|
+
timeoutMs: () => 60 * 6e4
|
|
446
|
+
}
|
|
447
|
+
}]
|
|
448
|
+
},
|
|
449
|
+
{ ...lockVerb, name: "lock verify", usage: "[<lock-id>...]", summary: "Replay locks and report which still match" },
|
|
450
|
+
{
|
|
451
|
+
...lockVerb,
|
|
452
|
+
name: "locks",
|
|
453
|
+
usage: "",
|
|
454
|
+
summary: "List the project's locks (cascade lock --help for all options)",
|
|
455
|
+
mcp: [{
|
|
456
|
+
name: "locks",
|
|
457
|
+
description: "List the project's locks (cascade locks).",
|
|
458
|
+
inputSchema: object({}, []),
|
|
459
|
+
call: { kind: "cli", argv: (_args, resolve) => ["locks", resolve(".")] }
|
|
460
|
+
}]
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
...lockVerb,
|
|
464
|
+
name: "ladder",
|
|
465
|
+
usage: "<graph-file> <node> --name <name> --vary <node.prop>=<a,b,c> | --seeds <a,b,c> [--cairn <vault dir>]",
|
|
466
|
+
summary: "Cook 3-5 variants, write a labelled contact sheet and ladders/<id>.json; --cairn makes a tap-to-pick card",
|
|
467
|
+
mcp: [{
|
|
468
|
+
name: "ladder",
|
|
469
|
+
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. `cairn` is a directory in the Substrate vault (the space\'s _attachments/ladders/<id>): each variant is copied there and `data.cairnChoice` is a cairn:choice fence to post as is (at most 8 variants); a tap comes back as "Picked: <label> (pick:<id>:<n>)", then call `pick` with ladder <id> and variant <n>.',
|
|
470
|
+
inputSchema: object({
|
|
471
|
+
graph,
|
|
472
|
+
node: { type: "string" },
|
|
473
|
+
name: { type: "string" },
|
|
474
|
+
vary: { type: "object", description: "node.prop to an array of values, one per variant" },
|
|
475
|
+
seeds: { type: "array", items: { type: "integer" } },
|
|
476
|
+
columns: { type: "integer" },
|
|
477
|
+
note: { type: "string" },
|
|
478
|
+
live: { type: "boolean" },
|
|
479
|
+
cairn: { type: "string", description: "Absolute vault directory for the variant images; returns data.cairnChoice" },
|
|
480
|
+
vault: { type: "string", description: "Absolute vault root the cairnChoice image paths are relative to (default: nearest .obsidian or .git above `cairn`)" }
|
|
481
|
+
}, ["graph", "node", "name"]),
|
|
482
|
+
call: {
|
|
483
|
+
kind: "cli",
|
|
484
|
+
argv: (args, resolve) => [
|
|
485
|
+
"ladder",
|
|
486
|
+
resolve(args.graph),
|
|
487
|
+
args.node,
|
|
488
|
+
"--name",
|
|
489
|
+
args.name,
|
|
490
|
+
// Outside the project by design (the vault), so passed as given: absolute.
|
|
491
|
+
...args.cairn ? ["--cairn", String(args.cairn)] : [],
|
|
492
|
+
...args.vault ? ["--vault", String(args.vault)] : [],
|
|
493
|
+
...Object.entries(args.vary ?? {}).flatMap(([key, values]) => [
|
|
494
|
+
"--vary",
|
|
495
|
+
`${key}=${(Array.isArray(values) ? values : [values]).map((value) => JSON.stringify(value)).join(",")}`
|
|
496
|
+
]),
|
|
497
|
+
...args.seeds?.length ? ["--seeds", args.seeds.join(",")] : [],
|
|
498
|
+
...args.columns ? ["--columns", String(args.columns)] : [],
|
|
499
|
+
...args.note ? ["--note", args.note] : [],
|
|
500
|
+
...args.live ? ["--live"] : []
|
|
501
|
+
],
|
|
502
|
+
timeoutMs: () => 60 * 6e4
|
|
503
|
+
}
|
|
504
|
+
}]
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
...lockVerb,
|
|
508
|
+
name: "pick",
|
|
509
|
+
usage: "<ladder-id> <n> [--name <lock name>] [--note ...]",
|
|
510
|
+
summary: "Lock variant <n> of a ladder at approval (commit, seed, params, image)",
|
|
511
|
+
mcp: [{
|
|
512
|
+
name: "pick",
|
|
513
|
+
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.",
|
|
514
|
+
inputSchema: object({
|
|
515
|
+
ladder: { type: "string" },
|
|
516
|
+
variant: { type: "integer" },
|
|
517
|
+
name: { type: "string" },
|
|
518
|
+
note: { type: "string" },
|
|
519
|
+
supersedes: { type: "string" }
|
|
520
|
+
}, ["ladder", "variant"]),
|
|
521
|
+
call: {
|
|
522
|
+
kind: "cli",
|
|
523
|
+
argv: (args, resolve) => [
|
|
524
|
+
"pick",
|
|
525
|
+
String(args.ladder).endsWith(".json") ? resolve(args.ladder) : args.ladder,
|
|
526
|
+
String(args.variant),
|
|
527
|
+
"--project",
|
|
528
|
+
resolve("."),
|
|
529
|
+
...args.name ? ["--name", args.name] : [],
|
|
530
|
+
...args.note ? ["--note", args.note] : [],
|
|
531
|
+
...args.supersedes ? ["--supersedes", args.supersedes] : []
|
|
532
|
+
]
|
|
533
|
+
}
|
|
534
|
+
}]
|
|
535
|
+
},
|
|
536
|
+
// One of `cascade render --workers`' processes, forked by it with its spec.
|
|
537
|
+
{ name: "__render-worker", usage: "<spec>", summary: "internal", section: "internal", handler: "renderWorker", json: "studio", help: "own", version: "handler" },
|
|
538
|
+
// A node's declaration from the installed package (helpCommand.ts).
|
|
539
|
+
{
|
|
540
|
+
name: "help node",
|
|
541
|
+
usage: "[<type>] [--project <dir>]",
|
|
542
|
+
summary: "Print a node type's inputs, props, outputs and description, or list every node type",
|
|
543
|
+
section: "agents",
|
|
544
|
+
handler: "help",
|
|
545
|
+
json: "keep",
|
|
546
|
+
help: "own",
|
|
547
|
+
version: "handler",
|
|
548
|
+
mcp: [{
|
|
549
|
+
name: "node_help",
|
|
550
|
+
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.",
|
|
551
|
+
inputSchema: object({ type: { type: "string" } }, []),
|
|
552
|
+
call: { kind: "cli", argv: (args, resolve) => ["help", "node", ...args.type ? [String(args.type)] : [], "--project", resolve(".")] }
|
|
553
|
+
}]
|
|
554
|
+
},
|
|
555
|
+
// What a headless run here will find; --fix builds the declared Python environment (doctorCommand.ts).
|
|
556
|
+
{
|
|
557
|
+
name: "doctor",
|
|
558
|
+
usage: "[project-directory] [--fix]",
|
|
559
|
+
summary: "Check the project's Python environment, stage interpreter, ffmpeg and canvas; --fix builds the declared environment",
|
|
560
|
+
section: "run",
|
|
561
|
+
handler: "doctor",
|
|
562
|
+
json: "keep",
|
|
563
|
+
help: "own",
|
|
564
|
+
version: "handler",
|
|
565
|
+
mcp: [{
|
|
566
|
+
name: "doctor",
|
|
567
|
+
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.',
|
|
568
|
+
inputSchema: object({ fix: { type: "boolean" } }, []),
|
|
569
|
+
call: { kind: "cli", argv: (args, resolve) => ["doctor", resolve("."), ...args.fix ? ["--fix"] : []], timeoutMs: () => 30 * 6e4 }
|
|
570
|
+
}]
|
|
571
|
+
}
|
|
572
|
+
];
|
|
573
|
+
function verbFor(args) {
|
|
574
|
+
const [first, second] = args;
|
|
575
|
+
if (!first) return void 0;
|
|
576
|
+
const two = `${first} ${second ?? ""}`;
|
|
577
|
+
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);
|
|
578
|
+
}
|
|
579
|
+
function isGraphCommand(args) {
|
|
580
|
+
return verbFor(args)?.handler === "graph";
|
|
581
|
+
}
|
|
582
|
+
function graphOptions() {
|
|
583
|
+
const sets = { value: /* @__PURE__ */ new Set(), list: /* @__PURE__ */ new Set(), flag: /* @__PURE__ */ new Set(["dry-run", "json"]) };
|
|
584
|
+
for (const verb of VERBS) {
|
|
585
|
+
if (verb.handler !== "graph") continue;
|
|
586
|
+
for (const [name, kind] of Object.entries(verb.options ?? {})) sets[kind].add(name);
|
|
587
|
+
}
|
|
588
|
+
return sets;
|
|
589
|
+
}
|
|
590
|
+
function mcpTools() {
|
|
591
|
+
return VERBS.flatMap((verb) => (verb.mcp ?? []).map((tool) => ({ verb, tool })));
|
|
592
|
+
}
|
|
593
|
+
var usageLine = (verb) => ` cascade ${verb.name}${verb.usage ? ` ${verb.usage}` : ""}`;
|
|
594
|
+
function graphCommandsHelp() {
|
|
595
|
+
return `
|
|
596
|
+
Graph editing (every command takes --json; edits take --dry-run):
|
|
597
|
+
${VERBS.filter((verb) => verb.section === "graph").map(usageLine).join("\n")}
|
|
598
|
+
`;
|
|
599
|
+
}
|
|
600
|
+
function mainHelp() {
|
|
601
|
+
const listed = VERBS.filter((verb) => verb.section !== "internal");
|
|
602
|
+
const before = listed.filter((verb) => verb.section !== "graph" && verb.section !== "locks" && verb.name !== "parity");
|
|
603
|
+
const after = listed.filter((verb) => verb.section === "locks" || verb.name === "parity");
|
|
604
|
+
const summaries = listed.filter((verb) => verb.section !== "graph");
|
|
605
|
+
const width = Math.max(...summaries.map((verb) => verb.name.length)) + 2;
|
|
606
|
+
return `
|
|
607
|
+
Cascade Graph Execution CLI
|
|
608
|
+
|
|
609
|
+
Usage:
|
|
610
|
+
cascade [project-directory | graph-file] [Studio options]
|
|
611
|
+
${before.map(usageLine).join("\n")}
|
|
612
|
+
${graphCommandsHelp()}
|
|
613
|
+
${after.map(usageLine).join("\n")}
|
|
614
|
+
|
|
615
|
+
Commands:
|
|
616
|
+
${summaries.map((verb) => ` ${verb.name.padEnd(width)}${verb.summary}`).join("\n")}
|
|
617
|
+
|
|
618
|
+
Options:
|
|
619
|
+
--host <address> Bind Studio to an address or hostname
|
|
620
|
+
--port <number> Bind Studio HTTP to an explicit port
|
|
621
|
+
--trusted-host <name> Allow an exact remote browser hostname (repeatable)
|
|
622
|
+
--fallback-host <addr> Bind this instead when --host does not resolve or is
|
|
623
|
+
not on this machine (repeatable; default 127.0.0.1).
|
|
624
|
+
The preferred host is retried and rebound when it returns
|
|
625
|
+
--no-open Do not open Studio in a browser
|
|
626
|
+
--node <id> Render only this node's output. Repeatable.
|
|
627
|
+
--entry-node <id> Older spelling of --node, single valued
|
|
628
|
+
--frames <range> Render a frame sequence: 1-100, 1-100x2 (step), 42, or
|
|
629
|
+
timeline (the range saved with the graph)
|
|
630
|
+
--fps <number> Frame rate to evaluate the range at (default: the graph's timeline fps)
|
|
631
|
+
--mp4 <file> After --frames, encode the sequence as H.264 yuv420p (needs ffmpeg)
|
|
632
|
+
--crf <n> Quality for --mp4, 0-51, lower is better (default 18)
|
|
633
|
+
--out <dir> Save image outputs here, project-relative or absolute: the
|
|
634
|
+
sequence with --frames (default: renders), else the still
|
|
635
|
+
--out-node <id> Also save this (mid-graph) node's image outputs. Repeatable.
|
|
636
|
+
--env NAME=value Set a variable for the run and its Python stages. Repeatable.
|
|
637
|
+
--json Print one { ok, data, warnings } envelope (any command)
|
|
638
|
+
--seed <n> Cook with this graph seed instead of the document's
|
|
639
|
+
--fresh With --frames, cook every node on every frame (default: only what the frame changed)
|
|
640
|
+
--timeout <ms> Bound a run in a supervised child process (exit 124 on timeout)
|
|
641
|
+
--validate-only Validate only (same as 'validate' command)
|
|
642
|
+
--strict Fail when a node names a module with no file (default: warn)
|
|
643
|
+
--verbose, -v Show verbose output
|
|
644
|
+
--version Show version
|
|
645
|
+
--help, -h Show this help message
|
|
646
|
+
|
|
647
|
+
Examples:
|
|
648
|
+
cascade .
|
|
649
|
+
cascade . --host KURO --port 3030
|
|
650
|
+
cascade new my-artwork
|
|
651
|
+
cascade node Multiply ./my-artwork
|
|
652
|
+
cascade run graph.cascade
|
|
653
|
+
cascade run graph.cascade --entry-node node_123
|
|
654
|
+
cascade validate graph.cascade
|
|
655
|
+
cascade run graph.cascade --frames 1-100
|
|
656
|
+
cascade run graph.cascade --frames 1-100x2 --fps 25 --out frames
|
|
657
|
+
cascade run graph.cascade --verbose
|
|
658
|
+
|
|
659
|
+
A frame sequence is written as <out>/<node id>.<frame>.<ext> \u2014 one file per
|
|
660
|
+
image output nothing downstream consumes (a Null passing an image counts), or
|
|
661
|
+
per output of --node, plus those of each --out-node. A node with several image
|
|
662
|
+
outputs adds the port: <node id>.<port>.<frame>.<ext>. Without --frames, --out
|
|
663
|
+
writes <out>/<node id>[.<port>].<ext>, and run --json lists every image output
|
|
664
|
+
in data.images either way.
|
|
665
|
+
|
|
666
|
+
Every run writes a trace to .cascade/runs/: each node's time and every Python
|
|
667
|
+
stage call with its cache key and state (hit, miss, off), and what each node
|
|
668
|
+
and its stages printed while it cooked (also in run --json as data.logs). A
|
|
669
|
+
failed run writes its trace too, and names it in the error. --verbose prints
|
|
670
|
+
the stage lines as they happen.
|
|
671
|
+
`;
|
|
672
|
+
}
|
|
673
|
+
function required(value, usage) {
|
|
674
|
+
if (!value) throw new Error(`usage: ${usage}`);
|
|
675
|
+
return value;
|
|
676
|
+
}
|
|
677
|
+
function nonEmpty(values, usage) {
|
|
678
|
+
if (!values.length) throw new Error(`usage: ${usage}`);
|
|
679
|
+
return [...values];
|
|
680
|
+
}
|
|
681
|
+
function splitPair(pair, flag, separator) {
|
|
682
|
+
const at = pair.indexOf(separator);
|
|
683
|
+
if (at <= 0) throw new Error(`${flag} takes name${separator}value (got "${pair}")`);
|
|
684
|
+
return [pair.slice(0, at), pair.slice(at + 1)];
|
|
685
|
+
}
|
|
686
|
+
function point(text) {
|
|
687
|
+
const [x, y] = text.split(",").map(Number);
|
|
688
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) throw new Error(`--at takes x,y (got "${text}")`);
|
|
689
|
+
return { x, y };
|
|
690
|
+
}
|
|
691
|
+
function interpolation(text) {
|
|
692
|
+
if (text === "constant" || text === "linear" || text === "smooth") return text;
|
|
693
|
+
throw new Error(`--interp is constant, linear or smooth (got "${text}")`);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
export {
|
|
697
|
+
VERBS,
|
|
698
|
+
verbFor,
|
|
699
|
+
isGraphCommand,
|
|
700
|
+
graphOptions,
|
|
701
|
+
mcpTools,
|
|
702
|
+
graphCommandsHelp,
|
|
703
|
+
mainHelp
|
|
704
|
+
};
|
|
705
|
+
//# sourceMappingURL=chunk-G23A2BZZ.js.map
|