@dpantani/tdmcp 0.5.0 → 0.6.1
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/README.md +4 -4
- package/dist/cli/agent.d.ts +106 -1
- package/dist/cli/agent.js +2291 -663
- package/dist/cli/agent.js.map +1 -1
- package/dist/index.js +2680 -958
- package/dist/index.js.map +1 -1
- package/package.json +21 -2
- package/safeskill.manifest.json +37 -0
- package/td/README.md +1 -1
- package/td/bootstrap.py +41 -9
- package/td/modules/mcp/controllers/api_controller.py +87 -3
- package/td/modules/mcp/install.py +49 -1
- package/td/modules/mcp/services/api_service.py +82 -0
- package/td/modules/mcp/services/connect_service.py +213 -0
- package/td/modules/mcp/services/log_service.py +136 -0
- package/td/modules/mcp/services/param_text_service.py +218 -0
- package/td/modules/utils/version.py +1 -1
package/dist/cli/agent.js
CHANGED
|
@@ -7,7 +7,7 @@ import { resolve as resolve6 } from "path";
|
|
|
7
7
|
import { createInterface } from "readline";
|
|
8
8
|
import { pathToFileURL } from "url";
|
|
9
9
|
import { parseArgs } from "util";
|
|
10
|
-
import { z as
|
|
10
|
+
import { z as z159 } from "zod";
|
|
11
11
|
|
|
12
12
|
// src/td-client/types.ts
|
|
13
13
|
var TdError = class extends Error {
|
|
@@ -45,6 +45,11 @@ function friendlyTdError(err) {
|
|
|
45
45
|
if (err instanceof Error) return err.message;
|
|
46
46
|
return String(err);
|
|
47
47
|
}
|
|
48
|
+
function isMissingEndpoint(err) {
|
|
49
|
+
if (!(err instanceof TdApiError)) return false;
|
|
50
|
+
if (err.status === 404) return true;
|
|
51
|
+
return /^Unsupported (GET|POST|PUT|PATCH|DELETE) /.test(err.message);
|
|
52
|
+
}
|
|
48
53
|
|
|
49
54
|
// src/feedback/previewCapture.ts
|
|
50
55
|
async function capturePreview(client, path2, width = 640, height = 360) {
|
|
@@ -1042,12 +1047,35 @@ var NodeRefSchema = z3.object({
|
|
|
1042
1047
|
/** Parameters that could not be applied at create time (unknown name or bad value). */
|
|
1043
1048
|
parameter_warnings: z3.array(z3.string()).optional()
|
|
1044
1049
|
});
|
|
1050
|
+
var NodeFlagsSchema = z3.object({
|
|
1051
|
+
bypass: z3.boolean().optional(),
|
|
1052
|
+
render: z3.boolean().optional(),
|
|
1053
|
+
display: z3.boolean().optional(),
|
|
1054
|
+
lock: z3.boolean().optional(),
|
|
1055
|
+
allowCooking: z3.boolean().optional(),
|
|
1056
|
+
cloneImmune: z3.boolean().optional(),
|
|
1057
|
+
is_clone: z3.boolean().optional(),
|
|
1058
|
+
clone: z3.string().nullable().optional()
|
|
1059
|
+
});
|
|
1060
|
+
var NodeWireSchema = z3.object({
|
|
1061
|
+
in_index: z3.number().int().nullable(),
|
|
1062
|
+
from: z3.string(),
|
|
1063
|
+
out_index: z3.number().int()
|
|
1064
|
+
});
|
|
1045
1065
|
var NodeDetailSchema = NodeRefSchema.extend({
|
|
1046
1066
|
parameters: z3.record(z3.string(), z3.unknown()).default({}),
|
|
1047
1067
|
inputs: z3.array(z3.string()).optional(),
|
|
1048
1068
|
outputs: z3.array(z3.string()).optional(),
|
|
1049
1069
|
family: z3.string().optional(),
|
|
1050
|
-
errors: z3.array(z3.string()).optional()
|
|
1070
|
+
errors: z3.array(z3.string()).optional(),
|
|
1071
|
+
// --- NEW (node_flags_in_detail): cosmetic + behavioral signals for a faithful round-trip ---
|
|
1072
|
+
flags: NodeFlagsSchema.optional(),
|
|
1073
|
+
wires_in: z3.array(NodeWireSchema).optional(),
|
|
1074
|
+
nodeX: z3.number().optional(),
|
|
1075
|
+
nodeY: z3.number().optional(),
|
|
1076
|
+
comment: z3.string().optional(),
|
|
1077
|
+
color: z3.array(z3.number()).optional(),
|
|
1078
|
+
tags: z3.array(z3.string()).optional()
|
|
1051
1079
|
});
|
|
1052
1080
|
var NodeListSchema = z3.object({ nodes: z3.array(NodeRefSchema).default([]) });
|
|
1053
1081
|
var InfoSchema = z3.object({
|
|
@@ -1134,6 +1162,70 @@ var BatchOperationSchema = z3.discriminatedUnion("action", [
|
|
|
1134
1162
|
target_input: z3.number().int().default(0)
|
|
1135
1163
|
})
|
|
1136
1164
|
]);
|
|
1165
|
+
var ConnectResultSchema = z3.object({
|
|
1166
|
+
source_path: z3.string(),
|
|
1167
|
+
target_path: z3.string(),
|
|
1168
|
+
requested_input: z3.number().int().optional(),
|
|
1169
|
+
actual_input: z3.number().int().optional(),
|
|
1170
|
+
source_output: z3.number().int().default(0),
|
|
1171
|
+
connected: z3.boolean().default(true)
|
|
1172
|
+
});
|
|
1173
|
+
var DisconnectResultSchema = z3.object({
|
|
1174
|
+
to_path: z3.string(),
|
|
1175
|
+
from_path: z3.string().nullable().optional(),
|
|
1176
|
+
to_input: z3.number().int().nullable().optional(),
|
|
1177
|
+
removed: z3.array(z3.object({ input: z3.number().int(), from: z3.string() })).default([]),
|
|
1178
|
+
warnings: z3.array(z3.string()).default([])
|
|
1179
|
+
});
|
|
1180
|
+
var ParamModeEntrySchema = z3.object({
|
|
1181
|
+
name: z3.string(),
|
|
1182
|
+
mode: z3.string(),
|
|
1183
|
+
value: z3.unknown().optional(),
|
|
1184
|
+
expr: z3.string().optional(),
|
|
1185
|
+
bind_expr: z3.string().optional(),
|
|
1186
|
+
export_op: z3.string().optional()
|
|
1187
|
+
});
|
|
1188
|
+
var ParamModesSchema = z3.object({
|
|
1189
|
+
path: z3.string(),
|
|
1190
|
+
type: z3.string().default(""),
|
|
1191
|
+
name: z3.string().default(""),
|
|
1192
|
+
parameters: z3.array(ParamModeEntrySchema).default([]),
|
|
1193
|
+
warnings: z3.array(z3.string()).default([])
|
|
1194
|
+
});
|
|
1195
|
+
var SetParamModeResultSchema = z3.object({
|
|
1196
|
+
path: z3.string(),
|
|
1197
|
+
param: z3.string(),
|
|
1198
|
+
mode: z3.string(),
|
|
1199
|
+
readback_mode: z3.string().default(""),
|
|
1200
|
+
readback_expr: z3.string().default("")
|
|
1201
|
+
});
|
|
1202
|
+
var DatTextSchema = z3.object({
|
|
1203
|
+
path: z3.string(),
|
|
1204
|
+
text: z3.string().default(""),
|
|
1205
|
+
is_table: z3.boolean().default(false),
|
|
1206
|
+
num_rows: z3.number().int().default(0),
|
|
1207
|
+
num_cols: z3.number().int().default(0)
|
|
1208
|
+
});
|
|
1209
|
+
var DatTextWriteSchema = z3.object({
|
|
1210
|
+
path: z3.string(),
|
|
1211
|
+
old_length: z3.number().int().default(0),
|
|
1212
|
+
new_length: z3.number().int().default(0)
|
|
1213
|
+
});
|
|
1214
|
+
var BridgeLogLineSchema = z3.object({
|
|
1215
|
+
source: z3.string().default(""),
|
|
1216
|
+
message: z3.string().default(""),
|
|
1217
|
+
absframe: z3.number().int().optional(),
|
|
1218
|
+
frame: z3.number().int().optional(),
|
|
1219
|
+
severity: z3.string().default(""),
|
|
1220
|
+
type: z3.string().default("")
|
|
1221
|
+
});
|
|
1222
|
+
var BridgeLogsSchema = z3.object({
|
|
1223
|
+
lines: z3.array(BridgeLogLineSchema).default([]),
|
|
1224
|
+
count: z3.number().int().default(0),
|
|
1225
|
+
error_dat: z3.string().optional(),
|
|
1226
|
+
available: z3.boolean().default(true),
|
|
1227
|
+
warnings: z3.array(z3.string()).default([])
|
|
1228
|
+
});
|
|
1137
1229
|
|
|
1138
1230
|
// src/td-client/touchDesignerClient.ts
|
|
1139
1231
|
var sleep = (ms) => new Promise((resolve7) => setTimeout(resolve7, ms));
|
|
@@ -1324,6 +1416,52 @@ var TouchDesignerClient = class {
|
|
|
1324
1416
|
recursive ? { recursive: true } : void 0
|
|
1325
1417
|
);
|
|
1326
1418
|
}
|
|
1419
|
+
// --- First-class wiring (survives TDMCP_BRIDGE_ALLOW_EXEC=0) ---
|
|
1420
|
+
connectNodes(sourcePath, targetPath, sourceOutput = 0, targetInput = 0) {
|
|
1421
|
+
return this.request("POST", "/api/connect", ConnectResultSchema, {
|
|
1422
|
+
source_path: sourcePath,
|
|
1423
|
+
target_path: targetPath,
|
|
1424
|
+
source_output: sourceOutput,
|
|
1425
|
+
target_input: targetInput
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1428
|
+
disconnectNodes(toPath, fromPath, toInput) {
|
|
1429
|
+
return this.request("POST", "/api/disconnect", DisconnectResultSchema, {
|
|
1430
|
+
to_path: toPath,
|
|
1431
|
+
from_path: fromPath ?? null,
|
|
1432
|
+
to_input: toInput ?? null
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
// --- Param-mode + DAT-text endpoints (survive ALLOW_EXEC=0) ---
|
|
1436
|
+
readParameterModes(path2, keys, nonDefaultOnly = false) {
|
|
1437
|
+
return this.request("GET", `/api/nodes/${segment(path2)}/params`, ParamModesSchema, void 0, {
|
|
1438
|
+
modes: true,
|
|
1439
|
+
keys: keys?.join(","),
|
|
1440
|
+
non_default_only: nonDefaultOnly || void 0
|
|
1441
|
+
});
|
|
1442
|
+
}
|
|
1443
|
+
setParameterMode(path2, param, mode, expr, value) {
|
|
1444
|
+
return this.request(
|
|
1445
|
+
"PATCH",
|
|
1446
|
+
`/api/nodes/${segment(path2)}/params/${encodeURIComponent(param)}/mode`,
|
|
1447
|
+
SetParamModeResultSchema,
|
|
1448
|
+
{ mode, expr, value }
|
|
1449
|
+
);
|
|
1450
|
+
}
|
|
1451
|
+
getDatText(path2) {
|
|
1452
|
+
return this.request("GET", `/api/nodes/${segment(path2)}/text`, DatTextSchema);
|
|
1453
|
+
}
|
|
1454
|
+
putDatText(path2, text) {
|
|
1455
|
+
return this.request("PUT", `/api/nodes/${segment(path2)}/text`, DatTextWriteSchema, { text });
|
|
1456
|
+
}
|
|
1457
|
+
// --- Structured bridge logs (Error DAT reader) ---
|
|
1458
|
+
getLogs(severity = "all", maxLines = 200, scope) {
|
|
1459
|
+
return this.request("GET", "/api/logs", BridgeLogsSchema, void 0, {
|
|
1460
|
+
severity,
|
|
1461
|
+
max_lines: maxLines,
|
|
1462
|
+
scope
|
|
1463
|
+
});
|
|
1464
|
+
}
|
|
1327
1465
|
};
|
|
1328
1466
|
|
|
1329
1467
|
// src/utils/config.ts
|
|
@@ -1599,6 +1737,13 @@ async function checkErrors(client, path2, recursive = true) {
|
|
|
1599
1737
|
|
|
1600
1738
|
// src/tools/layer2/connectHelper.ts
|
|
1601
1739
|
async function connectNodesViaBridge(client, sourcePath, targetPath, sourceOutput = 0, targetInput = 0) {
|
|
1740
|
+
try {
|
|
1741
|
+
const r2 = await client.connectNodes(sourcePath, targetPath, sourceOutput, targetInput);
|
|
1742
|
+
return { method: "endpoint", actualInput: r2.actual_input };
|
|
1743
|
+
} catch (err) {
|
|
1744
|
+
if (!isMissingEndpoint(err)) throw err;
|
|
1745
|
+
}
|
|
1746
|
+
let batchError;
|
|
1602
1747
|
try {
|
|
1603
1748
|
const result = await client.batch([
|
|
1604
1749
|
{
|
|
@@ -1609,7 +1754,9 @@ async function connectNodesViaBridge(client, sourcePath, targetPath, sourceOutpu
|
|
|
1609
1754
|
target_input: targetInput
|
|
1610
1755
|
}
|
|
1611
1756
|
]);
|
|
1612
|
-
|
|
1757
|
+
const op = result.results[0];
|
|
1758
|
+
if (op?.ok) return { method: "batch" };
|
|
1759
|
+
batchError = op?.error;
|
|
1613
1760
|
} catch (err) {
|
|
1614
1761
|
if (!(err instanceof TdApiError)) throw err;
|
|
1615
1762
|
}
|
|
@@ -1621,8 +1768,17 @@ async function connectNodesViaBridge(client, sourcePath, targetPath, sourceOutpu
|
|
|
1621
1768
|
`if __s.parent() is None or __d.parent() is None or __s.parent().path != __d.parent().path: raise ValueError('connect: cannot wire across containers (%s -> %s); use a Select/In OP to bring an operator across networks' % (${src}, ${dst}))`,
|
|
1622
1769
|
`__d.inputConnectors[${targetInput}].connect(__s.outputConnectors[${sourceOutput}])`
|
|
1623
1770
|
].join("\n");
|
|
1624
|
-
|
|
1625
|
-
|
|
1771
|
+
try {
|
|
1772
|
+
await client.executePythonScript(python, false);
|
|
1773
|
+
} catch (err) {
|
|
1774
|
+
if (batchError) {
|
|
1775
|
+
throw new TdApiError(`${friendlyTdError(err)} (batch connect also failed: ${batchError})`, {
|
|
1776
|
+
cause: err
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
throw err;
|
|
1780
|
+
}
|
|
1781
|
+
return batchError ? { method: "python", batchError } : { method: "python" };
|
|
1626
1782
|
}
|
|
1627
1783
|
|
|
1628
1784
|
// src/tools/layout.ts
|
|
@@ -3402,7 +3558,7 @@ var createDatamoshSchema = z15.object({
|
|
|
3402
3558
|
"How slowly the trail fades (0\u20131). Higher values = longer smear / more persistent ghost. Applied via levelTOP brightness1. Default 0.9."
|
|
3403
3559
|
),
|
|
3404
3560
|
displace: z15.coerce.number().min(0).default(0).describe(
|
|
3405
|
-
"Pixel displacement of the fed-back frame each cycle (the 'mosh wobble'). Applied via displaceTOP displaceweight. 0 = no wobble. Default 0.0."
|
|
3561
|
+
"Pixel displacement of the fed-back frame each cycle (the 'mosh wobble'). Applied via displaceTOP displaceweight1 (falls back to displaceweight on older builds). 0 = no wobble. Default 0.0."
|
|
3406
3562
|
),
|
|
3407
3563
|
resolution: z15.tuple([z15.number(), z15.number()]).default([1280, 720]).describe(
|
|
3408
3564
|
"Output resolution [width, height] in pixels. Forced on the feedback loop to prevent flickering. Default [1280, 720]."
|
|
@@ -3434,11 +3590,23 @@ async function buildFeedbackEcho(builder, args) {
|
|
|
3434
3590
|
const decay = await builder.add("levelTOP", "decay1");
|
|
3435
3591
|
await builder.setParams(decay, { brightness1: args.decay });
|
|
3436
3592
|
let last = comp;
|
|
3593
|
+
let dispPath = null;
|
|
3437
3594
|
if (args.displace > 0) {
|
|
3438
3595
|
const disp = await builder.add("displaceTOP", "displace1");
|
|
3439
|
-
await builder.
|
|
3596
|
+
await builder.python(
|
|
3597
|
+
`_d = op(${q10(disp)})
|
|
3598
|
+
_set = False
|
|
3599
|
+
for _pn in ['displaceweight1', 'displaceweight']:
|
|
3600
|
+
try:
|
|
3601
|
+
setattr(_d.par, _pn, ${args.displace})
|
|
3602
|
+
_set = True
|
|
3603
|
+
break
|
|
3604
|
+
except Exception:
|
|
3605
|
+
pass`
|
|
3606
|
+
);
|
|
3440
3607
|
await builder.connect(last, disp, 0, 0);
|
|
3441
3608
|
await builder.connect(src, disp, 0, 1);
|
|
3609
|
+
dispPath = disp;
|
|
3442
3610
|
last = disp;
|
|
3443
3611
|
}
|
|
3444
3612
|
await builder.connect(last, decay);
|
|
@@ -3460,14 +3628,14 @@ async function buildFeedbackEcho(builder, args) {
|
|
|
3460
3628
|
default: args.decay,
|
|
3461
3629
|
bind_to: [`${decay}.brightness1`]
|
|
3462
3630
|
},
|
|
3463
|
-
...
|
|
3631
|
+
...dispPath !== null ? [
|
|
3464
3632
|
{
|
|
3465
3633
|
name: "Displace",
|
|
3466
3634
|
type: "float",
|
|
3467
3635
|
min: 0,
|
|
3468
3636
|
max: 1,
|
|
3469
3637
|
default: args.displace,
|
|
3470
|
-
bind_to: [`${
|
|
3638
|
+
bind_to: [`${dispPath}.displaceweight1`, `${dispPath}.displaceweight`]
|
|
3471
3639
|
}
|
|
3472
3640
|
] : []
|
|
3473
3641
|
];
|
|
@@ -3497,7 +3665,17 @@ async function buildFrameBlend(builder, args) {
|
|
|
3497
3665
|
let last = comp;
|
|
3498
3666
|
if (args.displace > 0) {
|
|
3499
3667
|
const disp = await builder.add("displaceTOP", "displace1");
|
|
3500
|
-
await builder.
|
|
3668
|
+
await builder.python(
|
|
3669
|
+
`_d = op(${q10(disp)})
|
|
3670
|
+
_set = False
|
|
3671
|
+
for _pn in ['displaceweight1', 'displaceweight']:
|
|
3672
|
+
try:
|
|
3673
|
+
setattr(_d.par, _pn, ${args.displace})
|
|
3674
|
+
_set = True
|
|
3675
|
+
break
|
|
3676
|
+
except Exception:
|
|
3677
|
+
pass`
|
|
3678
|
+
);
|
|
3501
3679
|
await builder.connect(last, disp, 0, 0);
|
|
3502
3680
|
await builder.connect(src, disp, 0, 1);
|
|
3503
3681
|
last = disp;
|
|
@@ -3549,7 +3727,17 @@ async function buildTimeEcho(builder, args) {
|
|
|
3549
3727
|
let last = timeMachine;
|
|
3550
3728
|
if (args.displace > 0) {
|
|
3551
3729
|
const disp = await builder.add("displaceTOP", "displace1");
|
|
3552
|
-
await builder.
|
|
3730
|
+
await builder.python(
|
|
3731
|
+
`_d = op(${q10(disp)})
|
|
3732
|
+
_set = False
|
|
3733
|
+
for _pn in ['displaceweight1', 'displaceweight']:
|
|
3734
|
+
try:
|
|
3735
|
+
setattr(_d.par, _pn, ${args.displace})
|
|
3736
|
+
_set = True
|
|
3737
|
+
break
|
|
3738
|
+
except Exception:
|
|
3739
|
+
pass`
|
|
3740
|
+
);
|
|
3553
3741
|
await builder.connect(last, disp, 0, 0);
|
|
3554
3742
|
await builder.connect(noiseMap, disp, 0, 1);
|
|
3555
3743
|
last = disp;
|
|
@@ -5346,7 +5534,7 @@ import { z as z27 } from "zod";
|
|
|
5346
5534
|
var q21 = (value) => JSON.stringify(value);
|
|
5347
5535
|
var HALFTONE_SHADER = `out vec4 fragColor;
|
|
5348
5536
|
|
|
5349
|
-
uniform
|
|
5537
|
+
uniform float uStyle;
|
|
5350
5538
|
uniform float uDotSize;
|
|
5351
5539
|
uniform float uAngle;
|
|
5352
5540
|
uniform float uMix;
|
|
@@ -5361,8 +5549,8 @@ vec2 rotateUV(vec2 uv, float angle) {
|
|
|
5361
5549
|
|
|
5362
5550
|
// Dot-grid halftone: returns 0 (dot) or 1 (paper) for a single channel value
|
|
5363
5551
|
float dotCell(vec2 uv, float val, float cellPx, float angle) {
|
|
5364
|
-
//
|
|
5365
|
-
vec2 px =
|
|
5552
|
+
// res.xy = (1/width, 1/height) per TDTexInfo layout \u2014 already texel size
|
|
5553
|
+
vec2 px = uTDOutputInfo.res.xy;
|
|
5366
5554
|
vec2 uvr = rotateUV(uv, angle);
|
|
5367
5555
|
vec2 cell = fract(uvr / (cellPx * px)) - 0.5;
|
|
5368
5556
|
float dist = length(cell);
|
|
@@ -5386,17 +5574,18 @@ float bayer4(vec2 pos) {
|
|
|
5386
5574
|
void main() {
|
|
5387
5575
|
vec2 uv = vUV.st;
|
|
5388
5576
|
vec4 orig = texture(sTD2DInputs[0], uv);
|
|
5577
|
+
int style = int(uStyle);
|
|
5389
5578
|
|
|
5390
5579
|
vec4 styled;
|
|
5391
5580
|
|
|
5392
|
-
if (
|
|
5581
|
+
if (style == 0) {
|
|
5393
5582
|
// ---- DOTS: monochrome halftone ----
|
|
5394
5583
|
float lum = dot(orig.rgb, vec3(0.299, 0.587, 0.114));
|
|
5395
5584
|
float angleRad = uAngle * 3.14159265 / 180.0;
|
|
5396
5585
|
float paper = dotCell(uv, lum, uDotSize, angleRad);
|
|
5397
5586
|
styled = vec4(vec3(paper), orig.a);
|
|
5398
5587
|
|
|
5399
|
-
} else if (
|
|
5588
|
+
} else if (style == 1) {
|
|
5400
5589
|
// ---- CMYK: 4-colour halftone separation ----
|
|
5401
5590
|
// Convert RGB \u2192 CMY; K = min component
|
|
5402
5591
|
float k = 1.0 - max(max(orig.r, orig.g), orig.b);
|
|
@@ -5416,9 +5605,9 @@ void main() {
|
|
|
5416
5605
|
col -= (1.0 - kDot) * vec3(1.0, 1.0, 1.0); // black ink
|
|
5417
5606
|
styled = vec4(clamp(col, 0.0, 1.0), orig.a);
|
|
5418
5607
|
|
|
5419
|
-
} else if (
|
|
5608
|
+
} else if (style == 2) {
|
|
5420
5609
|
// ---- DITHER: 4\xD74 Bayer ordered dither ----
|
|
5421
|
-
vec2 px =
|
|
5610
|
+
vec2 px = uTDOutputInfo.res.xy;
|
|
5422
5611
|
vec2 screenPos = uv / px;
|
|
5423
5612
|
float thresh = bayer4(screenPos);
|
|
5424
5613
|
// Quantise each channel against the dither threshold
|
|
@@ -5434,7 +5623,7 @@ void main() {
|
|
|
5434
5623
|
float levels = 6.0;
|
|
5435
5624
|
vec3 post = floor(orig.rgb * levels) / levels;
|
|
5436
5625
|
// Edge detect via luminance gradient (centre-difference, 1-pixel step)
|
|
5437
|
-
vec2 px =
|
|
5626
|
+
vec2 px = uTDOutputInfo.res.xy;
|
|
5438
5627
|
float lc = dot(orig.rgb, vec3(0.299, 0.587, 0.114));
|
|
5439
5628
|
float lr = dot(texture(sTD2DInputs[0], uv + vec2(px.x, 0.0)).rgb, vec3(0.299, 0.587, 0.114));
|
|
5440
5629
|
float lu = dot(texture(sTD2DInputs[0], uv + vec2(0.0, px.y)).rgb, vec3(0.299, 0.587, 0.114));
|
|
@@ -13645,13 +13834,23 @@ async function connectNodesImpl(ctx, args) {
|
|
|
13645
13834
|
args.source_output,
|
|
13646
13835
|
args.target_input
|
|
13647
13836
|
),
|
|
13648
|
-
(result) =>
|
|
13649
|
-
|
|
13650
|
-
|
|
13651
|
-
|
|
13652
|
-
|
|
13653
|
-
|
|
13654
|
-
|
|
13837
|
+
(result) => {
|
|
13838
|
+
const actualInput = result.actualInput ?? args.target_input;
|
|
13839
|
+
const packed = result.actualInput !== void 0 && result.actualInput !== args.target_input;
|
|
13840
|
+
const note = (result.batchError ? ` Batch connect first failed (${result.batchError}); used the Python fallback.` : "") + (packed ? ` Requested input ${args.target_input} packed to live slot ${actualInput}.` : "");
|
|
13841
|
+
return jsonResult(
|
|
13842
|
+
`Connected ${args.source_path} \u2192 ${args.target_path} (via ${result.method}).${note}`,
|
|
13843
|
+
{
|
|
13844
|
+
source: args.source_path,
|
|
13845
|
+
target: args.target_path,
|
|
13846
|
+
source_output: args.source_output,
|
|
13847
|
+
target_input: args.target_input,
|
|
13848
|
+
actual_input: actualInput,
|
|
13849
|
+
method: result.method,
|
|
13850
|
+
...result.batchError ? { batch_error: result.batchError } : {}
|
|
13851
|
+
}
|
|
13852
|
+
);
|
|
13853
|
+
}
|
|
13655
13854
|
);
|
|
13656
13855
|
}
|
|
13657
13856
|
|
|
@@ -16022,19 +16221,478 @@ async function createLedMapperImpl(ctx, args) {
|
|
|
16022
16221
|
);
|
|
16023
16222
|
}
|
|
16024
16223
|
|
|
16025
|
-
// src/tools/layer2/
|
|
16224
|
+
// src/tools/layer2/createLookBank.ts
|
|
16026
16225
|
import { z as z98 } from "zod";
|
|
16027
|
-
var
|
|
16028
|
-
|
|
16029
|
-
|
|
16030
|
-
|
|
16031
|
-
|
|
16226
|
+
var createLookBankSchema = z98.object({
|
|
16227
|
+
action: z98.enum(["build", "store", "recall", "set_ab", "list", "delete"]).default("build").describe(
|
|
16228
|
+
"build: create the look-bank container (Table DAT + A\u2194B morph knob + recall button row) on a control COMP. store: snapshot the COMP's current numeric look into a named slot. recall: jump or crossfade to a slot. set_ab: assign which two slots the A\u2194B knob blends, and optionally set the knob. list / delete slots."
|
|
16229
|
+
),
|
|
16230
|
+
comp_path: z98.string().default("/project1").describe(
|
|
16231
|
+
"Control COMP whose custom-parameter values the looks capture (a control-panel container, e.g. from create_control_panel). The look-bank widgets are built inside it; recall drives this COMP's params."
|
|
16232
|
+
),
|
|
16233
|
+
name: z98.string().default("look_bank").describe("Name of the look-bank panel container built inside comp_path."),
|
|
16234
|
+
slot: z98.string().optional().describe("Slot name (required for store / recall / delete)."),
|
|
16235
|
+
morph_seconds: z98.coerce.number().min(0).default(0).describe(
|
|
16236
|
+
"(recall) 0 = snap instantly; >0 = crossfade to the slot over this many seconds (eased), via the cue morph engine."
|
|
16237
|
+
),
|
|
16238
|
+
quantize: z98.enum(["off", "beat", "bar"]).default("off").describe(
|
|
16239
|
+
"(recall) Defer the snap/crossfade to the next musical boundary (project tempo), so look changes land on the downbeat. Mirrors manage_cue."
|
|
16240
|
+
),
|
|
16241
|
+
slot_a: z98.string().optional().describe("(set_ab) Slot the A\u2194B knob reads at value 0."),
|
|
16242
|
+
slot_b: z98.string().optional().describe("(set_ab) Slot the A\u2194B knob reads at value 1."),
|
|
16243
|
+
ab: z98.coerce.number().min(0).max(1).optional().describe(
|
|
16244
|
+
"(set_ab) Optionally set the A\u2194B knob position now (0 = slot A, 1 = slot B, 0.5 = halfway). Omit to just (re)assign the slots."
|
|
16245
|
+
),
|
|
16246
|
+
include: z98.array(z98.string()).optional().describe(
|
|
16247
|
+
"(store) Restrict the snapshot to these custom-parameter names. Omit to capture every numeric/toggle/menu parameter (pulses and strings are always skipped)."
|
|
16248
|
+
)
|
|
16249
|
+
});
|
|
16250
|
+
var AB_BLEND_CB = `import td
|
|
16251
|
+
|
|
16252
|
+
def _lookbank_blend():
|
|
16253
|
+
bank = me.parent()
|
|
16254
|
+
comp = bank.parent()
|
|
16255
|
+
st = bank.fetch('tdmcp_lookbank_ab', {})
|
|
16256
|
+
sa = st.get('slot_a'); sb = st.get('slot_b')
|
|
16257
|
+
if not sa or not sb:
|
|
16258
|
+
return
|
|
16259
|
+
tbl = bank.op('looks')
|
|
16260
|
+
if tbl is None or tbl.numRows < 1:
|
|
16261
|
+
return
|
|
16262
|
+
header = [tbl[0, c].val for c in range(tbl.numCols)]
|
|
16263
|
+
if sa not in header or sb not in header:
|
|
16264
|
+
return
|
|
16265
|
+
ca = header.index(sa); cb = header.index(sb)
|
|
16266
|
+
abpar = getattr(bank.par, 'Ab', None)
|
|
16267
|
+
ab = float(abpar.eval()) if abpar is not None else 0.0
|
|
16268
|
+
for r in range(1, tbl.numRows):
|
|
16269
|
+
k = tbl[r, 0].val
|
|
16270
|
+
par = getattr(comp.par, k, None)
|
|
16271
|
+
if par is None or par.readOnly or not getattr(par, 'isNumber', False):
|
|
16272
|
+
continue
|
|
16273
|
+
try:
|
|
16274
|
+
a = float(tbl[r, ca].val); b = float(tbl[r, cb].val)
|
|
16275
|
+
except Exception:
|
|
16276
|
+
continue
|
|
16277
|
+
val = a + (b - a) * ab
|
|
16278
|
+
try:
|
|
16279
|
+
par.val = int(round(val)) if getattr(par, 'style', '') == 'Int' else val
|
|
16280
|
+
except Exception:
|
|
16281
|
+
pass
|
|
16282
|
+
return
|
|
16283
|
+
|
|
16284
|
+
def onValueChange(par, prev):
|
|
16285
|
+
if par.name == 'Ab':
|
|
16286
|
+
_lookbank_blend()
|
|
16287
|
+
return
|
|
16288
|
+
|
|
16289
|
+
def onPulse(par):
|
|
16290
|
+
return
|
|
16291
|
+
`;
|
|
16292
|
+
var LOOKBANK_SCRIPT = `
|
|
16293
|
+
import ast, json, base64, traceback
|
|
16294
|
+
import td
|
|
16295
|
+
_p = json.loads(base64.b64decode("__PAYLOAD_B64__").decode("utf-8"))
|
|
16296
|
+
report = {"action": _p["action"], "comp": _p["comp"], "warnings": []}
|
|
16297
|
+
_c = op(_p["comp"])
|
|
16298
|
+
|
|
16299
|
+
def _next_boundary_delay(quant):
|
|
16300
|
+
# Seconds from now (absTime) to the next beat/bar boundary, from the project tempo + signature.
|
|
16301
|
+
# Returns 0.0 for 'off' or on any failure, degrading to an immediate change.
|
|
16302
|
+
if quant not in ("beat", "bar"):
|
|
16303
|
+
return 0.0
|
|
16304
|
+
try:
|
|
16305
|
+
_t = op('/').time
|
|
16306
|
+
_tempo = float(getattr(_t, 'tempo', 0.0) or 0.0)
|
|
16307
|
+
if _tempo <= 0.0:
|
|
16308
|
+
return 0.0
|
|
16309
|
+
_spb = 60.0 / _tempo
|
|
16310
|
+
_beat = getattr(_t, 'beat', None)
|
|
16311
|
+
if _beat is None:
|
|
16312
|
+
_secs = float(getattr(_t, 'seconds', 0.0) or 0.0)
|
|
16313
|
+
_beat = _secs / _spb
|
|
16314
|
+
_beat = float(_beat)
|
|
16315
|
+
if quant == "beat":
|
|
16316
|
+
_period = 1.0
|
|
16317
|
+
else:
|
|
16318
|
+
_bpb = int(round(float(getattr(_t, 'signature1', 4) or 4)))
|
|
16319
|
+
if _bpb < 1:
|
|
16320
|
+
_bpb = 4
|
|
16321
|
+
_period = float(_bpb)
|
|
16322
|
+
_phase = _beat % _period
|
|
16323
|
+
_remaining = _period - _phase
|
|
16324
|
+
if _remaining <= 1e-6:
|
|
16325
|
+
_remaining = _period
|
|
16326
|
+
return _remaining * _spb
|
|
16327
|
+
except Exception:
|
|
16328
|
+
return 0.0
|
|
16329
|
+
|
|
16330
|
+
def _table(bank):
|
|
16331
|
+
return bank.op('looks') or bank.create(td.tableDAT, 'looks')
|
|
16332
|
+
|
|
16333
|
+
def _header(tbl):
|
|
16334
|
+
if tbl.numRows < 1:
|
|
16335
|
+
tbl.appendRow(['param'])
|
|
16336
|
+
return [tbl[0, c].val for c in range(tbl.numCols)]
|
|
16337
|
+
|
|
16338
|
+
def _slots(tbl):
|
|
16339
|
+
return _header(tbl)[1:] if tbl.numRows >= 1 else []
|
|
16340
|
+
|
|
16341
|
+
def _col_dict(tbl, slot):
|
|
16342
|
+
# Read a slot column into {param: typed value}. Values are stored as repr() so bools/ints/
|
|
16343
|
+
# floats round-trip; fall back to a float, then the raw string.
|
|
16344
|
+
hdr = _header(tbl)
|
|
16345
|
+
if slot not in hdr:
|
|
16346
|
+
return {}
|
|
16347
|
+
ci = hdr.index(slot)
|
|
16348
|
+
out = {}
|
|
16349
|
+
for r in range(1, tbl.numRows):
|
|
16350
|
+
k = tbl[r, 0].val
|
|
16351
|
+
raw = tbl[r, ci].val
|
|
16352
|
+
if raw == '':
|
|
16353
|
+
continue
|
|
16354
|
+
v = raw
|
|
16355
|
+
try:
|
|
16356
|
+
# literal_eval safely round-trips the repr() values written by store
|
|
16357
|
+
# (numbers, strings, bool, None, tuples/lists) and REJECTS code, so a
|
|
16358
|
+
# hand-edited look slot cannot execute arbitrary expressions in TD.
|
|
16359
|
+
v = ast.literal_eval(raw)
|
|
16360
|
+
except Exception:
|
|
16361
|
+
try:
|
|
16362
|
+
v = float(raw)
|
|
16363
|
+
except Exception:
|
|
16364
|
+
v = raw
|
|
16365
|
+
out[k] = v
|
|
16366
|
+
return out
|
|
16367
|
+
|
|
16368
|
+
def _ensure_morph(comp, text):
|
|
16369
|
+
h = comp.op('cue_morph') or comp.create(td.executeDAT, 'cue_morph')
|
|
16370
|
+
h.text = text
|
|
16371
|
+
if hasattr(h.par, 'framestart'):
|
|
16372
|
+
h.par.framestart = True
|
|
16373
|
+
h.par.active = True
|
|
16374
|
+
return h
|
|
16375
|
+
|
|
16376
|
+
def _btn_name(s):
|
|
16377
|
+
# Deterministic, INJECTIVE op name: ASCII-alnum chars pass through, every other
|
|
16378
|
+
# char (including '_' and non-ASCII) is escaped as _<HEX>_, so distinct slot
|
|
16379
|
+
# labels can NEVER collide to the same buttonCOMP (e.g. 'intro/drop' vs
|
|
16380
|
+
# 'intro_drop'). Pure function of the label, so create and delete agree; the
|
|
16381
|
+
# displayed label (par.label) and the cue key keep the raw slot string.
|
|
16382
|
+
_out = []
|
|
16383
|
+
for _ch in str(s):
|
|
16384
|
+
if _ch.isascii() and _ch.isalnum():
|
|
16385
|
+
_out.append(_ch)
|
|
16386
|
+
else:
|
|
16387
|
+
_out.append('_%X_' % ord(_ch))
|
|
16388
|
+
return 'recall_' + (''.join(_out) or 'slot')
|
|
16389
|
+
|
|
16390
|
+
def _rebuild_buttons(bank, comp, btn_cb):
|
|
16391
|
+
# One momentary buttonCOMP per slot + one Panel Execute DAT dispatching them via the shared
|
|
16392
|
+
# tdmcp_surface_cues map (same shape create_control_surface uses), so SURFACE_BUTTON_CB fires
|
|
16393
|
+
# the mirrored cue. morph_seconds per button defaults to 0 (snap) on (re)build.
|
|
16394
|
+
tbl = _table(bank)
|
|
16395
|
+
slots = _slots(tbl)
|
|
16396
|
+
cmap = {}
|
|
16397
|
+
paths = []
|
|
16398
|
+
for s in slots:
|
|
16399
|
+
_nm = _btn_name(s)
|
|
16400
|
+
bt = bank.op(_nm) or bank.create(td.buttonCOMP, _nm)
|
|
16401
|
+
try:
|
|
16402
|
+
bt.par.w = 110; bt.par.h = 60
|
|
16403
|
+
bt.par.label = s
|
|
16404
|
+
if hasattr(bt.par, 'buttontype'):
|
|
16405
|
+
bt.par.buttontype = 'momentary'
|
|
16406
|
+
except Exception:
|
|
16407
|
+
pass
|
|
16408
|
+
cmap[bt.path] = {"comp": comp.path, "cue": s, "dur": 0}
|
|
16409
|
+
paths.append(bt.path)
|
|
16410
|
+
if paths:
|
|
16411
|
+
bank.store('tdmcp_surface_cues', cmap)
|
|
16412
|
+
pe = bank.op('recall_exec') or bank.create(td.panelexecuteDAT, 'recall_exec')
|
|
16413
|
+
pe.text = btn_cb
|
|
16414
|
+
try:
|
|
16415
|
+
pe.par.panels = " ".join(paths)
|
|
16416
|
+
if hasattr(pe.par, 'offtoon'):
|
|
16417
|
+
pe.par.offtoon = True
|
|
16418
|
+
pe.par.active = True
|
|
16419
|
+
except Exception:
|
|
16420
|
+
pass
|
|
16421
|
+
return paths
|
|
16422
|
+
|
|
16423
|
+
try:
|
|
16424
|
+
if _c is None:
|
|
16425
|
+
report["fatal"] = "COMP not found: " + _p["comp"]
|
|
16426
|
+
elif not hasattr(_c, "customPars"):
|
|
16427
|
+
report["fatal"] = _p["comp"] + " is not a COMP, so it has no custom parameters to snapshot."
|
|
16428
|
+
else:
|
|
16429
|
+
_action = _p["action"]
|
|
16430
|
+
_bank = _c.op(_p["name"]) or _c.create(td.containerCOMP, _p["name"])
|
|
16431
|
+
report["bank"] = _bank.path
|
|
16432
|
+
_tbl = _table(_bank)
|
|
16433
|
+
report["table"] = _tbl.path
|
|
16434
|
+
_ensure_morph(_c, _p["morph_text"])
|
|
16435
|
+
# Ensure the Ab knob + its watcher exist on every action so the panel is always live.
|
|
16436
|
+
if getattr(_bank.par, "Ab", None) is None:
|
|
16437
|
+
_pg = None
|
|
16438
|
+
for _g in _bank.customPages:
|
|
16439
|
+
if _g.name == "LookBank":
|
|
16440
|
+
_pg = _g; break
|
|
16441
|
+
if _pg is None:
|
|
16442
|
+
_pg = _bank.appendCustomPage("LookBank")
|
|
16443
|
+
_abp = _pg.appendFloat("Ab")[0]
|
|
16444
|
+
_abp.normMin = 0; _abp.normMax = 1; _abp.default = 0; _abp.val = 0
|
|
16445
|
+
_abx = _bank.op("ab_exec") or _bank.create(td.parameterexecuteDAT, "ab_exec")
|
|
16446
|
+
_abx.text = _p["ab_cb"]
|
|
16447
|
+
try:
|
|
16448
|
+
_abx.par.op = _bank.path
|
|
16449
|
+
if hasattr(_abx.par, "valuechange"):
|
|
16450
|
+
_abx.par.valuechange = True
|
|
16451
|
+
if hasattr(_abx.par, "pars"):
|
|
16452
|
+
_abx.par.pars = "Ab"
|
|
16453
|
+
_abx.par.active = True
|
|
16454
|
+
except Exception:
|
|
16455
|
+
report["warnings"].append("Could not fully wire the A/B watcher.")
|
|
16456
|
+
|
|
16457
|
+
if _action == "build":
|
|
16458
|
+
report["buttons"] = _rebuild_buttons(_bank, _c, _p["button_cb"])
|
|
16459
|
+
report["slots"] = _slots(_tbl)
|
|
16460
|
+
elif _action == "list":
|
|
16461
|
+
report["slots"] = _slots(_tbl)
|
|
16462
|
+
elif _action == "store":
|
|
16463
|
+
_slot = _p.get("slot")
|
|
16464
|
+
_include = _p.get("include")
|
|
16465
|
+
_captured = []; _skipped = []
|
|
16466
|
+
_vals = {}
|
|
16467
|
+
for _pr in _c.customPars:
|
|
16468
|
+
_name = _pr.name
|
|
16469
|
+
if _include and _name not in _include:
|
|
16470
|
+
continue
|
|
16471
|
+
_keep = ((_pr.isNumber or _pr.isToggle or _pr.isMenu)
|
|
16472
|
+
and not _pr.readOnly
|
|
16473
|
+
and not (_pr.isPulse or _pr.isMomentary)
|
|
16474
|
+
and not _pr.isString)
|
|
16475
|
+
if not _keep:
|
|
16476
|
+
_skipped.append(_name); continue
|
|
16477
|
+
try:
|
|
16478
|
+
_vals[_name] = _pr.eval()
|
|
16479
|
+
_captured.append(_name)
|
|
16480
|
+
except Exception:
|
|
16481
|
+
_skipped.append(_name)
|
|
16482
|
+
# Write the slot column into the looks table (append the column if new).
|
|
16483
|
+
_hdr = _header(_tbl)
|
|
16484
|
+
if _slot not in _hdr:
|
|
16485
|
+
_tbl.appendCol([_slot])
|
|
16486
|
+
_hdr = _header(_tbl)
|
|
16487
|
+
_ci = _hdr.index(_slot)
|
|
16488
|
+
# Clear the column first: a re-store capturing FEWER params must not leave
|
|
16489
|
+
# stale cells behind (recall reads the Table column, skipping blank cells).
|
|
16490
|
+
for _r in range(1, _tbl.numRows):
|
|
16491
|
+
_tbl[_r, _ci] = ''
|
|
16492
|
+
# Ensure a row exists for each captured param, then write repr() into the slot cell.
|
|
16493
|
+
_rows = {_tbl[_r, 0].val: _r for _r in range(1, _tbl.numRows)}
|
|
16494
|
+
for _k, _v in _vals.items():
|
|
16495
|
+
if _k not in _rows:
|
|
16496
|
+
_newrow = [''] * _tbl.numCols
|
|
16497
|
+
_newrow[0] = _k
|
|
16498
|
+
_tbl.appendRow(_newrow)
|
|
16499
|
+
_rows[_k] = _tbl.numRows - 1
|
|
16500
|
+
_tbl[_rows[_k], _ci] = repr(_v)
|
|
16501
|
+
# Mirror into comp_path's tdmcp_cues so the slot is interoperable + recall-able.
|
|
16502
|
+
_cues = dict(_c.fetch("tdmcp_cues", {}))
|
|
16503
|
+
_cues[_slot] = _vals
|
|
16504
|
+
_c.store("tdmcp_cues", _cues)
|
|
16505
|
+
report["slot"] = _slot
|
|
16506
|
+
report["captured"] = sorted(_captured)
|
|
16507
|
+
report["skipped"] = sorted(_skipped)
|
|
16508
|
+
report["slots"] = _slots(_tbl)
|
|
16509
|
+
report["buttons"] = _rebuild_buttons(_bank, _c, _p["button_cb"])
|
|
16510
|
+
elif _action == "recall":
|
|
16511
|
+
_slot = _p.get("slot")
|
|
16512
|
+
_to = _col_dict(_tbl, _slot)
|
|
16513
|
+
if not _to and _slot not in _slots(_tbl):
|
|
16514
|
+
report["fatal"] = "Slot not found: '%s' (available: %s)" % (_slot, ", ".join(_slots(_tbl)) or "none")
|
|
16515
|
+
else:
|
|
16516
|
+
_quant = _p.get("quantize") or "off"
|
|
16517
|
+
_delay = _next_boundary_delay(_quant)
|
|
16518
|
+
_dur = float(_p.get("morph_seconds") or 0)
|
|
16519
|
+
if _dur <= 0 and _delay <= 0.0:
|
|
16520
|
+
_restored = []
|
|
16521
|
+
for _k, _v in _to.items():
|
|
16522
|
+
_pr = getattr(_c.par, _k, None)
|
|
16523
|
+
if _pr is None or _pr.readOnly:
|
|
16524
|
+
report["warnings"].append("skipped " + _k); continue
|
|
16525
|
+
try:
|
|
16526
|
+
_pr.val = _v; _restored.append(_k)
|
|
16527
|
+
except Exception:
|
|
16528
|
+
report["warnings"].append("could not set " + _k)
|
|
16529
|
+
report["restored"] = sorted(_restored)
|
|
16530
|
+
else:
|
|
16531
|
+
_from = {}
|
|
16532
|
+
for _k in _to.keys():
|
|
16533
|
+
_pr = getattr(_c.par, _k, None)
|
|
16534
|
+
if _pr is not None:
|
|
16535
|
+
try:
|
|
16536
|
+
_from[_k] = _pr.eval()
|
|
16537
|
+
except Exception:
|
|
16538
|
+
pass
|
|
16539
|
+
_tdur = _dur if _dur > 0 else 0.0001
|
|
16540
|
+
_start = td.absTime.seconds + _delay
|
|
16541
|
+
_c.store("tdmcp_cue_transition", {"active": True, "from": _from, "to": _to, "start": _start, "duration": _tdur})
|
|
16542
|
+
_ensure_morph(_c, _p["morph_text"])
|
|
16543
|
+
report["restored"] = sorted(_from.keys())
|
|
16544
|
+
if _dur > 0:
|
|
16545
|
+
report["morph_seconds"] = _dur
|
|
16546
|
+
if _delay > 0.0:
|
|
16547
|
+
report["quantize"] = _quant; report["scheduled_in"] = round(_delay, 4)
|
|
16548
|
+
report["slot"] = _slot
|
|
16549
|
+
elif _action == "set_ab":
|
|
16550
|
+
_sa = _p.get("slot_a"); _sb = _p.get("slot_b")
|
|
16551
|
+
_st = dict(_bank.fetch("tdmcp_lookbank_ab", {}))
|
|
16552
|
+
_st["slot_a"] = _sa; _st["slot_b"] = _sb
|
|
16553
|
+
_ab = _p.get("ab")
|
|
16554
|
+
if _ab is not None:
|
|
16555
|
+
_st["ab"] = float(_ab)
|
|
16556
|
+
_bank.store("tdmcp_lookbank_ab", _st)
|
|
16557
|
+
report["slot_a"] = _sa; report["slot_b"] = _sb
|
|
16558
|
+
if _ab is not None:
|
|
16559
|
+
_abp = getattr(_bank.par, "Ab", None)
|
|
16560
|
+
if _abp is not None and not _abp.readOnly:
|
|
16561
|
+
try:
|
|
16562
|
+
_abp.val = float(_ab)
|
|
16563
|
+
except Exception:
|
|
16564
|
+
pass
|
|
16565
|
+
report["ab"] = float(_ab)
|
|
16566
|
+
# Run the blend once so the knob preview is live immediately.
|
|
16567
|
+
_hdr = _header(_tbl)
|
|
16568
|
+
if _sa in _hdr and _sb in _hdr:
|
|
16569
|
+
_da = _col_dict(_tbl, _sa); _dbb = _col_dict(_tbl, _sb)
|
|
16570
|
+
for _k, _av in _da.items():
|
|
16571
|
+
_pr = getattr(_c.par, _k, None)
|
|
16572
|
+
if _pr is None or _pr.readOnly or not getattr(_pr, "isNumber", False):
|
|
16573
|
+
continue
|
|
16574
|
+
_bv = _dbb.get(_k, _av)
|
|
16575
|
+
try:
|
|
16576
|
+
_val = float(_av) + (float(_bv) - float(_av)) * float(_ab)
|
|
16577
|
+
_pr.val = int(round(_val)) if getattr(_pr, "style", "") == "Int" else _val
|
|
16578
|
+
except Exception:
|
|
16579
|
+
pass
|
|
16580
|
+
report["slots"] = _slots(_tbl)
|
|
16581
|
+
elif _action == "delete":
|
|
16582
|
+
_slot = _p.get("slot")
|
|
16583
|
+
_hdr = _header(_tbl)
|
|
16584
|
+
if _slot in _hdr:
|
|
16585
|
+
_tbl.deleteCol(_hdr.index(_slot))
|
|
16586
|
+
_bt = _bank.op(_btn_name(_slot))
|
|
16587
|
+
if _bt is not None:
|
|
16588
|
+
_bt.destroy()
|
|
16589
|
+
_cues = dict(_c.fetch("tdmcp_cues", {}))
|
|
16590
|
+
if _slot in _cues:
|
|
16591
|
+
_cues.pop(_slot, None); _c.store("tdmcp_cues", _cues)
|
|
16592
|
+
report["deleted"] = _slot
|
|
16593
|
+
report["buttons"] = _rebuild_buttons(_bank, _c, _p["button_cb"])
|
|
16594
|
+
else:
|
|
16595
|
+
report["warnings"].append("Slot not found: " + str(_slot))
|
|
16596
|
+
report["slots"] = _slots(_tbl)
|
|
16597
|
+
else:
|
|
16598
|
+
report["fatal"] = "Unknown action: " + str(_action)
|
|
16599
|
+
except Exception:
|
|
16600
|
+
report["fatal"] = traceback.format_exc().splitlines()[-1]
|
|
16601
|
+
print(json.dumps(report))
|
|
16602
|
+
`;
|
|
16603
|
+
function buildLookBankScript(payload) {
|
|
16604
|
+
return buildPayloadScript(LOOKBANK_SCRIPT, payload);
|
|
16605
|
+
}
|
|
16606
|
+
async function createLookBankImpl(ctx, args) {
|
|
16607
|
+
if ((args.action === "store" || args.action === "recall" || args.action === "delete") && !args.slot) {
|
|
16608
|
+
return errorResult(`A slot name is required for the '${args.action}' action.`);
|
|
16609
|
+
}
|
|
16610
|
+
if (args.action === "set_ab" && (!args.slot_a || !args.slot_b)) {
|
|
16611
|
+
return errorResult("set_ab needs both slot_a and slot_b (the two looks the A\u2194B knob blends).");
|
|
16612
|
+
}
|
|
16613
|
+
const RESERVED_SLOT = "param";
|
|
16614
|
+
if ([args.slot, args.slot_a, args.slot_b].includes(RESERVED_SLOT)) {
|
|
16615
|
+
return errorResult(
|
|
16616
|
+
`'${RESERVED_SLOT}' is a reserved slot name (the look table's parameter-key column). Choose a different slot name.`
|
|
16617
|
+
);
|
|
16618
|
+
}
|
|
16619
|
+
return guardTd(
|
|
16620
|
+
async () => {
|
|
16621
|
+
const script = buildLookBankScript({
|
|
16622
|
+
action: args.action,
|
|
16623
|
+
comp: args.comp_path,
|
|
16624
|
+
name: args.name,
|
|
16625
|
+
slot: args.slot,
|
|
16626
|
+
morph_seconds: args.morph_seconds,
|
|
16627
|
+
quantize: args.quantize,
|
|
16628
|
+
slot_a: args.slot_a,
|
|
16629
|
+
slot_b: args.slot_b,
|
|
16630
|
+
ab: args.ab,
|
|
16631
|
+
include: args.include,
|
|
16632
|
+
morph_text: MORPH_HOOK,
|
|
16633
|
+
ab_cb: AB_BLEND_CB,
|
|
16634
|
+
// SURFACE_BUTTON_CB is exported by the integrator (reuse decision A). Coalesce to "" so
|
|
16635
|
+
// this file is runtime-safe before that one-line export lands; an empty dispatcher simply
|
|
16636
|
+
// means no recall buttons fire until the export is applied (build still succeeds).
|
|
16637
|
+
button_cb: SURFACE_BUTTON_CB ?? ""
|
|
16638
|
+
});
|
|
16639
|
+
const exec = await ctx.client.executePythonScript(script, true);
|
|
16640
|
+
return parsePythonReport(exec.stdout);
|
|
16641
|
+
},
|
|
16642
|
+
(report) => {
|
|
16643
|
+
if (report.fatal) {
|
|
16644
|
+
return errorResult(`Look bank ${report.action} failed: ${report.fatal}`, report);
|
|
16645
|
+
}
|
|
16646
|
+
let summary;
|
|
16647
|
+
switch (report.action) {
|
|
16648
|
+
case "build":
|
|
16649
|
+
summary = `Built look bank ${report.bank} on ${report.comp} (${report.slots?.length ?? 0} slot(s), A\u2194B morph knob ready).`;
|
|
16650
|
+
break;
|
|
16651
|
+
case "store": {
|
|
16652
|
+
const cap = report.captured?.length ?? 0;
|
|
16653
|
+
const skip = report.skipped?.length ?? 0;
|
|
16654
|
+
summary = `Stored look "${report.slot}" (${cap} control(s) captured${skip ? `, ${skip} skipped` : ""}) on ${report.comp}.`;
|
|
16655
|
+
break;
|
|
16656
|
+
}
|
|
16657
|
+
case "recall": {
|
|
16658
|
+
const n = report.restored?.length ?? 0;
|
|
16659
|
+
if (report.quantize) {
|
|
16660
|
+
summary = `Look "${report.slot}" (${n} control(s)) ${report.morph_seconds ? `crossfades over ${report.morph_seconds}s` : "snaps"} on the next ${report.quantize} (~${report.scheduled_in}s) on ${report.comp}.`;
|
|
16661
|
+
} else if (report.morph_seconds) {
|
|
16662
|
+
summary = `Crossfading to look "${report.slot}" over ${report.morph_seconds}s (${n} control(s)) on ${report.comp}.`;
|
|
16663
|
+
} else {
|
|
16664
|
+
summary = `Recalled look "${report.slot}" (${n} control(s)) \u2014 jumped on ${report.comp}.`;
|
|
16665
|
+
}
|
|
16666
|
+
break;
|
|
16667
|
+
}
|
|
16668
|
+
case "set_ab":
|
|
16669
|
+
summary = report.ab === void 0 ? `A\u2194B knob (re)assigned: A="${report.slot_a}" \u2192 B="${report.slot_b}" on ${report.comp} (knob not moved).` : `A\u2194B knob set to ${report.ab} blending A="${report.slot_a}" \u2192 B="${report.slot_b}" on ${report.comp}.`;
|
|
16670
|
+
break;
|
|
16671
|
+
case "delete":
|
|
16672
|
+
summary = report.deleted ? `Deleted look "${report.deleted}" on ${report.comp}.` : `No look to delete on ${report.comp}.`;
|
|
16673
|
+
break;
|
|
16674
|
+
default:
|
|
16675
|
+
summary = `${report.slots?.length ?? 0} look(s) on ${report.comp}: ${report.slots?.join(", ") || "no slots"}.`;
|
|
16676
|
+
}
|
|
16677
|
+
if (report.warnings.length) summary += ` ${report.warnings.length} warning(s).`;
|
|
16678
|
+
return jsonResult(summary, report);
|
|
16679
|
+
}
|
|
16680
|
+
);
|
|
16681
|
+
}
|
|
16682
|
+
|
|
16683
|
+
// src/tools/layer2/createMacro.ts
|
|
16684
|
+
import { z as z99 } from "zod";
|
|
16685
|
+
var macroTargetSchema = z99.object({
|
|
16686
|
+
param: z99.string().describe("Parameter to drive, written as 'nodePath.parName'."),
|
|
16687
|
+
min: z99.coerce.number().describe("Value this target takes when the macro is at 0."),
|
|
16688
|
+
max: z99.coerce.number().describe("Value this target takes when the macro is at 1."),
|
|
16689
|
+
curve: z99.coerce.number().positive().default(1).describe("Response curve exponent: 1 = linear, >1 = ease-in, <1 = ease-out.")
|
|
16032
16690
|
});
|
|
16033
|
-
var createMacroSchema =
|
|
16034
|
-
comp_path:
|
|
16035
|
-
name:
|
|
16036
|
-
default:
|
|
16037
|
-
targets:
|
|
16691
|
+
var createMacroSchema = z99.object({
|
|
16692
|
+
comp_path: z99.string().default("/project1").describe("COMP that will hold the macro knob (usually a control-panel container)."),
|
|
16693
|
+
name: z99.string().describe("Macro control name, e.g. 'Energy' or 'Intensity'."),
|
|
16694
|
+
default: z99.coerce.number().min(0).max(1).default(0).describe("Initial macro value (0\u20131)."),
|
|
16695
|
+
targets: z99.array(macroTargetSchema).min(1).describe("Parameters this macro drives, each remapped from the macro's 0\u20131 into [min,max].")
|
|
16038
16696
|
});
|
|
16039
16697
|
var MACRO_SCRIPT = `
|
|
16040
16698
|
import json, base64, traceback
|
|
@@ -16112,7 +16770,7 @@ async function createMacroImpl(ctx, args) {
|
|
|
16112
16770
|
}
|
|
16113
16771
|
|
|
16114
16772
|
// src/tools/layer2/createMidiMap.ts
|
|
16115
|
-
import { z as
|
|
16773
|
+
import { z as z100 } from "zod";
|
|
16116
16774
|
var DEVICE_PRESETS = {
|
|
16117
16775
|
// Akai APC Mini Mk1 — UNVERIFIED
|
|
16118
16776
|
// Faders: CC 48-55 on ch1; pads: note-on on ch1 (rows 0-7, cols 0-7)
|
|
@@ -16237,23 +16895,23 @@ var DEVICE_PRESETS = {
|
|
|
16237
16895
|
// generic: bare MIDI In + empty template table — no preset bindings
|
|
16238
16896
|
generic: []
|
|
16239
16897
|
};
|
|
16240
|
-
var bindingSchema =
|
|
16241
|
-
control:
|
|
16898
|
+
var bindingSchema = z100.object({
|
|
16899
|
+
control: z100.string().describe(
|
|
16242
16900
|
"Control id from the device preset map, e.g. 'fader1', 'knob3', 'pad5'. For 'generic' device, use any label you choose."
|
|
16243
16901
|
),
|
|
16244
|
-
target_param:
|
|
16245
|
-
cue:
|
|
16902
|
+
target_param: z100.string().optional().describe("'nodePath.parName' to drive (for continuous controls)."),
|
|
16903
|
+
cue: z100.string().optional().describe("Cue name to recall when this pad is triggered.")
|
|
16246
16904
|
});
|
|
16247
|
-
var createMidiMapSchema =
|
|
16248
|
-
parent_path:
|
|
16249
|
-
name:
|
|
16250
|
-
device:
|
|
16905
|
+
var createMidiMapSchema = z100.object({
|
|
16906
|
+
parent_path: z100.string().default("/project1").describe("COMP to create the MIDI map inside."),
|
|
16907
|
+
name: z100.string().default("midi_map").describe("Name for the MIDI In CHOP node created under parent_path."),
|
|
16908
|
+
device: z100.enum(["apc_mini", "launchpad", "midi_mix", "nanokontrol", "generic"]).default("nanokontrol").describe(
|
|
16251
16909
|
"Controller preset. Each preset embeds a best-effort CC/note map for that device (UNVERIFIED \u2014 real numbers depend on firmware; validate with hardware). 'generic' builds a bare MIDI In + a template bind table with no preset."
|
|
16252
16910
|
),
|
|
16253
|
-
target:
|
|
16911
|
+
target: z100.string().optional().describe(
|
|
16254
16912
|
"COMP whose custom numeric params/cues the preset auto-binds faders/knobs onto. Faders bind to the first N float/int custom pars; pads look for matching cues. Auto-binding is best-effort and hardware-gated."
|
|
16255
16913
|
),
|
|
16256
|
-
bindings:
|
|
16914
|
+
bindings: z100.array(bindingSchema).default([]).describe(
|
|
16257
16915
|
"Explicit control\u2192param/cue overrides. Applied after the preset auto-map. Omit to rely entirely on the device preset's default map."
|
|
16258
16916
|
)
|
|
16259
16917
|
});
|
|
@@ -16417,17 +17075,277 @@ async function createMidiMapImpl(ctx, args) {
|
|
|
16417
17075
|
);
|
|
16418
17076
|
}
|
|
16419
17077
|
|
|
16420
|
-
// src/tools/layer2/
|
|
16421
|
-
import { z as
|
|
16422
|
-
var
|
|
16423
|
-
|
|
16424
|
-
|
|
16425
|
-
|
|
17078
|
+
// src/tools/layer2/createModulators.ts
|
|
17079
|
+
import { z as z101 } from "zod";
|
|
17080
|
+
var modulatorSchema = z101.object({
|
|
17081
|
+
name: z101.string().optional().describe(
|
|
17082
|
+
"Output channel name for this modulator (e.g. 'breathe', 'sweep'). Sanitized to a valid channel name; defaults to mod1, mod2, \u2026"
|
|
17083
|
+
),
|
|
17084
|
+
shape: z101.enum(["sine", "triangle", "saw", "square", "random"]).default("sine").describe(
|
|
17085
|
+
"Oscillator shape. 'saw' is a rising ramp; 'random' is a sample-&-hold (a new random value held each cycle) built from a Noise CHOP. Every shape sweeps the full depth range."
|
|
17086
|
+
),
|
|
17087
|
+
rate_beats: z101.coerce.number().positive().default(4).describe(
|
|
17088
|
+
"Cycle length in BEATS \u2014 how many beats one full cycle takes. 4 = one cycle per bar (slow breathe), 1 = one cycle per beat, 0.5 = twice per beat (eighths). Locked to the tempo source so it tracks BPM."
|
|
17089
|
+
),
|
|
17090
|
+
depth_min: z101.coerce.number().default(0).describe("Low end of this modulator's output range."),
|
|
17091
|
+
depth_max: z101.coerce.number().default(1).describe("High end of this modulator's output range."),
|
|
17092
|
+
phase: z101.coerce.number().min(0).max(1).default(0).describe(
|
|
17093
|
+
"Phase offset 0\u20131 (fraction of a cycle). Use to fan a bank out of step (e.g. 0, 0.25, 0.5, 0.75)."
|
|
17094
|
+
)
|
|
16426
17095
|
});
|
|
16427
|
-
var
|
|
16428
|
-
|
|
16429
|
-
|
|
16430
|
-
|
|
17096
|
+
var createModulatorsSchema = z101.object({
|
|
17097
|
+
name: z101.string().default("modulators").describe("Name of the self-contained modulator-bank container."),
|
|
17098
|
+
parent_path: z101.string().default("/project1").describe("Parent COMP the 'modulators' container is created inside."),
|
|
17099
|
+
modulators: z101.array(modulatorSchema).min(1).max(32).describe(
|
|
17100
|
+
"The modulators (LFOs) to build. Each becomes one named output channel on the bank's Null."
|
|
17101
|
+
),
|
|
17102
|
+
tempo_source: z101.string().optional().describe(
|
|
17103
|
+
"Path to an existing tempo Null/Beat CHOP carrying a 'bpm' channel (e.g. the Null from create_tempo_sync, '/project1/tempo_sync/tempo'). Omit to create a fresh Beat CHOP locked to TouchDesigner's global tempo inside the bank."
|
|
17104
|
+
),
|
|
17105
|
+
bpm_channel: z101.string().default("bpm").describe("Name of the BPM channel on the tempo source to lock rates to."),
|
|
17106
|
+
expose_controls: z101.boolean().default(true).describe(
|
|
17107
|
+
"Expose a live custom-parameter page on the bank: a master Rate multiplier and a master Depth (amplitude) scale, so you can speed up or flatten the whole bank from one knob during a show."
|
|
17108
|
+
)
|
|
17109
|
+
});
|
|
17110
|
+
var WAVE_MAP3 = {
|
|
17111
|
+
sine: "sin",
|
|
17112
|
+
triangle: "tri",
|
|
17113
|
+
saw: "ramp",
|
|
17114
|
+
square: "square"
|
|
17115
|
+
};
|
|
17116
|
+
var BIPOLAR_SHAPES = /* @__PURE__ */ new Set(["sine", "triangle", "square"]);
|
|
17117
|
+
function toChannelName(value, index) {
|
|
17118
|
+
let name = value.replace(/[^a-zA-Z0-9_]/g, "");
|
|
17119
|
+
if (!name) name = `mod${index + 1}`;
|
|
17120
|
+
if (!/^[a-zA-Z]/.test(name)) name = `m${name}`;
|
|
17121
|
+
return name;
|
|
17122
|
+
}
|
|
17123
|
+
function pyStr(value) {
|
|
17124
|
+
return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
|
|
17125
|
+
}
|
|
17126
|
+
var MODULATORS_SCRIPT = `
|
|
17127
|
+
import json, base64, traceback
|
|
17128
|
+
_p = json.loads(base64.b64decode("__PAYLOAD_B64__").decode("utf-8"))
|
|
17129
|
+
report = {
|
|
17130
|
+
"comp": "", "out_chop": "", "channels": [], "tempo_source": "",
|
|
17131
|
+
"beat_created": False, "modulators": [], "time_playing": False,
|
|
17132
|
+
"warnings": [],
|
|
17133
|
+
}
|
|
17134
|
+
try:
|
|
17135
|
+
report["time_playing"] = bool(op('/').time.play)
|
|
17136
|
+
_parent = op(_p["parent_path"])
|
|
17137
|
+
if _parent is None:
|
|
17138
|
+
report["fatal"] = "Parent not found: " + _p["parent_path"]
|
|
17139
|
+
elif not hasattr(_parent, "create"):
|
|
17140
|
+
report["fatal"] = _p["parent_path"] + " cannot contain operators."
|
|
17141
|
+
else:
|
|
17142
|
+
# Reuse-or-create the named container so it is NEVER auto-renamed on a
|
|
17143
|
+
# collision: a bare create() would yield e.g. "modulators1" while the
|
|
17144
|
+
# pre-built freq/rate expressions still point at parent_path/name, binding
|
|
17145
|
+
# the bank to the wrong/missing tempo control. Clear children so a re-run
|
|
17146
|
+
# rebuilds cleanly at deterministic child paths (beat / lfo_* / mod_out).
|
|
17147
|
+
_comp = _parent.op(_p["name"])
|
|
17148
|
+
if _comp is not None and not hasattr(_comp, "create"):
|
|
17149
|
+
raise TypeError("%s exists and is not a container; pass a different name." % _comp.path)
|
|
17150
|
+
_comp = _comp or _parent.create(containerCOMP, _p["name"])
|
|
17151
|
+
for _old in list(_comp.children):
|
|
17152
|
+
_old.destroy()
|
|
17153
|
+
report["comp"] = _comp.path
|
|
17154
|
+
|
|
17155
|
+
# Resolve the tempo source: an external Null/Beat CHOP, or a fresh internal Beat CHOP.
|
|
17156
|
+
_tempo_path = _p.get("tempo_source")
|
|
17157
|
+
if _tempo_path:
|
|
17158
|
+
if op(_tempo_path) is None:
|
|
17159
|
+
report["warnings"].append("Tempo source not found: %s (rates will read 0 until it exists)." % _tempo_path)
|
|
17160
|
+
else:
|
|
17161
|
+
_beat = _comp.create(beatCHOP, "beat")
|
|
17162
|
+
try:
|
|
17163
|
+
_beat.par.bpm = 1
|
|
17164
|
+
except Exception:
|
|
17165
|
+
pass
|
|
17166
|
+
_tempo_path = _beat.path
|
|
17167
|
+
report["beat_created"] = True
|
|
17168
|
+
report["tempo_source"] = _tempo_path
|
|
17169
|
+
|
|
17170
|
+
_merge = _comp.create(mergeCHOP, "merge")
|
|
17171
|
+
_src_ops = []
|
|
17172
|
+
for _m in _p["modulators"]:
|
|
17173
|
+
try:
|
|
17174
|
+
if _m["kind"] == "noise":
|
|
17175
|
+
_o = _comp.create(noiseCHOP, "noise_" + _m["channel"])
|
|
17176
|
+
_o.par.type = "random"
|
|
17177
|
+
try:
|
|
17178
|
+
_o.par.periodunit = "seconds"
|
|
17179
|
+
except Exception:
|
|
17180
|
+
pass
|
|
17181
|
+
_pp = _o.par.period
|
|
17182
|
+
_PM = type(_pp.mode)
|
|
17183
|
+
_pp.expr = _m["period_expr"]
|
|
17184
|
+
_pp.mode = _PM.EXPRESSION
|
|
17185
|
+
else:
|
|
17186
|
+
_o = _comp.create(lfoCHOP, "lfo_" + _m["channel"])
|
|
17187
|
+
_o.par.wavetype = _m["wavetype"]
|
|
17188
|
+
_o.par.phase = _m["phase"]
|
|
17189
|
+
_fp = _o.par.frequency
|
|
17190
|
+
_PM = type(_fp.mode)
|
|
17191
|
+
_fp.expr = _m["freq_expr"]
|
|
17192
|
+
_fp.mode = _PM.EXPRESSION
|
|
17193
|
+
_o.par.amp = _m["amp"]
|
|
17194
|
+
_o.par.offset = _m["offset"]
|
|
17195
|
+
_o.par.channelname = _m["channel"]
|
|
17196
|
+
_src_ops.append(_o)
|
|
17197
|
+
report["modulators"].append({"op": _o.path, "channel": _m["channel"], "shape": _m["shape"], "rate_beats": _m["rate_beats"]})
|
|
17198
|
+
report["channels"].append(_m["channel"])
|
|
17199
|
+
except Exception:
|
|
17200
|
+
report["warnings"].append("Failed to build modulator '%s': %s" % (_m.get("channel", "?"), traceback.format_exc().splitlines()[-1]))
|
|
17201
|
+
|
|
17202
|
+
for _i, _o in enumerate(_src_ops):
|
|
17203
|
+
try:
|
|
17204
|
+
_merge.inputConnectors[_i].connect(_o)
|
|
17205
|
+
except Exception:
|
|
17206
|
+
report["warnings"].append("Failed to wire %s into merge." % _o.path)
|
|
17207
|
+
|
|
17208
|
+
_tail = _merge
|
|
17209
|
+
_controls = []
|
|
17210
|
+
if _p["expose_controls"]:
|
|
17211
|
+
_depth = _comp.op("depth") or _comp.create(mathCHOP, "depth")
|
|
17212
|
+
_depth.inputConnectors[0].connect(_merge)
|
|
17213
|
+
# Reuse existing Rate/Depth controls on a re-run: children were cleared,
|
|
17214
|
+
# but the container's custom page/params persist \u2014 re-appending would make
|
|
17215
|
+
# Rate1/Depth1 while the expressions still bind .par.Rate/.par.Depth.
|
|
17216
|
+
_rate_par = getattr(_comp.par, "Rate", None)
|
|
17217
|
+
_depth_par = getattr(_comp.par, "Depth", None)
|
|
17218
|
+
if _rate_par is None or _depth_par is None:
|
|
17219
|
+
_page = _comp.appendCustomPage("Modulators")
|
|
17220
|
+
if _rate_par is None:
|
|
17221
|
+
_rate_par = _page.appendFloat("Rate", label="Rate")[0]
|
|
17222
|
+
if _depth_par is None:
|
|
17223
|
+
_depth_par = _page.appendFloat("Depth", label="Depth")[0]
|
|
17224
|
+
_rate_par.normMin = 0.0; _rate_par.normMax = 4.0
|
|
17225
|
+
_rate_par.default = 1.0; _rate_par.val = 1.0
|
|
17226
|
+
_depth_par.normMin = 0.0; _depth_par.normMax = 2.0
|
|
17227
|
+
_depth_par.default = 1.0; _depth_par.val = 1.0
|
|
17228
|
+
# Master Depth scales the whole bank's amplitude via the Math CHOP gain.
|
|
17229
|
+
try:
|
|
17230
|
+
_gp = _depth.par.gain
|
|
17231
|
+
_PM = type(_gp.mode)
|
|
17232
|
+
_gp.expr = "op(%s).par.Depth" % repr(_comp.path)
|
|
17233
|
+
_gp.mode = _PM.EXPRESSION
|
|
17234
|
+
except Exception:
|
|
17235
|
+
report["warnings"].append("Could not bind master Depth to the Math CHOP gain.")
|
|
17236
|
+
_tail = _depth
|
|
17237
|
+
_controls = ["Rate", "Depth"]
|
|
17238
|
+
report["controls"] = _controls
|
|
17239
|
+
|
|
17240
|
+
_out = _comp.create(nullCHOP, "mod_out")
|
|
17241
|
+
_out.inputConnectors[0].connect(_tail)
|
|
17242
|
+
report["out_chop"] = _out.path
|
|
17243
|
+
|
|
17244
|
+
# Warn on any duplicate channel names that survived TS de-duplication (defensive).
|
|
17245
|
+
_seen = {}
|
|
17246
|
+
for _c in report["channels"]:
|
|
17247
|
+
_seen[_c] = _seen.get(_c, 0) + 1
|
|
17248
|
+
for _c, _n in _seen.items():
|
|
17249
|
+
if _n > 1:
|
|
17250
|
+
report["warnings"].append("Duplicate channel name '%s' (x%d) \u2014 Merge keeps only one." % (_c, _n))
|
|
17251
|
+
except Exception:
|
|
17252
|
+
report["fatal"] = traceback.format_exc().splitlines()[-1]
|
|
17253
|
+
print(json.dumps(report))
|
|
17254
|
+
`;
|
|
17255
|
+
function buildModulatorsScript(payload) {
|
|
17256
|
+
return buildPayloadScript(MODULATORS_SCRIPT, payload);
|
|
17257
|
+
}
|
|
17258
|
+
function buildModulatorSpecs(args, tempoOp) {
|
|
17259
|
+
const dupeWarnings = [];
|
|
17260
|
+
const usedNames = /* @__PURE__ */ new Set();
|
|
17261
|
+
const masterRate = args.expose_controls ? ` * op(${pyStr(`${args.parent_path}/${args.name}`)}).par.Rate` : "";
|
|
17262
|
+
const masterRateDiv = args.expose_controls ? ` / max(op(${pyStr(`${args.parent_path}/${args.name}`)}).par.Rate, 1e-6)` : "";
|
|
17263
|
+
const specs = args.modulators.map((m, i) => {
|
|
17264
|
+
const base = toChannelName(m.name ?? `mod${i + 1}`, i);
|
|
17265
|
+
let channel = base;
|
|
17266
|
+
if (usedNames.has(channel)) {
|
|
17267
|
+
let n = 2;
|
|
17268
|
+
while (usedNames.has(`${base}_${n}`)) n++;
|
|
17269
|
+
channel = `${base}_${n}`;
|
|
17270
|
+
dupeWarnings.push(
|
|
17271
|
+
`Duplicate channel name '${base}' renamed to '${channel}' so both modulators survive the Merge.`
|
|
17272
|
+
);
|
|
17273
|
+
}
|
|
17274
|
+
usedNames.add(channel);
|
|
17275
|
+
const span = m.depth_max - m.depth_min;
|
|
17276
|
+
const bipolar = BIPOLAR_SHAPES.has(m.shape);
|
|
17277
|
+
const amp = bipolar ? span / 2 : span;
|
|
17278
|
+
const offset = bipolar ? (m.depth_max + m.depth_min) / 2 : m.depth_min;
|
|
17279
|
+
const cpb = 1 / m.rate_beats;
|
|
17280
|
+
const beatsPerCycle = m.rate_beats;
|
|
17281
|
+
const isNoise = m.shape === "random";
|
|
17282
|
+
const tempoExpr = `op(${pyStr(tempoOp)})`;
|
|
17283
|
+
const tempoRef = `(${tempoExpr}[${pyStr(args.bpm_channel)}] if ${tempoExpr} else 0.0)`;
|
|
17284
|
+
return {
|
|
17285
|
+
channel,
|
|
17286
|
+
shape: m.shape,
|
|
17287
|
+
kind: isNoise ? "noise" : "lfo",
|
|
17288
|
+
wavetype: isNoise ? void 0 : WAVE_MAP3[m.shape],
|
|
17289
|
+
amp,
|
|
17290
|
+
offset,
|
|
17291
|
+
phase: m.phase,
|
|
17292
|
+
cpb,
|
|
17293
|
+
beats_per_cycle: beatsPerCycle,
|
|
17294
|
+
rate_beats: m.rate_beats,
|
|
17295
|
+
freq_expr: isNoise ? void 0 : `${tempoRef} / 60.0 * ${cpb}${masterRate}`,
|
|
17296
|
+
period_expr: isNoise ? args.expose_controls ? `(60.0 / max(${tempoRef}, 1e-6) * ${beatsPerCycle})${masterRateDiv}` : `60.0 / max(${tempoRef}, 1e-6) * ${beatsPerCycle}` : void 0
|
|
17297
|
+
};
|
|
17298
|
+
});
|
|
17299
|
+
return { specs, dupeWarnings };
|
|
17300
|
+
}
|
|
17301
|
+
async function createModulatorsImpl(ctx, args) {
|
|
17302
|
+
const tempoOp = args.tempo_source ?? `${args.parent_path}/${args.name}/beat`;
|
|
17303
|
+
const { specs, dupeWarnings } = buildModulatorSpecs(args, tempoOp);
|
|
17304
|
+
return guardTd(
|
|
17305
|
+
async () => {
|
|
17306
|
+
const script = buildModulatorsScript({
|
|
17307
|
+
name: args.name,
|
|
17308
|
+
parent_path: args.parent_path,
|
|
17309
|
+
tempo_source: args.tempo_source ?? null,
|
|
17310
|
+
bpm_channel: args.bpm_channel,
|
|
17311
|
+
expose_controls: args.expose_controls,
|
|
17312
|
+
modulators: specs
|
|
17313
|
+
});
|
|
17314
|
+
const exec = await ctx.client.executePythonScript(script, true);
|
|
17315
|
+
return parsePythonReport(exec.stdout);
|
|
17316
|
+
},
|
|
17317
|
+
(report) => {
|
|
17318
|
+
if (report.fatal) {
|
|
17319
|
+
return errorResult(`Could not build modulators: ${report.fatal}`, report);
|
|
17320
|
+
}
|
|
17321
|
+
const warnings = [...dupeWarnings, ...report.warnings];
|
|
17322
|
+
const tempoLabel = report.beat_created ? "TD global tempo" : report.tempo_source;
|
|
17323
|
+
let summary = `Built a bank of ${report.channels.length} BPM-synced modulator(s) at ${report.out_chop} (channels: ${report.channels.join(", ")}), locked to ${tempoLabel}. Bind with bind_to_channel(source_chop="${report.out_chop}", channel="${report.channels[0] ?? "mod1"}").`;
|
|
17324
|
+
if (report.controls?.length) {
|
|
17325
|
+
summary += ` Master controls exposed: ${report.controls.join(", ")}.`;
|
|
17326
|
+
}
|
|
17327
|
+
if (!report.time_playing) {
|
|
17328
|
+
summary += " \u26A0 The timeline is paused \u2014 modulators are frozen until you press Play (they're timeline-driven).";
|
|
17329
|
+
}
|
|
17330
|
+
if (warnings.length) {
|
|
17331
|
+
summary += ` ${warnings.length} warning(s).`;
|
|
17332
|
+
}
|
|
17333
|
+
return jsonResult(summary, { ...report, warnings });
|
|
17334
|
+
}
|
|
17335
|
+
);
|
|
17336
|
+
}
|
|
17337
|
+
|
|
17338
|
+
// src/tools/layer2/createNodeChain.ts
|
|
17339
|
+
import { z as z102 } from "zod";
|
|
17340
|
+
var ChainNodeSchema = z102.object({
|
|
17341
|
+
type: z102.string().describe("Operator type, e.g. 'noiseTOP'."),
|
|
17342
|
+
name: z102.string().optional().describe("Name for this node; auto-generated when omitted."),
|
|
17343
|
+
parameters: z102.record(z102.string(), z102.unknown()).optional().describe("Initial parameter values for this node, as a { parName: value } map.")
|
|
17344
|
+
});
|
|
17345
|
+
var createNodeChainSchema = z102.object({
|
|
17346
|
+
parent_path: z102.string().describe("Parent COMP to create the chain inside."),
|
|
17347
|
+
nodes: z102.array(ChainNodeSchema).min(1).describe("Ordered list of nodes to create."),
|
|
17348
|
+
connect_sequentially: z102.boolean().default(true).describe("Wire output[0] \u2192 input[0] for each consecutive pair.")
|
|
16431
17349
|
});
|
|
16432
17350
|
async function createNodeChainImpl(ctx, args) {
|
|
16433
17351
|
const created = [];
|
|
@@ -16496,31 +17414,31 @@ async function createNodeChainImpl(ctx, args) {
|
|
|
16496
17414
|
}
|
|
16497
17415
|
|
|
16498
17416
|
// src/tools/layer2/createPalette.ts
|
|
16499
|
-
import { z as
|
|
17417
|
+
import { z as z103 } from "zod";
|
|
16500
17418
|
var MAX_SWATCHES = 13;
|
|
16501
17419
|
var PALETTE_RULES = ["complementary", "analogous", "triad", "tetrad", "monochrome"];
|
|
16502
|
-
var createPaletteSchema =
|
|
16503
|
-
mode:
|
|
17420
|
+
var createPaletteSchema = z103.object({
|
|
17421
|
+
mode: z103.enum(["harmony", "from_source"]).default("harmony").describe(
|
|
16504
17422
|
"How swatches are derived: 'harmony' computes them from a base hue + a colour-theory rule (pure maths); 'from_source' samples dominant colours from a source TOP."
|
|
16505
17423
|
),
|
|
16506
|
-
base_hue:
|
|
17424
|
+
base_hue: z103.coerce.number().min(0).max(1).default(0).describe(
|
|
16507
17425
|
"(harmony) Base hue on the colour wheel, 0..1 (0 = red, 0.333 = green, 0.666 = blue)."
|
|
16508
17426
|
),
|
|
16509
|
-
saturation:
|
|
16510
|
-
value:
|
|
16511
|
-
rule:
|
|
17427
|
+
saturation: z103.coerce.number().min(0).max(1).default(0.7).describe("(harmony) Base saturation 0..1 (0 = grey, 1 = vivid)."),
|
|
17428
|
+
value: z103.coerce.number().min(0).max(1).default(0.9).describe("(harmony) Base value / brightness 0..1."),
|
|
17429
|
+
rule: z103.enum(PALETTE_RULES).default("triad").describe(
|
|
16512
17430
|
"(harmony) Colour-theory spread: complementary (base + opposite), analogous (neighbours), triad (3 evenly spaced), tetrad (4 evenly spaced), monochrome (one hue, varied brightness)."
|
|
16513
17431
|
),
|
|
16514
|
-
count:
|
|
17432
|
+
count: z103.coerce.number().int().min(1).max(MAX_SWATCHES).default(5).describe(
|
|
16515
17433
|
`Number of swatches to produce (1..${MAX_SWATCHES}; capped because the swatch Constant CHOP holds 40 channels = 13 RGB swatches).`
|
|
16516
17434
|
),
|
|
16517
|
-
analogous_spread:
|
|
16518
|
-
source:
|
|
17435
|
+
analogous_spread: z103.coerce.number().min(0).max(0.5).default(0.083).describe("(harmony, analogous rule) Hue step between neighbours, 0..0.5 (0.083 \u2248 30\xB0)."),
|
|
17436
|
+
source: z103.string().optional().describe(
|
|
16519
17437
|
"(from_source) Absolute path of a TOP to sample dominant colours from. It is down-res'd to a tiny image and its pixels are read back; if missing/unreadable the palette falls back to a neutral greyscale ramp."
|
|
16520
17438
|
),
|
|
16521
|
-
parent_path:
|
|
16522
|
-
name:
|
|
16523
|
-
expose_controls:
|
|
17439
|
+
parent_path: z103.string().default("/project1").describe("COMP to build the Ramp TOP + swatch CHOP inside."),
|
|
17440
|
+
name: z103.string().default("palette").describe("Base name for the created nodes."),
|
|
17441
|
+
expose_controls: z103.boolean().default(true).describe("Add BaseHue / Saturation / Value / Rule / Count custom parameters to parent_path.")
|
|
16524
17442
|
});
|
|
16525
17443
|
function wrapHue(h) {
|
|
16526
17444
|
const x = h % 1;
|
|
@@ -16746,22 +17664,22 @@ async function createPaletteImpl(ctx, args) {
|
|
|
16746
17664
|
}
|
|
16747
17665
|
|
|
16748
17666
|
// src/tools/layer2/createPanic.ts
|
|
16749
|
-
import { z as
|
|
17667
|
+
import { z as z104 } from "zod";
|
|
16750
17668
|
var q57 = (value) => JSON.stringify(value);
|
|
16751
|
-
var createPanicSchema =
|
|
16752
|
-
input_path:
|
|
17669
|
+
var createPanicSchema = z104.object({
|
|
17670
|
+
input_path: z104.string().optional().describe(
|
|
16753
17671
|
"Optional absolute path of the live source TOP to protect. Pulled in via a Select TOP (TD wires can't cross containers, so it's referenced by path). If omitted, a built-in test source (Ramp TOP) is used so the panic COMP still builds and previews on its own."
|
|
16754
17672
|
),
|
|
16755
|
-
blackout:
|
|
17673
|
+
blackout: z104.boolean().default(false).describe(
|
|
16756
17674
|
"Initial Blackout state. When on, the output is forced to black (Level TOP brightness1 = 0) \u2014 the instant kill switch."
|
|
16757
17675
|
),
|
|
16758
|
-
freeze:
|
|
17676
|
+
freeze: z104.boolean().default(false).describe(
|
|
16759
17677
|
"Initial Freeze state. When on, the last frame is held instead of passing the live input (Cache TOP stops capturing \u2014 active = 0)."
|
|
16760
17678
|
),
|
|
16761
|
-
expose_controls:
|
|
17679
|
+
expose_controls: z104.boolean().default(true).describe(
|
|
16762
17680
|
"Expose big Blackout and Freeze toggle buttons on the container so a performer can hit them instantly."
|
|
16763
17681
|
),
|
|
16764
|
-
parent_path:
|
|
17682
|
+
parent_path: z104.string().default("/project1").describe("Parent COMP the panic container is built inside (default '/project1').")
|
|
16765
17683
|
});
|
|
16766
17684
|
async function createPanicImpl(ctx, args) {
|
|
16767
17685
|
return runBuild(async () => {
|
|
@@ -16840,10 +17758,10 @@ _p.mode = type(_p.mode).EXPRESSION`
|
|
|
16840
17758
|
}
|
|
16841
17759
|
|
|
16842
17760
|
// src/tools/layer2/createPhoneRemote.ts
|
|
16843
|
-
import { z as
|
|
16844
|
-
var createPhoneRemoteSchema =
|
|
16845
|
-
comp_path:
|
|
16846
|
-
port:
|
|
17761
|
+
import { z as z105 } from "zod";
|
|
17762
|
+
var createPhoneRemoteSchema = z105.object({
|
|
17763
|
+
comp_path: z105.string().default("/project1").describe("Control COMP whose numeric custom parameters the phone page exposes."),
|
|
17764
|
+
port: z105.coerce.number().int().positive().max(65535).default(9981).describe("TCP port for the remote web server (keep it distinct from the bridge's 9980).")
|
|
16847
17765
|
});
|
|
16848
17766
|
var REMOTE_CALLBACKS = `from urllib.parse import urlparse, parse_qs
|
|
16849
17767
|
|
|
@@ -16969,17 +17887,17 @@ async function createPhoneRemoteImpl(ctx, args) {
|
|
|
16969
17887
|
}
|
|
16970
17888
|
|
|
16971
17889
|
// src/tools/layer2/createPythonScript.ts
|
|
16972
|
-
import { z as
|
|
17890
|
+
import { z as z106 } from "zod";
|
|
16973
17891
|
var TYPE_MAP = {
|
|
16974
17892
|
text: "textDAT",
|
|
16975
17893
|
execute: "executeDAT",
|
|
16976
17894
|
script: "scriptDAT"
|
|
16977
17895
|
};
|
|
16978
|
-
var createPythonScriptSchema =
|
|
16979
|
-
parent_path:
|
|
16980
|
-
name:
|
|
16981
|
-
code:
|
|
16982
|
-
dat_type:
|
|
17896
|
+
var createPythonScriptSchema = z106.object({
|
|
17897
|
+
parent_path: z106.string().describe("Parent COMP to create the DAT inside."),
|
|
17898
|
+
name: z106.string().optional().describe("Name for the new DAT; auto-generated when omitted."),
|
|
17899
|
+
code: z106.string().min(1).describe("Python source to place in the DAT."),
|
|
17900
|
+
dat_type: z106.enum(["text", "execute", "script"]).default("text").describe("Kind of DAT: 'text' (plain), 'execute' (event hooks), or 'script' (table builder).")
|
|
16983
17901
|
});
|
|
16984
17902
|
async function createPythonScriptImpl(ctx, args) {
|
|
16985
17903
|
return guardTd(
|
|
@@ -17010,16 +17928,16 @@ async function createPythonScriptImpl(ctx, args) {
|
|
|
17010
17928
|
}
|
|
17011
17929
|
|
|
17012
17930
|
// src/tools/layer2/createReplicator.ts
|
|
17013
|
-
import { z as
|
|
17014
|
-
var createReplicatorSchema =
|
|
17015
|
-
parent_path:
|
|
17016
|
-
name:
|
|
17017
|
-
template_path:
|
|
17931
|
+
import { z as z107 } from "zod";
|
|
17932
|
+
var createReplicatorSchema = z107.object({
|
|
17933
|
+
parent_path: z107.string().default("/project1").describe("COMP to build the replicator inside."),
|
|
17934
|
+
name: z107.string().default("replicator1").describe("Name for the Replicator COMP."),
|
|
17935
|
+
template_path: z107.string().optional().describe(
|
|
17018
17936
|
"Existing COMP to clone per row. Omit \u2192 create a minimal template COMP (a container with a Text)."
|
|
17019
17937
|
),
|
|
17020
|
-
table_path:
|
|
17021
|
-
rows:
|
|
17022
|
-
callback_stub:
|
|
17938
|
+
table_path: z107.string().optional().describe("Table DAT whose rows drive the clones. Omit \u2192 create a small example Table DAT."),
|
|
17939
|
+
rows: z107.number().int().min(0).max(64).default(0).describe("When creating an example table, how many example rows (0 = a 3-row demo)."),
|
|
17940
|
+
callback_stub: z107.boolean().default(true).describe("Generate an onReplicate callback DAT stub (per-clone setup hook).")
|
|
17023
17941
|
});
|
|
17024
17942
|
var REPLICATOR_SCRIPT = `
|
|
17025
17943
|
import json, base64, traceback
|
|
@@ -17161,30 +18079,30 @@ async function createReplicatorImpl(ctx, args) {
|
|
|
17161
18079
|
}
|
|
17162
18080
|
|
|
17163
18081
|
// src/tools/layer2/createStageDashboard.ts
|
|
17164
|
-
import { z as
|
|
17165
|
-
var faderSpecSchema =
|
|
17166
|
-
label:
|
|
17167
|
-
par_path:
|
|
18082
|
+
import { z as z108 } from "zod";
|
|
18083
|
+
var faderSpecSchema = z108.object({
|
|
18084
|
+
label: z108.string().describe("Fader label shown on the page."),
|
|
18085
|
+
par_path: z108.string().describe(
|
|
17168
18086
|
"Absolute parameter path the fader drives, e.g. '/project1/level1/brightness1' or '/project1/comp1/Intensity'. Written live as a float."
|
|
17169
18087
|
)
|
|
17170
18088
|
});
|
|
17171
|
-
var createStageDashboardSchema =
|
|
17172
|
-
target:
|
|
18089
|
+
var createStageDashboardSchema = z108.object({
|
|
18090
|
+
target: z108.string().default("/project1").describe(
|
|
17173
18091
|
"Control COMP the dashboard is built inside. It holds the cues (manage_cue) and the Blackout/Freeze toggles (create_panic); cue buttons fire that COMP's cues and the panic button toggles its safety pars."
|
|
17174
18092
|
),
|
|
17175
|
-
port:
|
|
18093
|
+
port: z108.coerce.number().int().positive().max(65535).default(9990).describe(
|
|
17176
18094
|
"TCP port for the dashboard web server (keep it distinct from the bridge's 9980 and phone_remote's 9981)."
|
|
17177
18095
|
),
|
|
17178
|
-
cues:
|
|
18096
|
+
cues: z108.array(z108.string()).default([]).describe(
|
|
17179
18097
|
"Cue names (stored with manage_cue) to expose as launch buttons, in order. Each becomes a button that instantly recalls its cue on the target COMP. Empty omits the cue grid."
|
|
17180
18098
|
),
|
|
17181
|
-
faders:
|
|
18099
|
+
faders: z108.array(faderSpecSchema).default([]).describe(
|
|
17182
18100
|
"Master faders, each a { label, par_path } that becomes a slider writing the parameter live. Empty omits the fader bank."
|
|
17183
18101
|
),
|
|
17184
|
-
audio_features:
|
|
18102
|
+
audio_features: z108.string().optional().describe(
|
|
17185
18103
|
"Optional audio-features Null CHOP path for the readout strip's VU bar (first channel). When omitted the readout still renders (beat from the timeline, VU flat)."
|
|
17186
18104
|
),
|
|
17187
|
-
name:
|
|
18105
|
+
name: z108.string().default("stage_dashboard").describe("Name of the Web Server DAT (and its callbacks DAT) built inside the target COMP.")
|
|
17188
18106
|
});
|
|
17189
18107
|
var DASHBOARD_CALLBACKS = `import json
|
|
17190
18108
|
from urllib.parse import urlparse, parse_qs
|
|
@@ -17427,12 +18345,12 @@ async function createStageDashboardImpl(ctx, args) {
|
|
|
17427
18345
|
}
|
|
17428
18346
|
|
|
17429
18347
|
// src/tools/layer2/duplicateNetwork.ts
|
|
17430
|
-
import { z as
|
|
18348
|
+
import { z as z109 } from "zod";
|
|
17431
18349
|
var q58 = (value) => JSON.stringify(value);
|
|
17432
|
-
var duplicateNetworkSchema =
|
|
17433
|
-
source_path:
|
|
17434
|
-
name:
|
|
17435
|
-
parent_path:
|
|
18350
|
+
var duplicateNetworkSchema = z109.object({
|
|
18351
|
+
source_path: z109.string().describe("Path of the node/COMP to duplicate."),
|
|
18352
|
+
name: z109.string().optional().describe("Name for the copy (auto-generated if omitted)."),
|
|
18353
|
+
parent_path: z109.string().optional().describe("Where to place the copy (defaults to the source's parent).")
|
|
17436
18354
|
});
|
|
17437
18355
|
async function duplicateNetworkImpl(ctx, args) {
|
|
17438
18356
|
const script = [
|
|
@@ -17453,23 +18371,23 @@ async function duplicateNetworkImpl(ctx, args) {
|
|
|
17453
18371
|
}
|
|
17454
18372
|
|
|
17455
18373
|
// src/tools/layer2/learnControl.ts
|
|
17456
|
-
import { z as
|
|
17457
|
-
var learnControlSchema =
|
|
17458
|
-
mode:
|
|
18374
|
+
import { z as z110 } from "zod";
|
|
18375
|
+
var learnControlSchema = z110.object({
|
|
18376
|
+
mode: z110.enum(["snapshot", "bind"]).describe(
|
|
17459
18377
|
"snapshot: record the current value of every channel of source_chop. bind: re-read source_chop, find the channel that moved the most since the snapshot, and bind target to it. Call snapshot first (controls at rest), wiggle one hardware control, then call bind."
|
|
17460
18378
|
),
|
|
17461
|
-
source_chop:
|
|
18379
|
+
source_chop: z110.string().describe(
|
|
17462
18380
|
"Absolute path of the input CHOP carrying the hardware controls (e.g. a midiin/oscin CHOP or a Null fed by one)."
|
|
17463
18381
|
),
|
|
17464
|
-
target:
|
|
18382
|
+
target: z110.string().optional().describe(
|
|
17465
18383
|
"Parameter to drive, written as 'nodePath.parName' (e.g. '/project1/sys/transform1.scale'). Required for mode:'bind'; switched to expression mode so it tracks the matched channel live."
|
|
17466
18384
|
),
|
|
17467
|
-
scale:
|
|
17468
|
-
offset:
|
|
17469
|
-
min_delta:
|
|
18385
|
+
scale: z110.coerce.number().default(1).describe("Multiply the matched channel value (mapping gain)."),
|
|
18386
|
+
offset: z110.coerce.number().default(0).describe("Add to the scaled value (mapping offset)."),
|
|
18387
|
+
min_delta: z110.coerce.number().optional().describe(
|
|
17470
18388
|
"mode:'bind' minimum NORMALIZED movement (default 0.05). The winning channel's delta is normalized by max(|old|, |new|, epsilon) \u2014 a unit-free relative change \u2014 so a 0\u2013127 MIDI CC and a 0\u20131 OSC float compare fairly. If the top channel moved less than this, nothing is bound and you're told to wiggle the control harder. Raise it to reject controller jitter; lower it for very small/slow knobs."
|
|
17471
18389
|
),
|
|
17472
|
-
parent_path:
|
|
18390
|
+
parent_path: z110.string().default("/project1").describe(
|
|
17473
18391
|
"COMP whose storage persists the snapshot between the snapshot and bind calls (defaults to /project1)."
|
|
17474
18392
|
)
|
|
17475
18393
|
});
|
|
@@ -17620,21 +18538,21 @@ async function learnControlImpl(ctx, args) {
|
|
|
17620
18538
|
}
|
|
17621
18539
|
|
|
17622
18540
|
// src/tools/layer2/manageAnnotation.ts
|
|
17623
|
-
import { z as
|
|
17624
|
-
var manageAnnotationSchema =
|
|
17625
|
-
action:
|
|
18541
|
+
import { z as z111 } from "zod";
|
|
18542
|
+
var manageAnnotationSchema = z111.object({
|
|
18543
|
+
action: z111.enum(["create", "comment", "list", "enclosed"]).describe(
|
|
17626
18544
|
"'create' a titled annotation box, 'comment' to set an op's comment, 'list' the annotations in a network, or 'enclosed' to list the ops a box geometrically encloses."
|
|
17627
18545
|
),
|
|
17628
|
-
parent_path:
|
|
17629
|
-
text:
|
|
17630
|
-
name:
|
|
17631
|
-
node_path:
|
|
18546
|
+
parent_path: z111.string().default("/project1").describe("(create/list) The network (COMP) to act in."),
|
|
18547
|
+
text: z111.string().optional().describe("(create) The title/text shown on the box; (comment) the comment string to set."),
|
|
18548
|
+
name: z111.string().optional().describe("(create) Name for the annotation COMP (defaults to 'anno')."),
|
|
18549
|
+
node_path: z111.string().optional().describe(
|
|
17632
18550
|
"(comment) The op to comment on; (enclosed) the annotation box whose enclosed ops to list."
|
|
17633
18551
|
),
|
|
17634
|
-
x:
|
|
17635
|
-
y:
|
|
17636
|
-
w:
|
|
17637
|
-
h:
|
|
18552
|
+
x: z111.coerce.number().optional().describe("(create) Node-space X position for the box's left edge."),
|
|
18553
|
+
y: z111.coerce.number().optional().describe("(create) Node-space Y position for the box's top edge."),
|
|
18554
|
+
w: z111.coerce.number().optional().describe("(create) Node-space width of the box."),
|
|
18555
|
+
h: z111.coerce.number().optional().describe("(create) Node-space height of the box.")
|
|
17638
18556
|
});
|
|
17639
18557
|
var ANNOTATION_SCRIPT = `
|
|
17640
18558
|
import json, base64, traceback
|
|
@@ -17879,15 +18797,15 @@ async function manageAnnotationImpl(ctx, args) {
|
|
|
17879
18797
|
}
|
|
17880
18798
|
|
|
17881
18799
|
// src/tools/layer2/manageCheckpoint.ts
|
|
17882
|
-
import { z as
|
|
17883
|
-
var manageCheckpointSchema =
|
|
17884
|
-
action:
|
|
18800
|
+
import { z as z112 } from "zod";
|
|
18801
|
+
var manageCheckpointSchema = z112.object({
|
|
18802
|
+
action: z112.enum(["store", "restore", "list", "delete"]).describe(
|
|
17885
18803
|
"store a full snapshot of a sub-network, restore one, list all, or delete one. A checkpoint is an 'undo point' before risky live edits."
|
|
17886
18804
|
),
|
|
17887
|
-
comp_path:
|
|
17888
|
-
name:
|
|
17889
|
-
prune_created:
|
|
17890
|
-
recreate_deleted:
|
|
18805
|
+
comp_path: z112.string().default("/project1").describe("Root COMP whose whole sub-network the checkpoint captures."),
|
|
18806
|
+
name: z112.string().optional().describe("Checkpoint name (required for store/restore/delete)."),
|
|
18807
|
+
prune_created: z112.boolean().default(true).describe("(restore) Destroy nodes that were created after the checkpoint was stored."),
|
|
18808
|
+
recreate_deleted: z112.boolean().default(true).describe(
|
|
17891
18809
|
"(restore) Recreate nodes that were deleted after the checkpoint (type + params + wiring, best-effort)."
|
|
17892
18810
|
)
|
|
17893
18811
|
});
|
|
@@ -18090,17 +19008,17 @@ async function manageCheckpointImpl(ctx, args) {
|
|
|
18090
19008
|
}
|
|
18091
19009
|
|
|
18092
19010
|
// src/tools/layer2/manageComponent.ts
|
|
18093
|
-
import { z as
|
|
18094
|
-
var manageComponentSchema =
|
|
18095
|
-
action:
|
|
18096
|
-
file_path:
|
|
18097
|
-
comp_path:
|
|
18098
|
-
parent_path:
|
|
18099
|
-
linked:
|
|
19011
|
+
import { z as z113 } from "zod";
|
|
19012
|
+
var manageComponentSchema = z113.object({
|
|
19013
|
+
action: z113.enum(["save", "load"]).describe("save a COMP to a .tox file, or load a .tox into the project."),
|
|
19014
|
+
file_path: z113.string().describe("Absolute path to the .tox file (e.g. '/Users/me/components/widget.tox')."),
|
|
19015
|
+
comp_path: z113.string().optional().describe("(save) The COMP to save as a reusable .tox component."),
|
|
19016
|
+
parent_path: z113.string().default("/project1").describe("(load) COMP to place the loaded component inside."),
|
|
19017
|
+
linked: z113.boolean().default(false).describe(
|
|
18100
19018
|
"(load) Create a live-linked instance (externaltox) that re-reads the file on change, instead of an independent copy."
|
|
18101
19019
|
),
|
|
18102
|
-
name:
|
|
18103
|
-
create_folders:
|
|
19020
|
+
name: z113.string().optional().describe("(load, linked) Name for the linked COMP; defaults to the file name."),
|
|
19021
|
+
create_folders: z113.boolean().default(false).describe("(save) Create the parent folders if they do not exist.")
|
|
18104
19022
|
});
|
|
18105
19023
|
var COMPONENT_SCRIPT = `
|
|
18106
19024
|
import json, base64, traceback, os
|
|
@@ -18191,14 +19109,14 @@ async function manageComponentImpl(ctx, args) {
|
|
|
18191
19109
|
}
|
|
18192
19110
|
|
|
18193
19111
|
// src/tools/layer2/managePresets.ts
|
|
18194
|
-
import { z as
|
|
18195
|
-
var managePresetsSchema =
|
|
18196
|
-
action:
|
|
18197
|
-
comp_path:
|
|
19112
|
+
import { z as z114 } from "zod";
|
|
19113
|
+
var managePresetsSchema = z114.object({
|
|
19114
|
+
action: z114.enum(["store", "recall", "list", "delete"]).describe("store a snapshot, recall one, list all, or delete one."),
|
|
19115
|
+
comp_path: z114.string().default("/project1").describe(
|
|
18198
19116
|
"COMP whose parameter values the preset captures \u2014 usually a control-panel container."
|
|
18199
19117
|
),
|
|
18200
|
-
name:
|
|
18201
|
-
params:
|
|
19118
|
+
name: z114.string().optional().describe("Preset name (required for store/recall/delete)."),
|
|
19119
|
+
params: z114.array(z114.string()).optional().describe(
|
|
18202
19120
|
"Specific custom-parameter names to capture/restore. Defaults to every custom parameter on the COMP."
|
|
18203
19121
|
)
|
|
18204
19122
|
});
|
|
@@ -18299,14 +19217,14 @@ async function managePresetsImpl(ctx, args) {
|
|
|
18299
19217
|
}
|
|
18300
19218
|
|
|
18301
19219
|
// src/tools/layer2/randomizeControls.ts
|
|
18302
|
-
import { z as
|
|
18303
|
-
var randomizeControlsSchema =
|
|
18304
|
-
comp_path:
|
|
18305
|
-
params:
|
|
18306
|
-
amount:
|
|
19220
|
+
import { z as z115 } from "zod";
|
|
19221
|
+
var randomizeControlsSchema = z115.object({
|
|
19222
|
+
comp_path: z115.string().default("/project1").describe("COMP whose custom parameters to randomize (usually a control-panel container)."),
|
|
19223
|
+
params: z115.array(z115.string()).optional().describe("Specific custom-parameter names to randomize. Defaults to every numeric one."),
|
|
19224
|
+
amount: z115.coerce.number().min(0).max(1).default(1).describe(
|
|
18307
19225
|
"How far to move toward a random value in range: 1 = fully random, 0.2 = a gentle nudge from the current value. Lets you improvise without losing the current look."
|
|
18308
19226
|
),
|
|
18309
|
-
seed:
|
|
19227
|
+
seed: z115.coerce.number().int().optional().describe("Optional RNG seed for repeatable results.")
|
|
18310
19228
|
});
|
|
18311
19229
|
var RANDOMIZE_SCRIPT = `
|
|
18312
19230
|
import json, base64, traceback, random
|
|
@@ -18375,37 +19293,37 @@ async function randomizeControlsImpl(ctx, args) {
|
|
|
18375
19293
|
}
|
|
18376
19294
|
|
|
18377
19295
|
// src/tools/layer2/rebuildNetwork.ts
|
|
18378
|
-
import { z as
|
|
18379
|
-
var ParamSpec =
|
|
18380
|
-
value:
|
|
18381
|
-
mode:
|
|
19296
|
+
import { z as z116 } from "zod";
|
|
19297
|
+
var ParamSpec = z116.object({
|
|
19298
|
+
value: z116.unknown().optional().describe("Constant value for the parameter (used when mode is constant or omitted)."),
|
|
19299
|
+
mode: z116.string().optional().describe(
|
|
18382
19300
|
"Parameter mode: 'EXPRESSION', 'BIND', or 'CONSTANT' (case-insensitive). Omit for a plain constant."
|
|
18383
19301
|
),
|
|
18384
|
-
expr:
|
|
19302
|
+
expr: z116.string().optional().describe(
|
|
18385
19303
|
"Expression or bind string, required when mode is EXPRESSION or BIND, e.g. 'me.time.seconds'."
|
|
18386
19304
|
)
|
|
18387
19305
|
});
|
|
18388
|
-
var NodeSpec =
|
|
18389
|
-
name:
|
|
18390
|
-
type:
|
|
18391
|
-
params:
|
|
18392
|
-
inputs:
|
|
18393
|
-
|
|
18394
|
-
from:
|
|
18395
|
-
out_index:
|
|
18396
|
-
in_index:
|
|
19306
|
+
var NodeSpec = z116.object({
|
|
19307
|
+
name: z116.string().describe("Name for the node inside the parent (TD may adjust it to avoid collisions)."),
|
|
19308
|
+
type: z116.string().describe("Operator type to create, e.g. 'noiseTOP', 'levelTOP'."),
|
|
19309
|
+
params: z116.record(z116.string(), ParamSpec).default({}).describe("Map of parameter name \u2192 { value | mode | expr } to apply after creation."),
|
|
19310
|
+
inputs: z116.array(
|
|
19311
|
+
z116.object({
|
|
19312
|
+
from: z116.string().describe("Name of an earlier node in this spec to wire from."),
|
|
19313
|
+
out_index: z116.number().int().default(0).describe("Output connector index on the source node (default 0)."),
|
|
19314
|
+
in_index: z116.number().int().default(0).describe("Input connector index on this node (default 0).")
|
|
18397
19315
|
})
|
|
18398
19316
|
).default([]).describe("Inbound wires for this node, each referencing another node by `from` name."),
|
|
18399
|
-
x:
|
|
18400
|
-
y:
|
|
19317
|
+
x: z116.number().optional().describe("Optional node X position (nodeX) in the network editor."),
|
|
19318
|
+
y: z116.number().optional().describe("Optional node Y position (nodeY) in the network editor.")
|
|
18401
19319
|
});
|
|
18402
|
-
var rebuildNetworkSchema =
|
|
18403
|
-
parent_path:
|
|
18404
|
-
spec:
|
|
18405
|
-
root:
|
|
18406
|
-
nodes:
|
|
19320
|
+
var rebuildNetworkSchema = z116.object({
|
|
19321
|
+
parent_path: z116.string().describe("COMP to rebuild the network inside."),
|
|
19322
|
+
spec: z116.object({
|
|
19323
|
+
root: z116.string().optional().describe("Original root path the spec was serialized from (informational only)."),
|
|
19324
|
+
nodes: z116.array(NodeSpec).describe("Nodes to create, parameterize, and wire, in order.")
|
|
18407
19325
|
}).describe("A serialize_network spec to reconstruct."),
|
|
18408
|
-
clear_existing:
|
|
19326
|
+
clear_existing: z116.boolean().default(false).describe("Delete existing children of parent_path first (destructive).")
|
|
18409
19327
|
});
|
|
18410
19328
|
var REBUILD_SCRIPT = `
|
|
18411
19329
|
import json, base64, traceback
|
|
@@ -18551,26 +19469,26 @@ async function rebuildNetworkImpl(ctx, args) {
|
|
|
18551
19469
|
}
|
|
18552
19470
|
|
|
18553
19471
|
// src/tools/layer2/scaffoldExtension.ts
|
|
18554
|
-
import { z as
|
|
19472
|
+
import { z as z117 } from "zod";
|
|
18555
19473
|
var PY_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
18556
|
-
var scaffoldExtensionSchema =
|
|
18557
|
-
comp_path:
|
|
18558
|
-
class_name:
|
|
19474
|
+
var scaffoldExtensionSchema = z117.object({
|
|
19475
|
+
comp_path: z117.string().describe("The COMP to give a Python extension class."),
|
|
19476
|
+
class_name: z117.string().regex(
|
|
18559
19477
|
PY_IDENTIFIER,
|
|
18560
19478
|
"class_name must be a valid Python identifier (a letter or underscore, then letters/digits/underscores)."
|
|
18561
19479
|
).describe(
|
|
18562
19480
|
"Extension class name, e.g. 'WidgetExt' (capitalized to a valid identifier; must already be identifier-safe)."
|
|
18563
19481
|
),
|
|
18564
|
-
methods:
|
|
18565
|
-
|
|
19482
|
+
methods: z117.array(
|
|
19483
|
+
z117.string().regex(
|
|
18566
19484
|
PY_IDENTIFIER,
|
|
18567
19485
|
"each method name must be a valid Python identifier (a letter or underscore, then letters/digits/underscores)."
|
|
18568
19486
|
)
|
|
18569
19487
|
).optional().describe("Optional method-name stubs to add to the class (each takes only `self`)."),
|
|
18570
|
-
promote:
|
|
19488
|
+
promote: z117.boolean().default(true).describe(
|
|
18571
19489
|
"Promote the extension so its members are callable directly on the COMP (op.Method())."
|
|
18572
19490
|
),
|
|
18573
|
-
slot:
|
|
19491
|
+
slot: z117.coerce.number().int().min(1).max(8).default(1).describe("Extension slot (1\u20138) \u2014 a COMP can hold several extensions.")
|
|
18574
19492
|
});
|
|
18575
19493
|
var PY_KEYWORDS = /* @__PURE__ */ new Set([
|
|
18576
19494
|
"False",
|
|
@@ -18762,13 +19680,13 @@ async function scaffoldExtensionImpl(ctx, args) {
|
|
|
18762
19680
|
}
|
|
18763
19681
|
|
|
18764
19682
|
// src/tools/layer2/setParametersBatch.ts
|
|
18765
|
-
import { z as
|
|
18766
|
-
var UpdateSchema =
|
|
18767
|
-
path:
|
|
18768
|
-
parameters:
|
|
19683
|
+
import { z as z118 } from "zod";
|
|
19684
|
+
var UpdateSchema = z118.object({
|
|
19685
|
+
path: z118.string().describe("Path of the node whose parameters to update."),
|
|
19686
|
+
parameters: z118.record(z118.string(), z118.unknown()).describe("Parameter values to set on that node, as a { parName: value } map.")
|
|
18769
19687
|
});
|
|
18770
|
-
var setParametersBatchSchema =
|
|
18771
|
-
updates:
|
|
19688
|
+
var setParametersBatchSchema = z118.object({
|
|
19689
|
+
updates: z118.array(UpdateSchema).min(1).describe(
|
|
18772
19690
|
"List of { path, parameters } updates sent in one batch request (per-update results; not transactional \u2014 a failed update does not roll back the others)."
|
|
18773
19691
|
)
|
|
18774
19692
|
});
|
|
@@ -18791,9 +19709,9 @@ async function setParametersBatchImpl(ctx, args) {
|
|
|
18791
19709
|
}
|
|
18792
19710
|
|
|
18793
19711
|
// src/tools/layer2/setPerformMode.ts
|
|
18794
|
-
import { z as
|
|
18795
|
-
var setPerformModeSchema =
|
|
18796
|
-
enabled:
|
|
19712
|
+
import { z as z119 } from "zod";
|
|
19713
|
+
var setPerformModeSchema = z119.object({
|
|
19714
|
+
enabled: z119.boolean().describe(
|
|
18797
19715
|
"true to enter perform mode before a live show, false to leave it afterward. While perform mode is ON, tools that honor the advisory flag skip nonessential compute. The built-in guard currently suppresses auto preview captures; future tools can opt in by checking op('/').fetch('tdmcp_perform_mode', False) before doing expensive work. It does NOT stop the TD timeline or kill audio/video processing."
|
|
18798
19716
|
)
|
|
18799
19717
|
});
|
|
@@ -18846,42 +19764,42 @@ async function setPerformModeImpl(ctx, args) {
|
|
|
18846
19764
|
}
|
|
18847
19765
|
|
|
18848
19766
|
// src/tools/layer3/analyzeProject.ts
|
|
18849
|
-
import { z as
|
|
18850
|
-
var analyzeProjectSchema =
|
|
18851
|
-
path:
|
|
18852
|
-
recursive:
|
|
19767
|
+
import { z as z120 } from "zod";
|
|
19768
|
+
var analyzeProjectSchema = z120.object({
|
|
19769
|
+
path: z120.string().default("/project1").describe("Network root to analyze (the COMP whose descendants are scanned)."),
|
|
19770
|
+
recursive: z120.boolean().default(true).describe(
|
|
18853
19771
|
"Recurse into child COMPs (true) or only inspect the root's direct children (false)."
|
|
18854
19772
|
)
|
|
18855
19773
|
});
|
|
18856
|
-
var analyzeProjectOutputSchema =
|
|
18857
|
-
path:
|
|
18858
|
-
recursive:
|
|
18859
|
-
counts:
|
|
18860
|
-
nodes:
|
|
18861
|
-
by_family:
|
|
19774
|
+
var analyzeProjectOutputSchema = z120.object({
|
|
19775
|
+
path: z120.string(),
|
|
19776
|
+
recursive: z120.boolean(),
|
|
19777
|
+
counts: z120.object({
|
|
19778
|
+
nodes: z120.number(),
|
|
19779
|
+
by_family: z120.record(z120.string(), z120.number())
|
|
18862
19780
|
}),
|
|
18863
|
-
unused:
|
|
18864
|
-
|
|
18865
|
-
path:
|
|
18866
|
-
type:
|
|
18867
|
-
reason:
|
|
19781
|
+
unused: z120.array(
|
|
19782
|
+
z120.object({
|
|
19783
|
+
path: z120.string(),
|
|
19784
|
+
type: z120.string(),
|
|
19785
|
+
reason: z120.string()
|
|
18868
19786
|
})
|
|
18869
19787
|
),
|
|
18870
|
-
broken_file_deps:
|
|
18871
|
-
|
|
18872
|
-
path:
|
|
18873
|
-
par:
|
|
18874
|
-
file:
|
|
19788
|
+
broken_file_deps: z120.array(
|
|
19789
|
+
z120.object({
|
|
19790
|
+
path: z120.string(),
|
|
19791
|
+
par: z120.string(),
|
|
19792
|
+
file: z120.string()
|
|
18875
19793
|
})
|
|
18876
19794
|
),
|
|
18877
|
-
orphan_comps:
|
|
18878
|
-
|
|
18879
|
-
path:
|
|
18880
|
-
reason:
|
|
19795
|
+
orphan_comps: z120.array(
|
|
19796
|
+
z120.object({
|
|
19797
|
+
path: z120.string(),
|
|
19798
|
+
reason: z120.string()
|
|
18881
19799
|
})
|
|
18882
19800
|
),
|
|
18883
|
-
dependency_map:
|
|
18884
|
-
warnings:
|
|
19801
|
+
dependency_map: z120.record(z120.string(), z120.array(z120.string())),
|
|
19802
|
+
warnings: z120.array(z120.string())
|
|
18885
19803
|
});
|
|
18886
19804
|
var ANALYZE_SCRIPT = `
|
|
18887
19805
|
import json, base64, traceback, os, re
|
|
@@ -19130,28 +20048,28 @@ async function analyzeProjectImpl(ctx, args) {
|
|
|
19130
20048
|
}
|
|
19131
20049
|
|
|
19132
20050
|
// src/tools/layer3/compareTdNodes.ts
|
|
19133
|
-
import { z as
|
|
19134
|
-
var compareTdNodesSchema =
|
|
19135
|
-
path_a:
|
|
19136
|
-
path_b:
|
|
19137
|
-
only_diff:
|
|
20051
|
+
import { z as z121 } from "zod";
|
|
20052
|
+
var compareTdNodesSchema = z121.object({
|
|
20053
|
+
path_a: z121.string().describe("First node path."),
|
|
20054
|
+
path_b: z121.string().describe("Second node path."),
|
|
20055
|
+
only_diff: z121.boolean().default(true).describe("Return only the parameters that differ (true) or also list the identical ones.")
|
|
19138
20056
|
});
|
|
19139
|
-
var compareTdNodesOutputSchema =
|
|
19140
|
-
a:
|
|
19141
|
-
b:
|
|
19142
|
-
type_a:
|
|
19143
|
-
type_b:
|
|
19144
|
-
type_match:
|
|
19145
|
-
differing_count:
|
|
19146
|
-
same_count:
|
|
19147
|
-
differing:
|
|
19148
|
-
|
|
19149
|
-
param:
|
|
19150
|
-
a:
|
|
19151
|
-
b:
|
|
20057
|
+
var compareTdNodesOutputSchema = z121.object({
|
|
20058
|
+
a: z121.string().describe("Path of the first node compared."),
|
|
20059
|
+
b: z121.string().describe("Path of the second node compared."),
|
|
20060
|
+
type_a: z121.string().describe("Operator type of the first node."),
|
|
20061
|
+
type_b: z121.string().describe("Operator type of the second node."),
|
|
20062
|
+
type_match: z121.boolean().describe("True if both nodes are the same operator type."),
|
|
20063
|
+
differing_count: z121.number().describe("Number of parameters whose values differ."),
|
|
20064
|
+
same_count: z121.number().describe("Number of parameters that are identical on both nodes."),
|
|
20065
|
+
differing: z121.array(
|
|
20066
|
+
z121.object({
|
|
20067
|
+
param: z121.string().describe("Name of the differing parameter."),
|
|
20068
|
+
a: z121.unknown().describe("Its value on the first node."),
|
|
20069
|
+
b: z121.unknown().describe("Its value on the second node.")
|
|
19152
20070
|
})
|
|
19153
20071
|
).describe("Every parameter that differs, with each node's value."),
|
|
19154
|
-
identical:
|
|
20072
|
+
identical: z121.array(z121.string()).optional().describe("Names of identical parameters; present only when only_diff is false.")
|
|
19155
20073
|
});
|
|
19156
20074
|
async function compareTdNodesImpl(ctx, args) {
|
|
19157
20075
|
return guardTd(
|
|
@@ -19192,12 +20110,12 @@ async function compareTdNodesImpl(ctx, args) {
|
|
|
19192
20110
|
}
|
|
19193
20111
|
|
|
19194
20112
|
// src/tools/layer3/createTdNode.ts
|
|
19195
|
-
import { z as
|
|
19196
|
-
var createTdNodeSchema =
|
|
19197
|
-
parent_path:
|
|
19198
|
-
type:
|
|
19199
|
-
name:
|
|
19200
|
-
parameters:
|
|
20113
|
+
import { z as z122 } from "zod";
|
|
20114
|
+
var createTdNodeSchema = z122.object({
|
|
20115
|
+
parent_path: z122.string().default("/project1").describe("Parent COMP path to create the node inside."),
|
|
20116
|
+
type: z122.string().describe("Operator type string, e.g. 'noiseTOP', 'feedbackTOP', 'nullTOP', 'constantCHOP'."),
|
|
20117
|
+
name: z122.string().optional().describe("Optional node name (auto-generated if omitted)."),
|
|
20118
|
+
parameters: z122.record(z122.string(), z122.unknown()).optional().describe("Optional initial parameter overrides as key\u2192value pairs.")
|
|
19201
20119
|
});
|
|
19202
20120
|
async function createTdNodeImpl(ctx, args) {
|
|
19203
20121
|
const warnings = [];
|
|
@@ -19230,9 +20148,9 @@ async function createTdNodeImpl(ctx, args) {
|
|
|
19230
20148
|
}
|
|
19231
20149
|
|
|
19232
20150
|
// src/tools/layer3/deleteTdNode.ts
|
|
19233
|
-
import { z as
|
|
19234
|
-
var deleteTdNodeSchema =
|
|
19235
|
-
path:
|
|
20151
|
+
import { z as z123 } from "zod";
|
|
20152
|
+
var deleteTdNodeSchema = z123.object({
|
|
20153
|
+
path: z123.string().describe("Full path of the node to delete, e.g. '/project1/noise1'.")
|
|
19236
20154
|
});
|
|
19237
20155
|
async function deleteTdNodeImpl(ctx, args) {
|
|
19238
20156
|
return guardTd(
|
|
@@ -19242,23 +20160,23 @@ async function deleteTdNodeImpl(ctx, args) {
|
|
|
19242
20160
|
}
|
|
19243
20161
|
|
|
19244
20162
|
// src/tools/layer3/diffSnapshots.ts
|
|
19245
|
-
import { z as
|
|
19246
|
-
var snapshotNodeSchema =
|
|
19247
|
-
path:
|
|
19248
|
-
type:
|
|
19249
|
-
parameters:
|
|
20163
|
+
import { z as z124 } from "zod";
|
|
20164
|
+
var snapshotNodeSchema = z124.object({
|
|
20165
|
+
path: z124.string(),
|
|
20166
|
+
type: z124.string().optional(),
|
|
20167
|
+
parameters: z124.record(z124.string(), z124.unknown()).optional()
|
|
19250
20168
|
});
|
|
19251
|
-
var snapshotConnSchema =
|
|
19252
|
-
source_path:
|
|
19253
|
-
source_output:
|
|
19254
|
-
target_path:
|
|
19255
|
-
target_input:
|
|
20169
|
+
var snapshotConnSchema = z124.object({
|
|
20170
|
+
source_path: z124.string(),
|
|
20171
|
+
source_output: z124.number().optional(),
|
|
20172
|
+
target_path: z124.string(),
|
|
20173
|
+
target_input: z124.number().optional()
|
|
19256
20174
|
});
|
|
19257
|
-
var snapshotSchema =
|
|
19258
|
-
nodes:
|
|
19259
|
-
connections:
|
|
20175
|
+
var snapshotSchema = z124.object({
|
|
20176
|
+
nodes: z124.array(snapshotNodeSchema).default([]),
|
|
20177
|
+
connections: z124.array(snapshotConnSchema).default([])
|
|
19260
20178
|
});
|
|
19261
|
-
var diffSnapshotsSchema =
|
|
20179
|
+
var diffSnapshotsSchema = z124.object({
|
|
19262
20180
|
before: snapshotSchema.describe(
|
|
19263
20181
|
"Earlier snapshot (from snapshot_td_graph, include_params for param diffs)."
|
|
19264
20182
|
),
|
|
@@ -19302,13 +20220,13 @@ function diffSnapshotsImpl(_ctx, args) {
|
|
|
19302
20220
|
}
|
|
19303
20221
|
|
|
19304
20222
|
// src/tools/layer3/disconnectNodes.ts
|
|
19305
|
-
import { z as
|
|
19306
|
-
var disconnectNodesSchema =
|
|
19307
|
-
to_path:
|
|
19308
|
-
from_path:
|
|
20223
|
+
import { z as z125 } from "zod";
|
|
20224
|
+
var disconnectNodesSchema = z125.object({
|
|
20225
|
+
to_path: z125.string().describe("The downstream node to remove input wire(s) from."),
|
|
20226
|
+
from_path: z125.string().optional().describe(
|
|
19309
20227
|
"Only remove wires coming from this upstream node. Omit to remove ALL input wires into to_path (scoped by to_input if given)."
|
|
19310
20228
|
),
|
|
19311
|
-
to_input:
|
|
20229
|
+
to_input: z125.number().int().min(0).optional().describe("Only clear this input index on to_path (0-based). Omit to clear all inputs.")
|
|
19312
20230
|
});
|
|
19313
20231
|
var DISCONNECT_SCRIPT = `
|
|
19314
20232
|
import json, base64, traceback
|
|
@@ -19401,6 +20319,12 @@ function buildDisconnectScript(payload) {
|
|
|
19401
20319
|
async function disconnectNodesImpl(ctx, args) {
|
|
19402
20320
|
return guardTd(
|
|
19403
20321
|
async () => {
|
|
20322
|
+
try {
|
|
20323
|
+
const r2 = await ctx.client.disconnectNodes(args.to_path, args.from_path, args.to_input);
|
|
20324
|
+
return { ...r2, probe: null };
|
|
20325
|
+
} catch (err) {
|
|
20326
|
+
if (!isMissingEndpoint(err)) throw err;
|
|
20327
|
+
}
|
|
19404
20328
|
const script = buildDisconnectScript({
|
|
19405
20329
|
to_path: args.to_path,
|
|
19406
20330
|
from_path: args.from_path ?? null,
|
|
@@ -19423,11 +20347,11 @@ async function disconnectNodesImpl(ctx, args) {
|
|
|
19423
20347
|
}
|
|
19424
20348
|
|
|
19425
20349
|
// src/tools/layer3/documentNetwork.ts
|
|
19426
|
-
import { z as
|
|
20350
|
+
import { z as z126 } from "zod";
|
|
19427
20351
|
var MAX_NODES = 150;
|
|
19428
|
-
var documentNetworkSchema =
|
|
19429
|
-
path:
|
|
19430
|
-
recursive:
|
|
20352
|
+
var documentNetworkSchema = z126.object({
|
|
20353
|
+
path: z126.string().default("/project1").describe("Network root to document."),
|
|
20354
|
+
recursive: z126.boolean().default(false).describe("Include all descendants (otherwise just the direct children).")
|
|
19431
20355
|
});
|
|
19432
20356
|
function family(type) {
|
|
19433
20357
|
const m = /(TOP|CHOP|SOP|COMP|DAT|MAT|POP)$/.exec(type);
|
|
@@ -19480,12 +20404,71 @@ async function documentNetworkImpl(ctx, args) {
|
|
|
19480
20404
|
}
|
|
19481
20405
|
|
|
19482
20406
|
// src/tools/layer3/editDatContent.ts
|
|
19483
|
-
import { z as
|
|
19484
|
-
|
|
19485
|
-
|
|
19486
|
-
|
|
19487
|
-
|
|
19488
|
-
|
|
20407
|
+
import { z as z127 } from "zod";
|
|
20408
|
+
|
|
20409
|
+
// src/tools/layer3/datTextReplace.ts
|
|
20410
|
+
function computeDatTextReplace(text, oldString, newString, replaceAll) {
|
|
20411
|
+
if (oldString.length === 0) {
|
|
20412
|
+
return {
|
|
20413
|
+
occurrences: 0,
|
|
20414
|
+
replacements: 0,
|
|
20415
|
+
replaceAll,
|
|
20416
|
+
error: "old_string must not be empty."
|
|
20417
|
+
};
|
|
20418
|
+
}
|
|
20419
|
+
const occurrences = countOccurrences(text, oldString);
|
|
20420
|
+
if (occurrences === 0) {
|
|
20421
|
+
return {
|
|
20422
|
+
occurrences,
|
|
20423
|
+
replacements: 0,
|
|
20424
|
+
replaceAll,
|
|
20425
|
+
error: "old_string not found."
|
|
20426
|
+
};
|
|
20427
|
+
}
|
|
20428
|
+
if (occurrences > 1 && !replaceAll) {
|
|
20429
|
+
return {
|
|
20430
|
+
occurrences,
|
|
20431
|
+
replacements: 0,
|
|
20432
|
+
replaceAll,
|
|
20433
|
+
error: `old_string matches ${occurrences} times; pass replace_all:true to replace all, or add surrounding context for a unique match.`
|
|
20434
|
+
};
|
|
20435
|
+
}
|
|
20436
|
+
if (replaceAll) {
|
|
20437
|
+
return {
|
|
20438
|
+
text: text.split(oldString).join(newString),
|
|
20439
|
+
occurrences,
|
|
20440
|
+
replacements: occurrences,
|
|
20441
|
+
replaceAll
|
|
20442
|
+
};
|
|
20443
|
+
}
|
|
20444
|
+
return {
|
|
20445
|
+
text: replaceFirst(text, oldString, newString),
|
|
20446
|
+
occurrences,
|
|
20447
|
+
replacements: 1,
|
|
20448
|
+
replaceAll
|
|
20449
|
+
};
|
|
20450
|
+
}
|
|
20451
|
+
function countOccurrences(haystack, needle) {
|
|
20452
|
+
let count = 0;
|
|
20453
|
+
let index = haystack.indexOf(needle);
|
|
20454
|
+
while (index !== -1) {
|
|
20455
|
+
count += 1;
|
|
20456
|
+
index = haystack.indexOf(needle, index + needle.length);
|
|
20457
|
+
}
|
|
20458
|
+
return count;
|
|
20459
|
+
}
|
|
20460
|
+
function replaceFirst(haystack, needle, replacement) {
|
|
20461
|
+
const index = haystack.indexOf(needle);
|
|
20462
|
+
if (index === -1) return haystack;
|
|
20463
|
+
return haystack.slice(0, index) + replacement + haystack.slice(index + needle.length);
|
|
20464
|
+
}
|
|
20465
|
+
|
|
20466
|
+
// src/tools/layer3/editDatContent.ts
|
|
20467
|
+
var editDatContentSchema = z127.object({
|
|
20468
|
+
dat_path: z127.string().describe("Absolute path to the Text or Table DAT to edit (e.g. '/project1/mytext1')."),
|
|
20469
|
+
old_string: z127.string().min(1).describe("Exact substring to find. Must match at least once. Empty strings are rejected."),
|
|
20470
|
+
new_string: z127.string().describe("Replacement text. May be empty to delete the matched substring."),
|
|
20471
|
+
replace_all: z127.boolean().default(false).describe(
|
|
19489
20472
|
"When false (default), requires exactly one match \u2014 0 or >1 occurrences is an error. Set true to replace every occurrence."
|
|
19490
20473
|
)
|
|
19491
20474
|
});
|
|
@@ -19527,6 +20510,35 @@ function buildEditDatContentScript(payload) {
|
|
|
19527
20510
|
async function editDatContentImpl(ctx, args) {
|
|
19528
20511
|
return guardTd(
|
|
19529
20512
|
async () => {
|
|
20513
|
+
try {
|
|
20514
|
+
const cur = await ctx.client.getDatText(args.dat_path);
|
|
20515
|
+
const res = computeDatTextReplace(
|
|
20516
|
+
cur.text,
|
|
20517
|
+
args.old_string,
|
|
20518
|
+
args.new_string,
|
|
20519
|
+
args.replace_all
|
|
20520
|
+
);
|
|
20521
|
+
if (res.error || res.text === void 0) {
|
|
20522
|
+
return {
|
|
20523
|
+
dat: args.dat_path,
|
|
20524
|
+
occurrences: res.occurrences,
|
|
20525
|
+
replacements: res.replacements,
|
|
20526
|
+
replace_all: args.replace_all,
|
|
20527
|
+
warnings: [],
|
|
20528
|
+
fatal: res.error ?? "edit_dat_content: nothing to replace."
|
|
20529
|
+
};
|
|
20530
|
+
}
|
|
20531
|
+
await ctx.client.putDatText(args.dat_path, res.text);
|
|
20532
|
+
return {
|
|
20533
|
+
dat: args.dat_path,
|
|
20534
|
+
occurrences: res.occurrences,
|
|
20535
|
+
replacements: res.replacements,
|
|
20536
|
+
replace_all: args.replace_all,
|
|
20537
|
+
warnings: []
|
|
20538
|
+
};
|
|
20539
|
+
} catch (err) {
|
|
20540
|
+
if (!isMissingEndpoint(err)) throw err;
|
|
20541
|
+
}
|
|
19530
20542
|
const script = buildEditDatContentScript({
|
|
19531
20543
|
dat: args.dat_path,
|
|
19532
20544
|
old: args.old_string,
|
|
@@ -19547,12 +20559,12 @@ async function editDatContentImpl(ctx, args) {
|
|
|
19547
20559
|
}
|
|
19548
20560
|
|
|
19549
20561
|
// src/tools/layer3/execNodeMethod.ts
|
|
19550
|
-
import { z as
|
|
19551
|
-
var execNodeMethodSchema =
|
|
19552
|
-
path:
|
|
19553
|
-
method:
|
|
19554
|
-
args:
|
|
19555
|
-
kwargs:
|
|
20562
|
+
import { z as z128 } from "zod";
|
|
20563
|
+
var execNodeMethodSchema = z128.object({
|
|
20564
|
+
path: z128.string().describe("Full path of the node to call the method on."),
|
|
20565
|
+
method: z128.string().describe("Method name to call, e.g. 'cook', 'par', 'destroy', 'copy'."),
|
|
20566
|
+
args: z128.array(z128.unknown()).default([]).describe("Positional arguments."),
|
|
20567
|
+
kwargs: z128.record(z128.string(), z128.unknown()).default({}).describe("Keyword arguments.")
|
|
19556
20568
|
});
|
|
19557
20569
|
async function execNodeMethodImpl(ctx, args) {
|
|
19558
20570
|
return guardTd(
|
|
@@ -19562,12 +20574,12 @@ async function execNodeMethodImpl(ctx, args) {
|
|
|
19562
20574
|
}
|
|
19563
20575
|
|
|
19564
20576
|
// src/tools/layer3/executePythonScript.ts
|
|
19565
|
-
import { z as
|
|
19566
|
-
var executePythonScriptSchema =
|
|
19567
|
-
script:
|
|
20577
|
+
import { z as z129 } from "zod";
|
|
20578
|
+
var executePythonScriptSchema = z129.object({
|
|
20579
|
+
script: z129.string().min(1).describe(
|
|
19568
20580
|
"Python source to execute inside TouchDesigner (runs in the TD process, not locally)."
|
|
19569
20581
|
),
|
|
19570
|
-
return_output:
|
|
20582
|
+
return_output: z129.boolean().default(true).describe("Capture stdout / the value of the last expression and return it.")
|
|
19571
20583
|
});
|
|
19572
20584
|
async function executePythonScriptImpl(ctx, args) {
|
|
19573
20585
|
return guardTd(
|
|
@@ -19577,7 +20589,7 @@ async function executePythonScriptImpl(ctx, args) {
|
|
|
19577
20589
|
}
|
|
19578
20590
|
|
|
19579
20591
|
// src/tools/layer3/findTdNodes.ts
|
|
19580
|
-
import { z as
|
|
20592
|
+
import { z as z130 } from "zod";
|
|
19581
20593
|
|
|
19582
20594
|
// src/tools/layer3/nodeMatch.ts
|
|
19583
20595
|
function globToRegExp(pattern) {
|
|
@@ -19590,21 +20602,21 @@ function parentOf2(path2) {
|
|
|
19590
20602
|
}
|
|
19591
20603
|
|
|
19592
20604
|
// src/tools/layer3/findTdNodes.ts
|
|
19593
|
-
var findTdNodesSchema =
|
|
19594
|
-
parent_path:
|
|
19595
|
-
pattern:
|
|
19596
|
-
type:
|
|
19597
|
-
recursive:
|
|
19598
|
-
path_only:
|
|
19599
|
-
limit:
|
|
20605
|
+
var findTdNodesSchema = z130.object({
|
|
20606
|
+
parent_path: z130.string().default("/project1").describe("Where to search from."),
|
|
20607
|
+
pattern: z130.string().optional().describe("Case-insensitive name/path filter with '*' wildcards (e.g. 'text*', '*noise*')."),
|
|
20608
|
+
type: z130.string().optional().describe("Case-insensitive operator-type substring (e.g. 'TOP', 'noise')."),
|
|
20609
|
+
recursive: z130.boolean().default(true).describe("Search the whole sub-network (true) or only direct children (false)."),
|
|
20610
|
+
path_only: z130.boolean().default(false).describe("Return only matching paths."),
|
|
20611
|
+
limit: z130.number().int().positive().default(50).describe("Max matches to return.")
|
|
19600
20612
|
});
|
|
19601
|
-
var findTdNodesOutputSchema =
|
|
19602
|
-
parent_path:
|
|
19603
|
-
recursive:
|
|
19604
|
-
count:
|
|
19605
|
-
truncated:
|
|
19606
|
-
paths:
|
|
19607
|
-
matches:
|
|
20613
|
+
var findTdNodesOutputSchema = z130.object({
|
|
20614
|
+
parent_path: z130.string().describe("The network root the search ran under."),
|
|
20615
|
+
recursive: z130.boolean().describe("Whether descendants were searched, echoing the request."),
|
|
20616
|
+
count: z130.number().describe("Total nodes matched before `limit` truncation."),
|
|
20617
|
+
truncated: z130.boolean().describe("True if more nodes matched than `limit` returned."),
|
|
20618
|
+
paths: z130.array(z130.string()).optional().describe("path_only mode: the matched node paths and nothing else."),
|
|
20619
|
+
matches: z130.array(NodeRefSchema).optional().describe("Default mode: each matched node as {path, name, type}.")
|
|
19608
20620
|
});
|
|
19609
20621
|
async function findTdNodesImpl(ctx, args) {
|
|
19610
20622
|
const fetch2 = args.recursive ? async () => (await ctx.client.getNetworkTopology(args.parent_path, true)).nodes : async () => (await ctx.client.getNodes(args.parent_path)).nodes;
|
|
@@ -19628,17 +20640,17 @@ async function findTdNodesImpl(ctx, args) {
|
|
|
19628
20640
|
}
|
|
19629
20641
|
|
|
19630
20642
|
// src/tools/layer3/generateReadme.ts
|
|
19631
|
-
import { z as
|
|
19632
|
-
var generateReadmeOutputSchema =
|
|
19633
|
-
markdown:
|
|
19634
|
-
node_count:
|
|
19635
|
-
families:
|
|
19636
|
-
has_preview:
|
|
20643
|
+
import { z as z131 } from "zod";
|
|
20644
|
+
var generateReadmeOutputSchema = z131.object({
|
|
20645
|
+
markdown: z131.string().describe("Full Markdown document."),
|
|
20646
|
+
node_count: z131.number().describe("Total child nodes inspected."),
|
|
20647
|
+
families: z131.record(z131.string(), z131.number()).describe("Node counts by operator family."),
|
|
20648
|
+
has_preview: z131.boolean().describe("Whether a preview thumbnail was successfully embedded.")
|
|
19637
20649
|
});
|
|
19638
|
-
var generateReadmeSchema =
|
|
19639
|
-
path:
|
|
19640
|
-
title:
|
|
19641
|
-
include_preview:
|
|
20650
|
+
var generateReadmeSchema = z131.object({
|
|
20651
|
+
path: z131.string().default("/project1").describe("Path of the project or COMP to document (e.g. /project1 or /project1/myComp)."),
|
|
20652
|
+
title: z131.string().optional().describe("Document title. Defaults to the COMP name when omitted."),
|
|
20653
|
+
include_preview: z131.boolean().default(true).describe("Capture and embed a preview thumbnail of the output TOP as a base64 inline image.")
|
|
19642
20654
|
});
|
|
19643
20655
|
var README_SCRIPT = `
|
|
19644
20656
|
import json, base64, traceback
|
|
@@ -19883,29 +20895,29 @@ _Preview capture failed for \`${report.output_top}\`._
|
|
|
19883
20895
|
}
|
|
19884
20896
|
|
|
19885
20897
|
// src/tools/layer3/getBridgeLogs.ts
|
|
19886
|
-
import { z as
|
|
19887
|
-
var getBridgeLogsSchema =
|
|
19888
|
-
scope:
|
|
20898
|
+
import { z as z132 } from "zod";
|
|
20899
|
+
var getBridgeLogsSchema = z132.object({
|
|
20900
|
+
scope: z132.string().default("/").describe(
|
|
19889
20901
|
"Network path to collect cook errors/warnings from (default whole project). Must be an existing operator path."
|
|
19890
20902
|
),
|
|
19891
|
-
max_lines:
|
|
19892
|
-
include_cook_errors:
|
|
20903
|
+
max_lines: z132.number().int().min(1).max(500).default(100).describe("Cap how many log lines to return (1\u2013500)."),
|
|
20904
|
+
include_cook_errors: z132.boolean().default(true).describe("Include current operator cook errors/warnings across the scope.")
|
|
19893
20905
|
});
|
|
19894
|
-
var getBridgeLogsOutputSchema =
|
|
19895
|
-
scope:
|
|
19896
|
-
lines:
|
|
19897
|
-
|
|
19898
|
-
source:
|
|
19899
|
-
level:
|
|
19900
|
-
text:
|
|
19901
|
-
op:
|
|
20906
|
+
var getBridgeLogsOutputSchema = z132.object({
|
|
20907
|
+
scope: z132.string().describe("The network path that was scanned, echoing the request."),
|
|
20908
|
+
lines: z132.array(
|
|
20909
|
+
z132.object({
|
|
20910
|
+
source: z132.string().describe("Log source: 'cook' for cook errors/warnings, 'textport' for textport lines."),
|
|
20911
|
+
level: z132.string().describe("Severity level: 'error' or 'warning'."),
|
|
20912
|
+
text: z132.string().describe("The log message text."),
|
|
20913
|
+
op: z132.string().optional().describe("Full operator path that produced this line (for cook sources).")
|
|
19902
20914
|
})
|
|
19903
20915
|
).describe("Collected log lines, newest-first within each source."),
|
|
19904
|
-
count:
|
|
19905
|
-
probe:
|
|
20916
|
+
count: z132.number().describe("Total number of lines returned (after capping at max_lines)."),
|
|
20917
|
+
probe: z132.record(z132.string(), z132.unknown()).optional().describe(
|
|
19906
20918
|
"Diagnostic info about which log sources were reachable in this TD build (cook_errors always present; textport availability varies by build)."
|
|
19907
20919
|
),
|
|
19908
|
-
warnings:
|
|
20920
|
+
warnings: z132.array(z132.string()).describe("Non-fatal issues during collection (e.g. truncation notes).")
|
|
19909
20921
|
});
|
|
19910
20922
|
var GET_BRIDGE_LOGS_SCRIPT = `
|
|
19911
20923
|
import json, base64, traceback
|
|
@@ -19935,14 +20947,20 @@ try:
|
|
|
19935
20947
|
)
|
|
19936
20948
|
for _o in _all_ops:
|
|
19937
20949
|
try:
|
|
19938
|
-
|
|
20950
|
+
# op.errors()/op.warnings() return a STRING, not a list. Wrap
|
|
20951
|
+
# each in a single-element list so a (possibly multi-line)
|
|
20952
|
+
# message becomes exactly ONE log line \u2014 iterating the string
|
|
20953
|
+
# directly would emit one bogus line per character.
|
|
20954
|
+
_err = _o.errors(recurse=False)
|
|
20955
|
+
for _msg in ([str(_err)] if _err else []):
|
|
19939
20956
|
report["lines"].append({
|
|
19940
20957
|
"source": "cook",
|
|
19941
20958
|
"level": "error",
|
|
19942
20959
|
"text": str(_msg),
|
|
19943
20960
|
"op": _o.path,
|
|
19944
20961
|
})
|
|
19945
|
-
|
|
20962
|
+
_warn = _o.warnings(recurse=False)
|
|
20963
|
+
for _msg in ([str(_warn)] if _warn else []):
|
|
19946
20964
|
report["lines"].append({
|
|
19947
20965
|
"source": "cook",
|
|
19948
20966
|
"level": "warning",
|
|
@@ -20016,6 +21034,30 @@ function buildGetBridgeLogsScript(payload) {
|
|
|
20016
21034
|
async function getBridgeLogsImpl(ctx, args) {
|
|
20017
21035
|
return guardTd(
|
|
20018
21036
|
async () => {
|
|
21037
|
+
if (args.include_cook_errors) {
|
|
21038
|
+
try {
|
|
21039
|
+
const logs = await ctx.client.getLogs("all", args.max_lines, args.scope);
|
|
21040
|
+
if (logs.available) {
|
|
21041
|
+
const lines = logs.lines.map((l) => ({
|
|
21042
|
+
source: "cook",
|
|
21043
|
+
level: (l.severity || "error").toLowerCase(),
|
|
21044
|
+
text: l.message,
|
|
21045
|
+
// Omit op when the Error-DAT row has a blank source — the schema makes
|
|
21046
|
+
// it optional, and op:"" would force callers to special-case an empty path.
|
|
21047
|
+
...l.source ? { op: l.source } : {}
|
|
21048
|
+
}));
|
|
21049
|
+
return {
|
|
21050
|
+
scope: args.scope,
|
|
21051
|
+
lines,
|
|
21052
|
+
count: lines.length,
|
|
21053
|
+
probe: { endpoint: true, error_dat: logs.error_dat },
|
|
21054
|
+
warnings: logs.warnings
|
|
21055
|
+
};
|
|
21056
|
+
}
|
|
21057
|
+
} catch (err) {
|
|
21058
|
+
if (!isMissingEndpoint(err)) throw err;
|
|
21059
|
+
}
|
|
21060
|
+
}
|
|
20019
21061
|
const script = buildGetBridgeLogsScript({
|
|
20020
21062
|
scope: args.scope,
|
|
20021
21063
|
max_lines: args.max_lines,
|
|
@@ -20043,9 +21085,9 @@ async function getBridgeLogsImpl(ctx, args) {
|
|
|
20043
21085
|
}
|
|
20044
21086
|
|
|
20045
21087
|
// src/tools/layer3/getModuleHelp.ts
|
|
20046
|
-
import { z as
|
|
20047
|
-
var getModuleHelpSchema =
|
|
20048
|
-
name:
|
|
21088
|
+
import { z as z133 } from "zod";
|
|
21089
|
+
var getModuleHelpSchema = z133.object({
|
|
21090
|
+
name: z133.string().describe("Class or module name to get help for, e.g. 'OP', 'App', 'Project'.")
|
|
20049
21091
|
});
|
|
20050
21092
|
function getModuleHelpImpl(ctx, args) {
|
|
20051
21093
|
const cls = ctx.knowledge.getPythonClass(args.name);
|
|
@@ -20074,26 +21116,26 @@ function getModuleHelpImpl(ctx, args) {
|
|
|
20074
21116
|
}
|
|
20075
21117
|
|
|
20076
21118
|
// src/tools/layer3/getNodeStateRuntime.ts
|
|
20077
|
-
import { z as
|
|
20078
|
-
var getNodeStateRuntimeSchema =
|
|
20079
|
-
path:
|
|
21119
|
+
import { z as z134 } from "zod";
|
|
21120
|
+
var getNodeStateRuntimeSchema = z134.object({
|
|
21121
|
+
path: z134.string().describe("Full path of the operator to inspect (e.g. '/project1/noise1').")
|
|
20080
21122
|
});
|
|
20081
|
-
var getNodeStateRuntimeOutputSchema =
|
|
20082
|
-
path:
|
|
20083
|
-
type:
|
|
20084
|
-
family:
|
|
20085
|
-
cook_time_ms:
|
|
20086
|
-
cook_count:
|
|
21123
|
+
var getNodeStateRuntimeOutputSchema = z134.object({
|
|
21124
|
+
path: z134.string().describe("Echoed operator path."),
|
|
21125
|
+
type: z134.string().describe("Operator type string (e.g. 'noiseTOP')."),
|
|
21126
|
+
family: z134.string().optional().describe("Operator family: TOP, CHOP, SOP, DAT, COMP, MAT, etc."),
|
|
21127
|
+
cook_time_ms: z134.number().optional().describe("Last cook duration in milliseconds (op.cookTime * 1000). UNVERIFIED attr name."),
|
|
21128
|
+
cook_count: z134.number().optional().describe(
|
|
20087
21129
|
"Total number of times the op has cooked (op.totalCooks / op.cookCount). UNVERIFIED."
|
|
20088
21130
|
),
|
|
20089
|
-
last_cook_frame:
|
|
20090
|
-
resolution:
|
|
20091
|
-
num_chans:
|
|
20092
|
-
num_samples:
|
|
20093
|
-
gpu_memory:
|
|
20094
|
-
errors:
|
|
20095
|
-
warnings:
|
|
20096
|
-
extra:
|
|
21131
|
+
last_cook_frame: z134.number().optional().describe("Absolute frame number of the last cook (op.cookAbsFrame). UNVERIFIED attr name."),
|
|
21132
|
+
resolution: z134.array(z134.number()).optional().describe("[width, height] for TOPs (op.width, op.height). UNVERIFIED."),
|
|
21133
|
+
num_chans: z134.number().optional().describe("Number of channels for CHOPs (op.numChans). UNVERIFIED."),
|
|
21134
|
+
num_samples: z134.number().optional().describe("Number of samples per channel for CHOPs (op.numSamples). UNVERIFIED."),
|
|
21135
|
+
gpu_memory: z134.number().optional().describe("GPU memory used in bytes for TOPs (op.gpuMemory). UNVERIFIED attr name."),
|
|
21136
|
+
errors: z134.array(z134.string()).describe("Cook errors from op.errors(recurse=False)."),
|
|
21137
|
+
warnings: z134.array(z134.string()).describe("Bridge-level warnings about unreadable attributes."),
|
|
21138
|
+
extra: z134.record(z134.string(), z134.unknown()).optional().describe(
|
|
20097
21139
|
"Additional Info attributes found via getattr probing \u2014 allows live-validation to confirm real attr names."
|
|
20098
21140
|
)
|
|
20099
21141
|
});
|
|
@@ -20267,9 +21309,9 @@ async function getNodeStateRuntimeImpl(ctx, args) {
|
|
|
20267
21309
|
}
|
|
20268
21310
|
|
|
20269
21311
|
// src/tools/layer3/getTdClassDetails.ts
|
|
20270
|
-
import { z as
|
|
20271
|
-
var getTdClassDetailsSchema =
|
|
20272
|
-
class_name:
|
|
21312
|
+
import { z as z135 } from "zod";
|
|
21313
|
+
var getTdClassDetailsSchema = z135.object({
|
|
21314
|
+
class_name: z135.string().describe("Python class name, e.g. 'OP', 'TOP', 'App', 'CHOP'.")
|
|
20273
21315
|
});
|
|
20274
21316
|
function getTdClassDetailsImpl(ctx, args) {
|
|
20275
21317
|
const cls = ctx.knowledge.getPythonClass(args.class_name);
|
|
@@ -20287,9 +21329,9 @@ function getTdClassDetailsImpl(ctx, args) {
|
|
|
20287
21329
|
}
|
|
20288
21330
|
|
|
20289
21331
|
// src/tools/layer3/getTdClasses.ts
|
|
20290
|
-
import { z as
|
|
20291
|
-
var getTdClassesSchema =
|
|
20292
|
-
filter:
|
|
21332
|
+
import { z as z136 } from "zod";
|
|
21333
|
+
var getTdClassesSchema = z136.object({
|
|
21334
|
+
filter: z136.string().optional().describe("Optional case-insensitive substring to filter class names by.")
|
|
20293
21335
|
});
|
|
20294
21336
|
function getTdClassesImpl(ctx, args) {
|
|
20295
21337
|
let classes = ctx.knowledge.listPythonClasses();
|
|
@@ -20305,7 +21347,7 @@ function getTdClassesImpl(ctx, args) {
|
|
|
20305
21347
|
}
|
|
20306
21348
|
|
|
20307
21349
|
// src/tools/layer3/getTdInfo.ts
|
|
20308
|
-
var EXPECTED_BRIDGE_VERSION = "0.
|
|
21350
|
+
var EXPECTED_BRIDGE_VERSION = "0.6.1";
|
|
20309
21351
|
async function getTdInfoImpl(ctx) {
|
|
20310
21352
|
const knowledge = ctx.knowledge.stats();
|
|
20311
21353
|
try {
|
|
@@ -20337,17 +21379,17 @@ async function getTdInfoImpl(ctx) {
|
|
|
20337
21379
|
}
|
|
20338
21380
|
|
|
20339
21381
|
// src/tools/layer3/getTdNodeErrors.ts
|
|
20340
|
-
import { z as
|
|
20341
|
-
var getTdNodeErrorsSchema =
|
|
20342
|
-
path:
|
|
20343
|
-
recursive:
|
|
20344
|
-
summary:
|
|
21382
|
+
import { z as z137 } from "zod";
|
|
21383
|
+
var getTdNodeErrorsSchema = z137.object({
|
|
21384
|
+
path: z137.string().describe("Full path of the node (or network root) to check for errors."),
|
|
21385
|
+
recursive: z137.boolean().default(false).describe("If true, check the whole network under `path`; otherwise just that node."),
|
|
21386
|
+
summary: z137.boolean().default(false).describe("Return only counts grouped by error type instead of the full error list.")
|
|
20345
21387
|
});
|
|
20346
|
-
var getTdNodeErrorsOutputSchema =
|
|
20347
|
-
path:
|
|
20348
|
-
total:
|
|
20349
|
-
errors:
|
|
20350
|
-
by_type:
|
|
21388
|
+
var getTdNodeErrorsOutputSchema = z137.object({
|
|
21389
|
+
path: z137.string().describe("The node or network root that was checked, echoing the request."),
|
|
21390
|
+
total: z137.number().describe("Total number of errors/warnings found (0 means clean)."),
|
|
21391
|
+
errors: z137.array(NodeErrorSchema).optional().describe("Full mode: each error/warning with its node path, type and message."),
|
|
21392
|
+
by_type: z137.record(z137.string(), z137.number()).optional().describe("summary mode: count of errors grouped by error type.")
|
|
20351
21393
|
});
|
|
20352
21394
|
async function getTdNodeErrorsImpl(ctx, args) {
|
|
20353
21395
|
return guardTd(
|
|
@@ -20375,12 +21417,300 @@ async function getTdNodeErrorsImpl(ctx, args) {
|
|
|
20375
21417
|
);
|
|
20376
21418
|
}
|
|
20377
21419
|
|
|
21420
|
+
// src/tools/layer3/getTdNodeFlags.ts
|
|
21421
|
+
import { z as z138 } from "zod";
|
|
21422
|
+
var getTdNodeFlagsSchema = z138.object({
|
|
21423
|
+
path: z138.string().describe(
|
|
21424
|
+
"Full path of the node to inspect, or the COMP whose children to scan when recursive is set."
|
|
21425
|
+
),
|
|
21426
|
+
recursive: z138.boolean().default(false).describe(
|
|
21427
|
+
"Also scan the immediate children (depth 1) of path. Use this on a container to diagnose its whole network in one round-trip."
|
|
21428
|
+
),
|
|
21429
|
+
only_problems: z138.boolean().default(false).describe(
|
|
21430
|
+
"Return only nodes whose flags or cook errors would suppress output: bypass on, allowCooking off, or a cook error present. Conservative \u2014 display/render are reported but never used to filter (they default off on many visible ops)."
|
|
21431
|
+
),
|
|
21432
|
+
max_nodes: z138.number().int().min(1).max(500).default(200).describe("Cap the number of nodes scanned during a recursive subtree walk.")
|
|
21433
|
+
});
|
|
21434
|
+
var nodeFlagsSchema = z138.object({
|
|
21435
|
+
bypass: z138.boolean().optional(),
|
|
21436
|
+
render: z138.boolean().optional(),
|
|
21437
|
+
display: z138.boolean().optional(),
|
|
21438
|
+
lock: z138.boolean().optional(),
|
|
21439
|
+
allowCooking: z138.boolean().optional(),
|
|
21440
|
+
cloneImmune: z138.boolean().optional(),
|
|
21441
|
+
is_clone: z138.boolean().optional(),
|
|
21442
|
+
clone: z138.string().nullable().optional()
|
|
21443
|
+
});
|
|
21444
|
+
var nodeWireSchema = z138.object({
|
|
21445
|
+
in_index: z138.number().int().nullable(),
|
|
21446
|
+
from: z138.string(),
|
|
21447
|
+
out_index: z138.number().int()
|
|
21448
|
+
});
|
|
21449
|
+
var nodeFlagsEntrySchema = z138.object({
|
|
21450
|
+
path: z138.string(),
|
|
21451
|
+
type: z138.string(),
|
|
21452
|
+
name: z138.string(),
|
|
21453
|
+
flags: nodeFlagsSchema,
|
|
21454
|
+
wires_in: z138.array(nodeWireSchema),
|
|
21455
|
+
nodeX: z138.number().optional(),
|
|
21456
|
+
nodeY: z138.number().optional(),
|
|
21457
|
+
color: z138.array(z138.number()).optional(),
|
|
21458
|
+
comment: z138.string().optional(),
|
|
21459
|
+
errors: z138.array(z138.string()),
|
|
21460
|
+
suspect_reason: z138.string().optional()
|
|
21461
|
+
});
|
|
21462
|
+
var getTdNodeFlagsOutputSchema = z138.object({
|
|
21463
|
+
path: z138.string(),
|
|
21464
|
+
scanned: z138.number().int(),
|
|
21465
|
+
nodes: z138.array(nodeFlagsEntrySchema),
|
|
21466
|
+
probe: z138.record(z138.string(), z138.unknown()).optional(),
|
|
21467
|
+
warnings: z138.array(z138.string())
|
|
21468
|
+
});
|
|
21469
|
+
var GET_FLAGS_SCRIPT = `
|
|
21470
|
+
import json, base64, traceback
|
|
21471
|
+
_p = json.loads(base64.b64decode("__PAYLOAD_B64__").decode("utf-8"))
|
|
21472
|
+
report = {"path": _p["path"], "scanned": 0, "nodes": [], "warnings": []}
|
|
21473
|
+
|
|
21474
|
+
def _flags(_o):
|
|
21475
|
+
out = {}
|
|
21476
|
+
for _attr in ("bypass", "render", "display", "lock", "allowCooking", "cloneImmune"):
|
|
21477
|
+
try:
|
|
21478
|
+
_v = getattr(_o, _attr)
|
|
21479
|
+
if isinstance(_v, bool):
|
|
21480
|
+
out[_attr] = _v
|
|
21481
|
+
except Exception:
|
|
21482
|
+
pass
|
|
21483
|
+
# clone is COMP-only and lives on .par.clone (path to master), NOT op.clone.
|
|
21484
|
+
try:
|
|
21485
|
+
if hasattr(_o, "isClone"):
|
|
21486
|
+
out["is_clone"] = bool(_o.isClone)
|
|
21487
|
+
except Exception:
|
|
21488
|
+
pass
|
|
21489
|
+
try:
|
|
21490
|
+
_cp = getattr(_o.par, "clone", None)
|
|
21491
|
+
if _cp is not None:
|
|
21492
|
+
_cv = _cp.eval()
|
|
21493
|
+
out["clone"] = str(_cv) if _cv else None
|
|
21494
|
+
except Exception:
|
|
21495
|
+
pass
|
|
21496
|
+
return out
|
|
21497
|
+
|
|
21498
|
+
def _wires_in(_o):
|
|
21499
|
+
# Faithful, index-aware: iterate inputConnectors (NOT _o.inputs, which omits
|
|
21500
|
+
# empty slots). Each wire => {in_index, from, out_index}.
|
|
21501
|
+
_wires = []
|
|
21502
|
+
try:
|
|
21503
|
+
for _ic in _o.inputConnectors:
|
|
21504
|
+
try:
|
|
21505
|
+
_in_index = _ic.index
|
|
21506
|
+
except Exception:
|
|
21507
|
+
_in_index = None
|
|
21508
|
+
try:
|
|
21509
|
+
_conns = list(_ic.connections)
|
|
21510
|
+
except Exception:
|
|
21511
|
+
_conns = []
|
|
21512
|
+
for _oc in _conns:
|
|
21513
|
+
try:
|
|
21514
|
+
_wires.append({"in_index": _in_index, "from": _oc.owner.path, "out_index": _oc.index})
|
|
21515
|
+
except Exception:
|
|
21516
|
+
pass
|
|
21517
|
+
except Exception:
|
|
21518
|
+
pass
|
|
21519
|
+
return _wires
|
|
21520
|
+
|
|
21521
|
+
def _errors(_o):
|
|
21522
|
+
# op.errors() returns a STRING, not a list \u2014 wrap it, never iterate it.
|
|
21523
|
+
try:
|
|
21524
|
+
_s = _o.errors(recurse=False)
|
|
21525
|
+
if _s:
|
|
21526
|
+
return [str(_s)]
|
|
21527
|
+
except Exception:
|
|
21528
|
+
pass
|
|
21529
|
+
return []
|
|
21530
|
+
|
|
21531
|
+
def _entry(_o, _only_problems):
|
|
21532
|
+
_e = {
|
|
21533
|
+
"path": _o.path,
|
|
21534
|
+
"type": _o.type,
|
|
21535
|
+
"name": _o.name,
|
|
21536
|
+
"flags": _flags(_o),
|
|
21537
|
+
"wires_in": _wires_in(_o),
|
|
21538
|
+
"errors": _errors(_o),
|
|
21539
|
+
}
|
|
21540
|
+
try:
|
|
21541
|
+
_e["nodeX"] = _o.nodeX
|
|
21542
|
+
_e["nodeY"] = _o.nodeY
|
|
21543
|
+
except Exception:
|
|
21544
|
+
pass
|
|
21545
|
+
try:
|
|
21546
|
+
_e["color"] = list(_o.color)
|
|
21547
|
+
except Exception:
|
|
21548
|
+
pass
|
|
21549
|
+
try:
|
|
21550
|
+
if _o.comment:
|
|
21551
|
+
_e["comment"] = _o.comment
|
|
21552
|
+
except Exception:
|
|
21553
|
+
pass
|
|
21554
|
+
# Conservative suspect classification: only the unambiguous, high-signal cases.
|
|
21555
|
+
_reasons = []
|
|
21556
|
+
if _e["flags"].get("bypass") is True:
|
|
21557
|
+
_reasons.append("bypass on")
|
|
21558
|
+
if _e["flags"].get("allowCooking") is False:
|
|
21559
|
+
_reasons.append("cooking disabled")
|
|
21560
|
+
if _e["errors"]:
|
|
21561
|
+
_reasons.append("cook error")
|
|
21562
|
+
if _reasons:
|
|
21563
|
+
_e["suspect_reason"] = ", ".join(_reasons)
|
|
21564
|
+
return _e, bool(_reasons)
|
|
21565
|
+
|
|
21566
|
+
try:
|
|
21567
|
+
_root = op(_p["path"])
|
|
21568
|
+
if _root is None:
|
|
21569
|
+
report["fatal"] = "Node not found: " + str(_p["path"])
|
|
21570
|
+
else:
|
|
21571
|
+
_only = bool(_p.get("only_problems", False))
|
|
21572
|
+
_recursive = bool(_p.get("recursive", False))
|
|
21573
|
+
_max = int(_p.get("max_nodes", 200))
|
|
21574
|
+
_targets = [_root]
|
|
21575
|
+
if _recursive:
|
|
21576
|
+
try:
|
|
21577
|
+
_targets += list(_root.findChildren(depth=1))
|
|
21578
|
+
except Exception as _fe:
|
|
21579
|
+
report["warnings"].append("findChildren failed: " + str(_fe))
|
|
21580
|
+
_first = True
|
|
21581
|
+
for _o in _targets:
|
|
21582
|
+
if report["scanned"] >= _max:
|
|
21583
|
+
report["warnings"].append("max_nodes (" + str(_max) + ") reached; subtree scan truncated.")
|
|
21584
|
+
break
|
|
21585
|
+
report["scanned"] += 1
|
|
21586
|
+
try:
|
|
21587
|
+
# Probe the first node's available flags to confirm the real TD API.
|
|
21588
|
+
if _first:
|
|
21589
|
+
_first = False
|
|
21590
|
+
try:
|
|
21591
|
+
report["probe"] = {
|
|
21592
|
+
"flags_present": sorted(_flags(_o).keys()),
|
|
21593
|
+
"has_inputConnectors": hasattr(_o, "inputConnectors"),
|
|
21594
|
+
"errors_is_str": isinstance(_o.errors(recurse=False), str),
|
|
21595
|
+
}
|
|
21596
|
+
except Exception:
|
|
21597
|
+
report["probe"] = {"error": traceback.format_exc().splitlines()[-1]}
|
|
21598
|
+
_ent, _is_problem = _entry(_o, _only)
|
|
21599
|
+
if _only and not _is_problem:
|
|
21600
|
+
continue
|
|
21601
|
+
report["nodes"].append(_ent)
|
|
21602
|
+
except Exception:
|
|
21603
|
+
try:
|
|
21604
|
+
report["warnings"].append("Error reading " + str(_o.path) + ": " + traceback.format_exc().splitlines()[-1])
|
|
21605
|
+
except Exception:
|
|
21606
|
+
report["warnings"].append("Error reading node: " + traceback.format_exc().splitlines()[-1])
|
|
21607
|
+
except Exception:
|
|
21608
|
+
report["fatal"] = traceback.format_exc().splitlines()[-1]
|
|
21609
|
+
print(json.dumps(report))
|
|
21610
|
+
`;
|
|
21611
|
+
function buildGetFlagsScript(payload) {
|
|
21612
|
+
return buildPayloadScript(GET_FLAGS_SCRIPT, payload);
|
|
21613
|
+
}
|
|
21614
|
+
function detailToFlagsEntry(d) {
|
|
21615
|
+
const errors = d.errors ?? [];
|
|
21616
|
+
const flags = d.flags ?? {};
|
|
21617
|
+
const entry = {
|
|
21618
|
+
path: d.path,
|
|
21619
|
+
type: d.type,
|
|
21620
|
+
name: d.name,
|
|
21621
|
+
flags,
|
|
21622
|
+
wires_in: d.wires_in ?? [],
|
|
21623
|
+
errors
|
|
21624
|
+
};
|
|
21625
|
+
if (d.nodeX !== void 0) entry.nodeX = d.nodeX;
|
|
21626
|
+
if (d.nodeY !== void 0) entry.nodeY = d.nodeY;
|
|
21627
|
+
if (d.color !== void 0) entry.color = d.color;
|
|
21628
|
+
if (d.comment) entry.comment = d.comment;
|
|
21629
|
+
const reasons = [];
|
|
21630
|
+
if (flags.bypass === true) reasons.push("bypass on");
|
|
21631
|
+
if (flags.allowCooking === false) reasons.push("cooking disabled");
|
|
21632
|
+
if (errors.length > 0) reasons.push("cook error");
|
|
21633
|
+
if (reasons.length > 0) entry.suspect_reason = reasons.join(", ");
|
|
21634
|
+
return entry;
|
|
21635
|
+
}
|
|
21636
|
+
async function getTdNodeFlagsImpl(ctx, args) {
|
|
21637
|
+
return guardTd(
|
|
21638
|
+
async () => {
|
|
21639
|
+
try {
|
|
21640
|
+
const root = await ctx.client.getNode(args.path);
|
|
21641
|
+
if (root.flags !== void 0) {
|
|
21642
|
+
const report = {
|
|
21643
|
+
path: args.path,
|
|
21644
|
+
scanned: 0,
|
|
21645
|
+
nodes: [],
|
|
21646
|
+
warnings: []
|
|
21647
|
+
};
|
|
21648
|
+
const details = [root];
|
|
21649
|
+
if (args.recursive) {
|
|
21650
|
+
try {
|
|
21651
|
+
const list = await ctx.client.getNodes(args.path);
|
|
21652
|
+
for (const child of list.nodes) {
|
|
21653
|
+
if (details.length >= args.max_nodes) {
|
|
21654
|
+
report.warnings.push(
|
|
21655
|
+
`max_nodes (${args.max_nodes}) reached; subtree scan truncated.`
|
|
21656
|
+
);
|
|
21657
|
+
break;
|
|
21658
|
+
}
|
|
21659
|
+
try {
|
|
21660
|
+
details.push(await ctx.client.getNode(child.path));
|
|
21661
|
+
} catch (childErr) {
|
|
21662
|
+
report.warnings.push(`Error reading ${child.path}: ${friendlyTdError(childErr)}`);
|
|
21663
|
+
}
|
|
21664
|
+
}
|
|
21665
|
+
} catch (listErr) {
|
|
21666
|
+
report.warnings.push(`findChildren failed: ${friendlyTdError(listErr)}`);
|
|
21667
|
+
}
|
|
21668
|
+
}
|
|
21669
|
+
for (const d of details) {
|
|
21670
|
+
report.scanned += 1;
|
|
21671
|
+
const entry = detailToFlagsEntry(d);
|
|
21672
|
+
if (args.only_problems && !entry.suspect_reason) continue;
|
|
21673
|
+
report.nodes.push(entry);
|
|
21674
|
+
}
|
|
21675
|
+
report.probe = { endpoint: "node_detail", flags_present: Object.keys(root.flags).sort() };
|
|
21676
|
+
return report;
|
|
21677
|
+
}
|
|
21678
|
+
} catch (err) {
|
|
21679
|
+
if (!isMissingEndpoint(err)) throw err;
|
|
21680
|
+
}
|
|
21681
|
+
const script = buildGetFlagsScript({
|
|
21682
|
+
path: args.path,
|
|
21683
|
+
recursive: args.recursive,
|
|
21684
|
+
only_problems: args.only_problems,
|
|
21685
|
+
max_nodes: args.max_nodes
|
|
21686
|
+
});
|
|
21687
|
+
const exec = await ctx.client.executePythonScript(script, true);
|
|
21688
|
+
return parsePythonReport(exec.stdout);
|
|
21689
|
+
},
|
|
21690
|
+
(report) => {
|
|
21691
|
+
if (report.fatal) {
|
|
21692
|
+
return errorResult(`get_td_node_flags failed: ${report.fatal}`, report);
|
|
21693
|
+
}
|
|
21694
|
+
const suspects = report.nodes.filter((n) => n.suspect_reason).length;
|
|
21695
|
+
const scope = args.recursive ? " (subtree)" : "";
|
|
21696
|
+
const summary = `Inspected ${report.scanned} node(s) under ${report.path}${scope} \u2014 ${report.nodes.length} reported, ${suspects} suspect.`;
|
|
21697
|
+
return structuredResult(summary, {
|
|
21698
|
+
path: report.path,
|
|
21699
|
+
scanned: report.scanned,
|
|
21700
|
+
nodes: report.nodes,
|
|
21701
|
+
probe: report.probe,
|
|
21702
|
+
warnings: report.warnings
|
|
21703
|
+
});
|
|
21704
|
+
}
|
|
21705
|
+
);
|
|
21706
|
+
}
|
|
21707
|
+
|
|
20378
21708
|
// src/tools/layer3/getTdNodeParameters.ts
|
|
20379
|
-
import { z as
|
|
20380
|
-
var getTdNodeParametersSchema =
|
|
20381
|
-
path:
|
|
20382
|
-
keys:
|
|
20383
|
-
omit_io:
|
|
21709
|
+
import { z as z139 } from "zod";
|
|
21710
|
+
var getTdNodeParametersSchema = z139.object({
|
|
21711
|
+
path: z139.string().describe("Full path of the node to inspect."),
|
|
21712
|
+
keys: z139.array(z139.string()).optional().describe("Only return these parameter names (case-sensitive). Omit to return all parameters."),
|
|
21713
|
+
omit_io: z139.boolean().default(false).describe("Drop the inputs/outputs lists from the result to save context.")
|
|
20384
21714
|
});
|
|
20385
21715
|
async function getTdNodeParametersImpl(ctx, args) {
|
|
20386
21716
|
return guardTd(
|
|
@@ -20410,29 +21740,29 @@ async function getTdNodeParametersImpl(ctx, args) {
|
|
|
20410
21740
|
}
|
|
20411
21741
|
|
|
20412
21742
|
// src/tools/layer3/getTdNodes.ts
|
|
20413
|
-
import { z as
|
|
20414
|
-
var getTdNodesSchema =
|
|
20415
|
-
parent_path:
|
|
20416
|
-
pattern:
|
|
21743
|
+
import { z as z140 } from "zod";
|
|
21744
|
+
var getTdNodesSchema = z140.object({
|
|
21745
|
+
parent_path: z140.string().default("/project1").describe("Parent COMP whose direct children should be listed."),
|
|
21746
|
+
pattern: z140.string().optional().describe(
|
|
20417
21747
|
"Case-insensitive filter on node name/path. Supports '*' wildcards (e.g. 'text*', '*noise*')."
|
|
20418
21748
|
),
|
|
20419
|
-
path_only:
|
|
20420
|
-
limit:
|
|
20421
|
-
detail_level:
|
|
21749
|
+
path_only: z140.boolean().default(false).describe("Return only the list of node paths, dropping type/name."),
|
|
21750
|
+
limit: z140.number().int().positive().optional().describe("Cap the number of nodes returned."),
|
|
21751
|
+
detail_level: z140.enum(["summary", "full"]).default("summary").describe(
|
|
20422
21752
|
"'summary' (default) returns a count, a type breakdown and the first few paths; 'full' returns every node. Use 'full' (or path_only) when you need the complete list."
|
|
20423
21753
|
)
|
|
20424
21754
|
});
|
|
20425
21755
|
var SAMPLE_SIZE = 10;
|
|
20426
|
-
var getTdNodesOutputSchema =
|
|
20427
|
-
parent_path:
|
|
20428
|
-
count:
|
|
20429
|
-
detail_level:
|
|
20430
|
-
truncated:
|
|
20431
|
-
by_type:
|
|
20432
|
-
sample:
|
|
20433
|
-
paths:
|
|
20434
|
-
nodes:
|
|
20435
|
-
hint:
|
|
21756
|
+
var getTdNodesOutputSchema = z140.object({
|
|
21757
|
+
parent_path: z140.string().describe("The parent COMP whose children were listed."),
|
|
21758
|
+
count: z140.number().describe("Number of children matched (before any limit truncation)."),
|
|
21759
|
+
detail_level: z140.enum(["summary", "full"]).describe("Which detail level produced this result, echoing the request."),
|
|
21760
|
+
truncated: z140.boolean().describe("True if `limit` cut the list short of the full match count."),
|
|
21761
|
+
by_type: z140.record(z140.string(), z140.number()).optional().describe("Summary mode: count of matched nodes per operator type."),
|
|
21762
|
+
sample: z140.array(z140.string()).optional().describe("Summary mode: paths of the first few matched nodes."),
|
|
21763
|
+
paths: z140.array(z140.string()).optional().describe("path_only mode: the matched node paths and nothing else."),
|
|
21764
|
+
nodes: z140.array(NodeRefSchema).optional().describe("Full mode: every matched node as {path, name, type}."),
|
|
21765
|
+
hint: z140.string().optional().describe("Summary mode: note that the list was sampled, with how to get all of it.")
|
|
20436
21766
|
});
|
|
20437
21767
|
async function getTdNodesImpl(ctx, args) {
|
|
20438
21768
|
return guardTd(
|
|
@@ -20487,7 +21817,7 @@ async function getTdNodesImpl(ctx, args) {
|
|
|
20487
21817
|
}
|
|
20488
21818
|
|
|
20489
21819
|
// src/tools/layer3/getTdPerformance.ts
|
|
20490
|
-
import { z as
|
|
21820
|
+
import { z as z141 } from "zod";
|
|
20491
21821
|
|
|
20492
21822
|
// src/feedback/performanceMonitor.ts
|
|
20493
21823
|
async function checkPerformance(client, path2, targetFps = 60, recursive = true) {
|
|
@@ -20512,26 +21842,26 @@ async function checkPerformance(client, path2, targetFps = 60, recursive = true)
|
|
|
20512
21842
|
}
|
|
20513
21843
|
|
|
20514
21844
|
// src/tools/layer3/getTdPerformance.ts
|
|
20515
|
-
var getTdPerformanceSchema =
|
|
20516
|
-
root_path:
|
|
20517
|
-
target_fps:
|
|
20518
|
-
recursive:
|
|
21845
|
+
var getTdPerformanceSchema = z141.object({
|
|
21846
|
+
root_path: z141.string().default("/project1").describe("Network root to measure cook times under."),
|
|
21847
|
+
target_fps: z141.number().positive().default(60).describe("Frame-rate target used to flag slow nodes."),
|
|
21848
|
+
recursive: z141.boolean().default(true).describe(
|
|
20519
21849
|
"Measure every descendant (true, default) so cook time inside generated containers is counted, not just the root's direct children."
|
|
20520
21850
|
)
|
|
20521
21851
|
});
|
|
20522
|
-
var getTdPerformanceOutputSchema =
|
|
20523
|
-
path:
|
|
20524
|
-
targetFps:
|
|
20525
|
-
frameBudgetMs:
|
|
20526
|
-
totalCookMs:
|
|
20527
|
-
nodes:
|
|
20528
|
-
|
|
20529
|
-
path:
|
|
20530
|
-
cook_time_ms:
|
|
20531
|
-
cook_count:
|
|
21852
|
+
var getTdPerformanceOutputSchema = z141.object({
|
|
21853
|
+
path: z141.string().describe("The network root that was measured, echoing the request."),
|
|
21854
|
+
targetFps: z141.number().describe("The frame-rate target used to derive the per-frame budget."),
|
|
21855
|
+
frameBudgetMs: z141.number().describe("Milliseconds available per frame at the target FPS (1000 / targetFps)."),
|
|
21856
|
+
totalCookMs: z141.number().describe("Sum of the measured nodes' last cook times, in milliseconds."),
|
|
21857
|
+
nodes: z141.array(
|
|
21858
|
+
z141.object({
|
|
21859
|
+
path: z141.string().describe("Path of the measured node."),
|
|
21860
|
+
cook_time_ms: z141.number().describe("That node's last cook time in milliseconds."),
|
|
21861
|
+
cook_count: z141.number().optional().describe("How many times the node has cooked, when reported by TD.")
|
|
20532
21862
|
})
|
|
20533
21863
|
).describe("Per-node cook times, slowest first."),
|
|
20534
|
-
warnings:
|
|
21864
|
+
warnings: z141.array(z141.string()).describe(
|
|
20535
21865
|
"Budget warnings: one line per node whose cook time exceeds the frame budget, plus a final aggregate line when the summed total cook time exceeds the budget. Empty when everything is within budget."
|
|
20536
21866
|
)
|
|
20537
21867
|
});
|
|
@@ -20546,7 +21876,7 @@ async function getTdPerformanceImpl(ctx, args) {
|
|
|
20546
21876
|
}
|
|
20547
21877
|
|
|
20548
21878
|
// src/tools/layer3/getTdTopology.ts
|
|
20549
|
-
import { z as
|
|
21879
|
+
import { z as z142 } from "zod";
|
|
20550
21880
|
|
|
20551
21881
|
// src/feedback/networkVerifier.ts
|
|
20552
21882
|
async function verifyNetwork(client, path2) {
|
|
@@ -20567,14 +21897,14 @@ async function verifyNetwork(client, path2) {
|
|
|
20567
21897
|
}
|
|
20568
21898
|
|
|
20569
21899
|
// src/tools/layer3/getTdTopology.ts
|
|
20570
|
-
var getTdTopologySchema =
|
|
20571
|
-
root_path:
|
|
21900
|
+
var getTdTopologySchema = z142.object({
|
|
21901
|
+
root_path: z142.string().default("/project1").describe("Network root to map.")
|
|
20572
21902
|
});
|
|
20573
|
-
var getTdTopologyOutputSchema =
|
|
20574
|
-
path:
|
|
20575
|
-
nodeCount:
|
|
20576
|
-
connectionCount:
|
|
20577
|
-
issues:
|
|
21903
|
+
var getTdTopologyOutputSchema = z142.object({
|
|
21904
|
+
path: z142.string().describe("The network root that was mapped, echoing the request."),
|
|
21905
|
+
nodeCount: z142.number().describe("Total number of nodes found under the root."),
|
|
21906
|
+
connectionCount: z142.number().describe("Total number of wires (connections) between those nodes."),
|
|
21907
|
+
issues: z142.array(z142.string()).describe("Plain-language structural problems detected, e.g. dangling or orphaned nodes."),
|
|
20578
21908
|
topology: TopologySchema.describe("The full graph: the node list and the connection list.")
|
|
20579
21909
|
});
|
|
20580
21910
|
async function getTdTopologyImpl(ctx, args) {
|
|
@@ -20588,38 +21918,38 @@ async function getTdTopologyImpl(ctx, args) {
|
|
|
20588
21918
|
}
|
|
20589
21919
|
|
|
20590
21920
|
// src/tools/layer3/inspectComponent.ts
|
|
20591
|
-
import { z as
|
|
20592
|
-
var inspectComponentSchema =
|
|
20593
|
-
path:
|
|
20594
|
-
include_storage:
|
|
20595
|
-
include_extensions:
|
|
20596
|
-
include_custom_pars:
|
|
21921
|
+
import { z as z143 } from "zod";
|
|
21922
|
+
var inspectComponentSchema = z143.object({
|
|
21923
|
+
path: z143.string().describe("COMP to inspect."),
|
|
21924
|
+
include_storage: z143.boolean().default(true).describe("Include the COMP's Python storage dict (keys + JSON-able values)."),
|
|
21925
|
+
include_extensions: z143.boolean().default(true).describe("Include extension classes + promoted members."),
|
|
21926
|
+
include_custom_pars: z143.boolean().default(true).describe("Include custom-parameter definitions (page/name/style/default).")
|
|
20597
21927
|
});
|
|
20598
|
-
var inspectComponentOutputSchema =
|
|
20599
|
-
path:
|
|
20600
|
-
type:
|
|
20601
|
-
storage:
|
|
21928
|
+
var inspectComponentOutputSchema = z143.object({
|
|
21929
|
+
path: z143.string().describe("Full path of the inspected COMP."),
|
|
21930
|
+
type: z143.string().describe("Operator type of the COMP (e.g. 'baseCOMP')."),
|
|
21931
|
+
storage: z143.record(z143.string(), z143.unknown()).optional().describe(
|
|
20602
21932
|
"Python storage dict \u2014 keys and their JSON-serializable values (non-serializable values are stringified)."
|
|
20603
21933
|
),
|
|
20604
|
-
extensions:
|
|
20605
|
-
|
|
20606
|
-
name:
|
|
20607
|
-
promoted:
|
|
20608
|
-
members:
|
|
21934
|
+
extensions: z143.array(
|
|
21935
|
+
z143.object({
|
|
21936
|
+
name: z143.string().describe("Extension class name as registered on the COMP."),
|
|
21937
|
+
promoted: z143.boolean().describe("Whether the extension's members are promoted onto the COMP."),
|
|
21938
|
+
members: z143.array(z143.string()).describe("Public (non-dunder) member names on the extension object (capped at 50).")
|
|
20609
21939
|
})
|
|
20610
21940
|
).optional().describe("Extension class descriptors attached to the COMP."),
|
|
20611
|
-
custom_pars:
|
|
20612
|
-
|
|
20613
|
-
page:
|
|
20614
|
-
name:
|
|
20615
|
-
style:
|
|
20616
|
-
default:
|
|
21941
|
+
custom_pars: z143.array(
|
|
21942
|
+
z143.object({
|
|
21943
|
+
page: z143.string().describe("Custom-parameter page name."),
|
|
21944
|
+
name: z143.string().describe("Parameter name."),
|
|
21945
|
+
style: z143.string().describe("Parameter style / widget type (e.g. 'Float', 'Toggle', 'Menu')."),
|
|
21946
|
+
default: z143.unknown().optional().describe("Default value, if readable.")
|
|
20617
21947
|
})
|
|
20618
21948
|
).optional().describe("Custom-parameter definitions on the COMP, across all custom pages."),
|
|
20619
|
-
probe:
|
|
21949
|
+
probe: z143.record(z143.string(), z143.unknown()).optional().describe(
|
|
20620
21950
|
"API-reachability map from the bridge \u2014 records which storage/extension/custom-par APIs were available on this TD build. UNVERIFIED: exact attribute names vary by build."
|
|
20621
21951
|
),
|
|
20622
|
-
warnings:
|
|
21952
|
+
warnings: z143.array(z143.string()).describe("Per-item problems that did not abort the inspection.")
|
|
20623
21953
|
});
|
|
20624
21954
|
var INSPECT_SCRIPT = `
|
|
20625
21955
|
import json, base64, traceback
|
|
@@ -20845,14 +22175,14 @@ async function inspectComponentImpl(ctx, args) {
|
|
|
20845
22175
|
}
|
|
20846
22176
|
|
|
20847
22177
|
// src/tools/layer3/optimizePerformance.ts
|
|
20848
|
-
import { z as
|
|
20849
|
-
var optimizePerformanceSchema =
|
|
20850
|
-
path:
|
|
20851
|
-
threshold_ms:
|
|
20852
|
-
apply:
|
|
22178
|
+
import { z as z144 } from "zod";
|
|
22179
|
+
var optimizePerformanceSchema = z144.object({
|
|
22180
|
+
path: z144.string().default("/project1").describe("Network to analyze (recursively)."),
|
|
22181
|
+
threshold_ms: z144.coerce.number().positive().default(2).describe("Flag nodes whose last cook took at least this many milliseconds."),
|
|
22182
|
+
apply: z144.boolean().default(false).describe(
|
|
20853
22183
|
"If true, actually lower the resolution of the flagged TOPs by `scale`. Default false = just report the bottlenecks and suggestions."
|
|
20854
22184
|
),
|
|
20855
|
-
scale:
|
|
22185
|
+
scale: z144.coerce.number().min(0.1).max(1).default(0.5).describe("(apply) Resolution multiplier for flagged TOPs (0.5 = half on each axis).")
|
|
20856
22186
|
});
|
|
20857
22187
|
var OPTIMIZE_SCRIPT = `
|
|
20858
22188
|
import json, base64, traceback
|
|
@@ -20932,32 +22262,32 @@ async function optimizePerformanceImpl(ctx, args) {
|
|
|
20932
22262
|
}
|
|
20933
22263
|
|
|
20934
22264
|
// src/tools/layer3/readParameterModes.ts
|
|
20935
|
-
import { z as
|
|
20936
|
-
var readParameterModesSchema =
|
|
20937
|
-
path:
|
|
20938
|
-
keys:
|
|
20939
|
-
non_default_only:
|
|
22265
|
+
import { z as z145 } from "zod";
|
|
22266
|
+
var readParameterModesSchema = z145.object({
|
|
22267
|
+
path: z145.string().describe("Full path of the node whose parameters to inspect."),
|
|
22268
|
+
keys: z145.array(z145.string()).optional().describe("Only report these parameter names (case-sensitive). Omit for all parameters."),
|
|
22269
|
+
non_default_only: z145.boolean().default(false).describe(
|
|
20940
22270
|
"Only return parameters whose mode is not plain constant (i.e. expression/export/bind) \u2014 the ones that matter for a faithful round-trip."
|
|
20941
22271
|
)
|
|
20942
22272
|
});
|
|
20943
|
-
var parameterModeInfoSchema =
|
|
20944
|
-
name:
|
|
20945
|
-
value:
|
|
20946
|
-
mode:
|
|
20947
|
-
expr:
|
|
20948
|
-
bind_expr:
|
|
20949
|
-
export_op:
|
|
20950
|
-
expression:
|
|
20951
|
-
bind_expression:
|
|
20952
|
-
export_source:
|
|
22273
|
+
var parameterModeInfoSchema = z145.object({
|
|
22274
|
+
name: z145.string(),
|
|
22275
|
+
value: z145.unknown().optional(),
|
|
22276
|
+
mode: z145.string(),
|
|
22277
|
+
expr: z145.string().optional(),
|
|
22278
|
+
bind_expr: z145.string().optional(),
|
|
22279
|
+
export_op: z145.string().optional(),
|
|
22280
|
+
expression: z145.string().optional(),
|
|
22281
|
+
bind_expression: z145.string().optional(),
|
|
22282
|
+
export_source: z145.string().optional()
|
|
20953
22283
|
});
|
|
20954
|
-
var readParameterModesOutputSchema =
|
|
20955
|
-
path:
|
|
20956
|
-
type:
|
|
20957
|
-
name:
|
|
20958
|
-
parameters:
|
|
20959
|
-
probe:
|
|
20960
|
-
warnings:
|
|
22284
|
+
var readParameterModesOutputSchema = z145.object({
|
|
22285
|
+
path: z145.string(),
|
|
22286
|
+
type: z145.string(),
|
|
22287
|
+
name: z145.string(),
|
|
22288
|
+
parameters: z145.array(parameterModeInfoSchema),
|
|
22289
|
+
probe: z145.record(z145.string(), z145.unknown()).optional(),
|
|
22290
|
+
warnings: z145.array(z145.string())
|
|
20961
22291
|
});
|
|
20962
22292
|
var READ_PARAMETER_MODES_SCRIPT = `
|
|
20963
22293
|
import json, base64, math, traceback
|
|
@@ -21068,6 +22398,18 @@ function buildReadParameterModesScript(payload) {
|
|
|
21068
22398
|
async function readParameterModesImpl(ctx, args) {
|
|
21069
22399
|
return guardTd(
|
|
21070
22400
|
async () => {
|
|
22401
|
+
try {
|
|
22402
|
+
const r2 = await ctx.client.readParameterModes(args.path, args.keys, args.non_default_only);
|
|
22403
|
+
return {
|
|
22404
|
+
path: r2.path,
|
|
22405
|
+
type: r2.type,
|
|
22406
|
+
name: r2.name,
|
|
22407
|
+
parameters: r2.parameters,
|
|
22408
|
+
warnings: r2.warnings
|
|
22409
|
+
};
|
|
22410
|
+
} catch (err) {
|
|
22411
|
+
if (!isMissingEndpoint(err)) throw err;
|
|
22412
|
+
}
|
|
21071
22413
|
const script = buildReadParameterModesScript({
|
|
21072
22414
|
path: args.path,
|
|
21073
22415
|
keys: args.keys ?? null,
|
|
@@ -21095,15 +22437,15 @@ async function readParameterModesImpl(ctx, args) {
|
|
|
21095
22437
|
}
|
|
21096
22438
|
|
|
21097
22439
|
// src/tools/layer3/recordMovie.ts
|
|
21098
|
-
import { z as
|
|
21099
|
-
var recordMovieSchema =
|
|
21100
|
-
action:
|
|
21101
|
-
node_path:
|
|
21102
|
-
file:
|
|
22440
|
+
import { z as z146 } from "zod";
|
|
22441
|
+
var recordMovieSchema = z146.object({
|
|
22442
|
+
action: z146.enum(["start", "stop"]).default("start").describe("start recording the TOP to a file, or stop the current recording."),
|
|
22443
|
+
node_path: z146.string().describe("Path of the TOP to record."),
|
|
22444
|
+
file: z146.string().optional().describe(
|
|
21103
22445
|
"(start) Output movie path on the TD machine, with a .mov or .mp4 extension. Absolute path recommended."
|
|
21104
22446
|
),
|
|
21105
|
-
fps:
|
|
21106
|
-
seconds:
|
|
22447
|
+
fps: z146.coerce.number().positive().default(30).describe("(start) Frames per second."),
|
|
22448
|
+
seconds: z146.coerce.number().positive().optional().describe(
|
|
21107
22449
|
"(start) If set, auto-stop after this many seconds (records a fixed-length loop); otherwise record until you call stop."
|
|
21108
22450
|
)
|
|
21109
22451
|
});
|
|
@@ -21211,8 +22553,8 @@ async function recordMovieImpl(ctx, args) {
|
|
|
21211
22553
|
}
|
|
21212
22554
|
|
|
21213
22555
|
// src/tools/layer3/reloadBridge.ts
|
|
21214
|
-
import { z as
|
|
21215
|
-
var reloadBridgeSchema =
|
|
22556
|
+
import { z as z147 } from "zod";
|
|
22557
|
+
var reloadBridgeSchema = z147.object({});
|
|
21216
22558
|
var RELOAD_SCRIPT = `
|
|
21217
22559
|
import json
|
|
21218
22560
|
from mcp import dev
|
|
@@ -21233,10 +22575,10 @@ async function reloadBridgeImpl(ctx) {
|
|
|
21233
22575
|
}
|
|
21234
22576
|
|
|
21235
22577
|
// src/tools/layer3/renderOutput.ts
|
|
21236
|
-
import { z as
|
|
21237
|
-
var renderOutputSchema =
|
|
21238
|
-
node_path:
|
|
21239
|
-
file:
|
|
22578
|
+
import { z as z148 } from "zod";
|
|
22579
|
+
var renderOutputSchema = z148.object({
|
|
22580
|
+
node_path: z148.string().describe("Path of the TOP to render to a file."),
|
|
22581
|
+
file: z148.string().describe(
|
|
21240
22582
|
"Output file path (written by TouchDesigner, so on the TD machine). Extension picks the format: .png/.jpg/.exr/.tiff. Use an absolute path."
|
|
21241
22583
|
)
|
|
21242
22584
|
});
|
|
@@ -21276,7 +22618,7 @@ async function renderOutputImpl(ctx, args) {
|
|
|
21276
22618
|
}
|
|
21277
22619
|
|
|
21278
22620
|
// src/tools/layer3/searchOperators.ts
|
|
21279
|
-
import { z as
|
|
22621
|
+
import { z as z149 } from "zod";
|
|
21280
22622
|
|
|
21281
22623
|
// src/knowledge/embeddings.ts
|
|
21282
22624
|
async function embedTexts(texts, config, timeoutMs = 2e4) {
|
|
@@ -21360,12 +22702,12 @@ function cosineSimilarity(a, b) {
|
|
|
21360
22702
|
}
|
|
21361
22703
|
|
|
21362
22704
|
// src/tools/layer3/searchOperators.ts
|
|
21363
|
-
var searchOperatorsSchema =
|
|
21364
|
-
query:
|
|
22705
|
+
var searchOperatorsSchema = z149.object({
|
|
22706
|
+
query: z149.string().min(1).describe(
|
|
21365
22707
|
"What you're looking for \u2014 words from a name, family, or description (e.g. 'blur edge', 'audio spectrum', 'instance geometry')."
|
|
21366
22708
|
),
|
|
21367
|
-
limit:
|
|
21368
|
-
semantic:
|
|
22709
|
+
limit: z149.coerce.number().int().positive().max(100).default(20).describe("Max results to return."),
|
|
22710
|
+
semantic: z149.boolean().default(false).describe(
|
|
21369
22711
|
"Opt-in: re-rank keyword candidates by embedding similarity via the configured LLM endpoint (TDMCP_LLM_BASE_URL / _MODEL, Ollama by default). Better for fuzzy/conceptual queries. Falls back to keyword ranking if the endpoint is unavailable \u2014 the default (false) needs nothing."
|
|
21370
22712
|
)
|
|
21371
22713
|
});
|
|
@@ -21401,44 +22743,49 @@ async function searchOperatorsImpl(ctx, args) {
|
|
|
21401
22743
|
}
|
|
21402
22744
|
|
|
21403
22745
|
// src/tools/layer3/serializeNetwork.ts
|
|
21404
|
-
import { z as
|
|
21405
|
-
var serializeNetworkSchema =
|
|
21406
|
-
path:
|
|
21407
|
-
max_nodes:
|
|
21408
|
-
include_custom_params:
|
|
22746
|
+
import { z as z150 } from "zod";
|
|
22747
|
+
var serializeNetworkSchema = z150.object({
|
|
22748
|
+
path: z150.string().describe("Root COMP whose children to serialize into a diffable spec."),
|
|
22749
|
+
max_nodes: z150.number().int().min(1).max(500).default(200).describe("Cap nodes serialized."),
|
|
22750
|
+
include_custom_params: z150.boolean().default(true).describe("Include custom-parameter definitions (best-effort).")
|
|
21409
22751
|
});
|
|
21410
|
-
var SerializedParamSchema =
|
|
21411
|
-
value:
|
|
21412
|
-
mode:
|
|
21413
|
-
expr:
|
|
22752
|
+
var SerializedParamSchema = z150.object({
|
|
22753
|
+
value: z150.unknown().optional().describe("Evaluated parameter value."),
|
|
22754
|
+
mode: z150.string().optional().describe("Parameter mode (CONSTANT / EXPRESSION / EXPORT / BIND), normalized."),
|
|
22755
|
+
expr: z150.string().optional().describe("Raw expression string when the mode is EXPRESSION.")
|
|
21414
22756
|
});
|
|
21415
|
-
var SerializedInputSchema =
|
|
21416
|
-
from:
|
|
21417
|
-
out_index:
|
|
21418
|
-
in_index:
|
|
22757
|
+
var SerializedInputSchema = z150.object({
|
|
22758
|
+
from: z150.string().describe("Source node NAME (within root) feeding this input."),
|
|
22759
|
+
out_index: z150.number().describe("Output connector index on the source node."),
|
|
22760
|
+
in_index: z150.number().describe("Input connector index on this node.")
|
|
21419
22761
|
});
|
|
21420
|
-
var SerializedCustomParSchema =
|
|
21421
|
-
name:
|
|
21422
|
-
page:
|
|
21423
|
-
style:
|
|
21424
|
-
default:
|
|
22762
|
+
var SerializedCustomParSchema = z150.object({
|
|
22763
|
+
name: z150.string().describe("Custom parameter name."),
|
|
22764
|
+
page: z150.string().optional().describe("Parameter page the knob lives on."),
|
|
22765
|
+
style: z150.string().optional().describe("Parameter style (Float, Int, Toggle, Menu, \u2026)."),
|
|
22766
|
+
default: z150.unknown().optional().describe("Default value, best-effort (UNVERIFIED shape).")
|
|
21425
22767
|
});
|
|
21426
|
-
var SerializedNodeSchema =
|
|
21427
|
-
name:
|
|
21428
|
-
type:
|
|
21429
|
-
params:
|
|
21430
|
-
inputs:
|
|
21431
|
-
x:
|
|
21432
|
-
y:
|
|
21433
|
-
|
|
22768
|
+
var SerializedNodeSchema = z150.object({
|
|
22769
|
+
name: z150.string().describe("Node name (unique within root)."),
|
|
22770
|
+
type: z150.string().describe('TD op type, e.g. "noiseTOP".'),
|
|
22771
|
+
params: z150.record(z150.string(), SerializedParamSchema).describe("Non-default / interesting params keyed by parameter name."),
|
|
22772
|
+
inputs: z150.array(SerializedInputSchema).describe("Input wires, by source node NAME."),
|
|
22773
|
+
x: z150.number().optional().describe("Node X position (cosmetic)."),
|
|
22774
|
+
y: z150.number().optional().describe("Node Y position (cosmetic)."),
|
|
22775
|
+
flags: z150.record(z150.string(), z150.boolean()).optional().describe(
|
|
22776
|
+
"Operator flags (bypass/render/display/lock/allowCooking) \u2014 inspection/diff metadata; rebuild_network does not restore these."
|
|
22777
|
+
),
|
|
22778
|
+
comment: z150.string().optional().describe("Node comment (cosmetic)."),
|
|
22779
|
+
color: z150.array(z150.number()).optional().describe("Node color RGB (cosmetic)."),
|
|
22780
|
+
custom_params: z150.array(SerializedCustomParSchema).optional().describe(
|
|
21434
22781
|
"Custom-parameter definitions so rebuild can recreate knobs (best-effort, UNVERIFIED)."
|
|
21435
22782
|
)
|
|
21436
22783
|
});
|
|
21437
|
-
var serializeNetworkOutputSchema =
|
|
21438
|
-
root:
|
|
21439
|
-
nodes:
|
|
21440
|
-
truncated:
|
|
21441
|
-
warnings:
|
|
22784
|
+
var serializeNetworkOutputSchema = z150.object({
|
|
22785
|
+
root: z150.string().describe("The serialized root path."),
|
|
22786
|
+
nodes: z150.array(SerializedNodeSchema).describe("Every serialized child node of the root."),
|
|
22787
|
+
truncated: z150.boolean().optional().describe("True when the child count exceeded max_nodes and the spec was capped."),
|
|
22788
|
+
warnings: z150.array(z150.string()).describe("Per-item problems collected without failing the read.")
|
|
21442
22789
|
});
|
|
21443
22790
|
var SERIALIZE_NETWORK_SCRIPT = `
|
|
21444
22791
|
import json, base64, traceback
|
|
@@ -21491,6 +22838,26 @@ try:
|
|
|
21491
22838
|
_node["y"] = _o.nodeY
|
|
21492
22839
|
except Exception:
|
|
21493
22840
|
pass
|
|
22841
|
+
# Flags (cosmetic + behavioral) \u2014 inspection/diff metadata; rebuild_network does not restore these.
|
|
22842
|
+
_flags = {}
|
|
22843
|
+
for _fa in ("bypass", "render", "display", "lock", "allowCooking"):
|
|
22844
|
+
try:
|
|
22845
|
+
_fv = getattr(_o, _fa)
|
|
22846
|
+
if isinstance(_fv, bool):
|
|
22847
|
+
_flags[_fa] = _fv
|
|
22848
|
+
except Exception:
|
|
22849
|
+
pass
|
|
22850
|
+
if _flags:
|
|
22851
|
+
_node["flags"] = _flags
|
|
22852
|
+
try:
|
|
22853
|
+
if _o.comment:
|
|
22854
|
+
_node["comment"] = _o.comment
|
|
22855
|
+
except Exception:
|
|
22856
|
+
pass
|
|
22857
|
+
try:
|
|
22858
|
+
_node["color"] = list(_o.color)
|
|
22859
|
+
except Exception:
|
|
22860
|
+
pass
|
|
21494
22861
|
# Parameters: value + normalized mode + raw expression, defensively per attribute.
|
|
21495
22862
|
try:
|
|
21496
22863
|
_pars = _o.pars()
|
|
@@ -21628,15 +22995,15 @@ async function serializeNetworkImpl(ctx, args) {
|
|
|
21628
22995
|
}
|
|
21629
22996
|
|
|
21630
22997
|
// src/tools/layer3/setDatContent.ts
|
|
21631
|
-
import { z as
|
|
21632
|
-
var setDatContentSchema =
|
|
21633
|
-
dat_path:
|
|
22998
|
+
import { z as z151 } from "zod";
|
|
22999
|
+
var setDatContentSchema = z151.object({
|
|
23000
|
+
dat_path: z151.string().describe(
|
|
21634
23001
|
"Absolute path to the Text or Table DAT whose content will be fully replaced (e.g. '/project1/mytext1')."
|
|
21635
23002
|
),
|
|
21636
|
-
text:
|
|
23003
|
+
text: z151.string().describe(
|
|
21637
23004
|
"The full new contents of the DAT. Every existing character will be discarded; this string becomes the entire `.text` value."
|
|
21638
23005
|
),
|
|
21639
|
-
confirm_wipe:
|
|
23006
|
+
confirm_wipe: z151.boolean().default(false).describe(
|
|
21640
23007
|
"Set true to allow writing empty or whitespace-only text, which clears the DAT. When false (default), the tool refuses to write blank content to prevent silent data loss."
|
|
21641
23008
|
)
|
|
21642
23009
|
});
|
|
@@ -21670,6 +23037,18 @@ async function setDatContentImpl(ctx, args) {
|
|
|
21670
23037
|
}
|
|
21671
23038
|
return guardTd(
|
|
21672
23039
|
async () => {
|
|
23040
|
+
try {
|
|
23041
|
+
const w = await ctx.client.putDatText(args.dat_path, args.text);
|
|
23042
|
+
return {
|
|
23043
|
+
dat: args.dat_path,
|
|
23044
|
+
old_length: w.old_length,
|
|
23045
|
+
new_length: w.new_length,
|
|
23046
|
+
wiped: args.text.trim() === "",
|
|
23047
|
+
warnings: []
|
|
23048
|
+
};
|
|
23049
|
+
} catch (err) {
|
|
23050
|
+
if (!isMissingEndpoint(err)) throw err;
|
|
23051
|
+
}
|
|
21673
23052
|
const script = buildSetDatContentScript({ dat: args.dat_path, text: args.text });
|
|
21674
23053
|
const exec = await ctx.client.executePythonScript(script, true);
|
|
21675
23054
|
return parsePythonReport(exec.stdout);
|
|
@@ -21686,19 +23065,19 @@ async function setDatContentImpl(ctx, args) {
|
|
|
21686
23065
|
}
|
|
21687
23066
|
|
|
21688
23067
|
// src/tools/layer3/setParameterExpression.ts
|
|
21689
|
-
import { z as
|
|
21690
|
-
var setParameterExpressionSchema =
|
|
21691
|
-
path:
|
|
21692
|
-
assignments:
|
|
21693
|
-
|
|
21694
|
-
param:
|
|
21695
|
-
mode:
|
|
23068
|
+
import { z as z152 } from "zod";
|
|
23069
|
+
var setParameterExpressionSchema = z152.object({
|
|
23070
|
+
path: z152.string().describe("Full path of the node whose parameters to set."),
|
|
23071
|
+
assignments: z152.array(
|
|
23072
|
+
z152.object({
|
|
23073
|
+
param: z152.string().describe("Parameter name (case-sensitive), e.g. 'tx'."),
|
|
23074
|
+
mode: z152.enum(["expression", "bind", "constant"]).default("expression").describe(
|
|
21696
23075
|
"expression: set par.expr; bind: set par.bindExpr; constant: set par.val from `value`."
|
|
21697
23076
|
),
|
|
21698
|
-
expr:
|
|
23077
|
+
expr: z152.string().optional().describe(
|
|
21699
23078
|
`The expression or bind string (required for mode expression/bind), e.g. 'me.time.seconds' or 'op("audio")["level"]'.`
|
|
21700
23079
|
),
|
|
21701
|
-
value:
|
|
23080
|
+
value: z152.union([z152.number(), z152.string(), z152.boolean()]).optional().describe("Constant value (for mode 'constant').")
|
|
21702
23081
|
})
|
|
21703
23082
|
).min(1).describe("One or more parameter assignments.")
|
|
21704
23083
|
});
|
|
@@ -21738,9 +23117,9 @@ try:
|
|
|
21738
23117
|
continue
|
|
21739
23118
|
_par.expr = _e
|
|
21740
23119
|
try:
|
|
21741
|
-
_par.mode =
|
|
23120
|
+
_par.mode = type(_par.mode).EXPRESSION
|
|
21742
23121
|
except Exception:
|
|
21743
|
-
report["warnings"].append("
|
|
23122
|
+
report["warnings"].append("param '" + str(_a["param"]) + "': could not flip to Expression mode")
|
|
21744
23123
|
elif _m == "bind":
|
|
21745
23124
|
_e = _a.get("expr")
|
|
21746
23125
|
if not _e:
|
|
@@ -21748,9 +23127,9 @@ try:
|
|
|
21748
23127
|
continue
|
|
21749
23128
|
_par.bindExpr = _e
|
|
21750
23129
|
try:
|
|
21751
|
-
_par.mode =
|
|
23130
|
+
_par.mode = type(_par.mode).BIND
|
|
21752
23131
|
except Exception:
|
|
21753
|
-
report["warnings"].append("
|
|
23132
|
+
report["warnings"].append("param '" + str(_a["param"]) + "': could not flip to Bind mode")
|
|
21754
23133
|
else:
|
|
21755
23134
|
_v = _a.get("value")
|
|
21756
23135
|
if _v is None:
|
|
@@ -21758,7 +23137,7 @@ try:
|
|
|
21758
23137
|
continue
|
|
21759
23138
|
_par.val = _v
|
|
21760
23139
|
try:
|
|
21761
|
-
_par.mode =
|
|
23140
|
+
_par.mode = type(_par.mode).CONSTANT
|
|
21762
23141
|
except Exception:
|
|
21763
23142
|
pass
|
|
21764
23143
|
report["applied"].append({
|
|
@@ -21779,6 +23158,46 @@ function buildSetExprScript(payload) {
|
|
|
21779
23158
|
async function setParameterExpressionImpl(ctx, args) {
|
|
21780
23159
|
return guardTd(
|
|
21781
23160
|
async () => {
|
|
23161
|
+
const applied = [];
|
|
23162
|
+
const warnings = [];
|
|
23163
|
+
let endpointUsable = true;
|
|
23164
|
+
let endpointProven = false;
|
|
23165
|
+
for (let i = 0; i < args.assignments.length; i++) {
|
|
23166
|
+
const a = args.assignments[i];
|
|
23167
|
+
if (!a) continue;
|
|
23168
|
+
if ((a.mode === "expression" || a.mode === "bind") && !a.expr) {
|
|
23169
|
+
warnings.push(`param '${a.param}': expr required for mode '${a.mode}'`);
|
|
23170
|
+
continue;
|
|
23171
|
+
}
|
|
23172
|
+
if (a.mode === "constant" && a.value === void 0) {
|
|
23173
|
+
warnings.push(`param '${a.param}': value required for mode 'constant'`);
|
|
23174
|
+
continue;
|
|
23175
|
+
}
|
|
23176
|
+
try {
|
|
23177
|
+
const r2 = await ctx.client.setParameterMode(args.path, a.param, a.mode, a.expr, a.value);
|
|
23178
|
+
endpointProven = true;
|
|
23179
|
+
applied.push({
|
|
23180
|
+
param: r2.param,
|
|
23181
|
+
mode: a.mode,
|
|
23182
|
+
readback_mode: r2.readback_mode,
|
|
23183
|
+
readback_expr: r2.readback_expr
|
|
23184
|
+
});
|
|
23185
|
+
} catch (err) {
|
|
23186
|
+
if (!endpointProven && isMissingEndpoint(err)) {
|
|
23187
|
+
endpointUsable = false;
|
|
23188
|
+
break;
|
|
23189
|
+
}
|
|
23190
|
+
if (err instanceof TdApiError) {
|
|
23191
|
+
endpointProven = true;
|
|
23192
|
+
warnings.push(`param '${a.param}': ${err.message}`);
|
|
23193
|
+
continue;
|
|
23194
|
+
}
|
|
23195
|
+
throw err;
|
|
23196
|
+
}
|
|
23197
|
+
}
|
|
23198
|
+
if (endpointUsable) {
|
|
23199
|
+
return { path: args.path, applied, warnings };
|
|
23200
|
+
}
|
|
21782
23201
|
const script = buildSetExprScript({
|
|
21783
23202
|
path: args.path,
|
|
21784
23203
|
assignments: args.assignments
|
|
@@ -21802,53 +23221,53 @@ async function setParameterExpressionImpl(ctx, args) {
|
|
|
21802
23221
|
}
|
|
21803
23222
|
|
|
21804
23223
|
// src/tools/layer3/snapshotTdGraph.ts
|
|
21805
|
-
import { z as
|
|
23224
|
+
import { z as z153 } from "zod";
|
|
21806
23225
|
var MAX_PARAM_NODES = 60;
|
|
21807
|
-
var snapshotTdGraphSchema =
|
|
21808
|
-
path:
|
|
21809
|
-
include_params:
|
|
21810
|
-
compact:
|
|
23226
|
+
var snapshotTdGraphSchema = z153.object({
|
|
23227
|
+
path: z153.string().default("/project1").describe("Network root to snapshot."),
|
|
23228
|
+
include_params: z153.boolean().default(false).describe("Also fetch each node's parameters (one request per node; capped for large graphs)."),
|
|
23229
|
+
compact: z153.boolean().default(false).describe(
|
|
21811
23230
|
"Token-cheap whole-COMP read: hoist each operator type's most-common parameter values into a shared `typeDefaults` map and store only each node's *deltas* from them (Embody-style read_tdn). Implies fetching parameters. Use for feeding a large network to an agent without paying for repeated identical values."
|
|
21812
23231
|
),
|
|
21813
|
-
include_parameter_modes:
|
|
23232
|
+
include_parameter_modes: z153.boolean().default(false).describe(
|
|
21814
23233
|
"Also preserve TouchDesigner parameter modes/expressions/binds where available. Compact mode implies this so reactive expressions are not flattened to their current value."
|
|
21815
23234
|
)
|
|
21816
23235
|
});
|
|
21817
|
-
var snapshotTdGraphOutputSchema =
|
|
21818
|
-
path:
|
|
21819
|
-
nodeCount:
|
|
21820
|
-
connectionCount:
|
|
21821
|
-
issues:
|
|
21822
|
-
params_truncated:
|
|
23236
|
+
var snapshotTdGraphOutputSchema = z153.object({
|
|
23237
|
+
path: z153.string().describe("The network root that was snapshotted, echoing the request."),
|
|
23238
|
+
nodeCount: z153.number().describe("Total number of nodes captured."),
|
|
23239
|
+
connectionCount: z153.number().describe("Total number of connections captured."),
|
|
23240
|
+
issues: z153.array(z153.string()).describe("Plain-language structural problems detected in the graph."),
|
|
23241
|
+
params_truncated: z153.boolean().describe(
|
|
21823
23242
|
"True if params were requested (`include_params` or `compact`) but the graph exceeded the per-node fetch cap."
|
|
21824
23243
|
),
|
|
21825
|
-
parameter_modes_truncated:
|
|
23244
|
+
parameter_modes_truncated: z153.boolean().optional().describe(
|
|
21826
23245
|
"True if parameter modes were requested (`include_parameter_modes` or `compact`) but the graph exceeded the per-node fetch cap."
|
|
21827
23246
|
),
|
|
21828
|
-
compact:
|
|
23247
|
+
compact: z153.boolean().optional().describe(
|
|
21829
23248
|
"True when compact mode hoisted per-type default parameters and delta-encoded nodes."
|
|
21830
23249
|
),
|
|
21831
|
-
typeDefaults:
|
|
23250
|
+
typeDefaults: z153.record(z153.string(), z153.record(z153.string(), z153.unknown())).optional().describe(
|
|
21832
23251
|
"Compact mode only: each operator type's hoisted default parameter values; nodes store only their deltas from these."
|
|
21833
23252
|
),
|
|
21834
|
-
nodes:
|
|
21835
|
-
|
|
21836
|
-
path:
|
|
21837
|
-
type:
|
|
21838
|
-
name:
|
|
21839
|
-
parameters:
|
|
23253
|
+
nodes: z153.array(
|
|
23254
|
+
z153.object({
|
|
23255
|
+
path: z153.string().describe("Full path of the node."),
|
|
23256
|
+
type: z153.string().describe("Operator type of the node."),
|
|
23257
|
+
name: z153.string().describe("Short name of the node."),
|
|
23258
|
+
parameters: z153.record(z153.string(), z153.unknown()).optional().describe(
|
|
21840
23259
|
"The node's parameters as key\u2192value; present when `include_params` or `compact` is set (compact implies fetching). In compact mode, only the deltas from the type default."
|
|
21841
23260
|
),
|
|
21842
|
-
params_unfetched:
|
|
23261
|
+
params_unfetched: z153.boolean().optional().describe(
|
|
21843
23262
|
"True when parameters were requested (`include_params` or `compact`) but not fetched for this node (past the per-node cap or a failed read), so a missing `parameters` field isn't mistaken for matching the type default."
|
|
21844
23263
|
),
|
|
21845
|
-
parameter_modes:
|
|
23264
|
+
parameter_modes: z153.record(z153.string(), parameterModeInfoSchema).optional().describe(
|
|
21846
23265
|
"Parameter state keyed by par name. Present when `include_parameter_modes` is true, and in compact mode only for expression/bind/export-like non-constant state."
|
|
21847
23266
|
),
|
|
21848
|
-
parameter_modes_unfetched:
|
|
23267
|
+
parameter_modes_unfetched: z153.boolean().optional().describe("True when parameter modes were requested but not fetched for this node.")
|
|
21849
23268
|
})
|
|
21850
23269
|
).describe("Every captured node, optionally with its parameters."),
|
|
21851
|
-
connections:
|
|
23270
|
+
connections: z153.array(ConnectionSchema).describe("Every wire as {source_path, target_path, \u2026}, suitable for diffing.")
|
|
21852
23271
|
});
|
|
21853
23272
|
function computeTypeDefaults(nodes) {
|
|
21854
23273
|
const byType = /* @__PURE__ */ new Map();
|
|
@@ -22028,28 +23447,28 @@ async function snapshotTdGraphImpl(ctx, args) {
|
|
|
22028
23447
|
}
|
|
22029
23448
|
|
|
22030
23449
|
// src/tools/layer3/summarizeTdErrors.ts
|
|
22031
|
-
import { z as
|
|
22032
|
-
var summarizeTdErrorsSchema =
|
|
22033
|
-
path:
|
|
22034
|
-
group_by:
|
|
23450
|
+
import { z as z154 } from "zod";
|
|
23451
|
+
var summarizeTdErrorsSchema = z154.object({
|
|
23452
|
+
path: z154.string().default("/project1").describe("Network root to collect errors under."),
|
|
23453
|
+
group_by: z154.enum(["message", "type", "parent"]).default("message").describe(
|
|
22035
23454
|
"How to cluster errors: by exact message, by error type, or by parent container (to find a common upstream cause)."
|
|
22036
23455
|
)
|
|
22037
23456
|
});
|
|
22038
|
-
var summarizeTdErrorsOutputSchema =
|
|
22039
|
-
path:
|
|
22040
|
-
total:
|
|
22041
|
-
group_by:
|
|
22042
|
-
groups:
|
|
22043
|
-
|
|
22044
|
-
key:
|
|
22045
|
-
count:
|
|
22046
|
-
sample:
|
|
22047
|
-
path:
|
|
22048
|
-
message:
|
|
23457
|
+
var summarizeTdErrorsOutputSchema = z154.object({
|
|
23458
|
+
path: z154.string().describe("The network root errors were collected under, echoing the request."),
|
|
23459
|
+
total: z154.number().describe("Total number of errors found across the network (0 means clean)."),
|
|
23460
|
+
group_by: z154.enum(["message", "type", "parent"]).describe("How the errors were clustered, echoing the request."),
|
|
23461
|
+
groups: z154.array(
|
|
23462
|
+
z154.object({
|
|
23463
|
+
key: z154.string().describe("The shared message, type, or parent path for this cluster."),
|
|
23464
|
+
count: z154.number().describe("How many errors fall into this cluster."),
|
|
23465
|
+
sample: z154.object({
|
|
23466
|
+
path: z154.string().describe("Path of one representative node in this cluster."),
|
|
23467
|
+
message: z154.string().describe("That node's error message, as a concrete example.")
|
|
22049
23468
|
}).describe("One representative error from the cluster.")
|
|
22050
23469
|
})
|
|
22051
23470
|
).describe("Error clusters, largest first; fixing a big cluster's cause clears it at once."),
|
|
22052
|
-
suggestions:
|
|
23471
|
+
suggestions: z154.array(z154.string()).describe("Plain-language next steps, e.g. the common cause and which nodes to check first.")
|
|
22053
23472
|
});
|
|
22054
23473
|
async function summarizeTdErrorsImpl(ctx, args) {
|
|
22055
23474
|
return guardTd(
|
|
@@ -22096,10 +23515,10 @@ async function summarizeTdErrorsImpl(ctx, args) {
|
|
|
22096
23515
|
}
|
|
22097
23516
|
|
|
22098
23517
|
// src/tools/layer3/updateTdNodeParameters.ts
|
|
22099
|
-
import { z as
|
|
22100
|
-
var updateTdNodeParametersSchema =
|
|
22101
|
-
path:
|
|
22102
|
-
parameters:
|
|
23518
|
+
import { z as z155 } from "zod";
|
|
23519
|
+
var updateTdNodeParametersSchema = z155.object({
|
|
23520
|
+
path: z155.string().describe("Full path of the node whose parameters to update."),
|
|
23521
|
+
parameters: z155.record(z155.string(), z155.unknown()).describe("Parameter overrides as key\u2192value pairs, e.g. { period: 4, amplitude: 0.5 }.")
|
|
22103
23522
|
});
|
|
22104
23523
|
async function updateTdNodeParametersImpl(ctx, args) {
|
|
22105
23524
|
return guardTd(
|
|
@@ -22111,15 +23530,15 @@ async function updateTdNodeParametersImpl(ctx, args) {
|
|
|
22111
23530
|
}
|
|
22112
23531
|
|
|
22113
23532
|
// src/tools/layer3/writeAgentGuide.ts
|
|
22114
|
-
import { z as
|
|
22115
|
-
var writeAgentGuideSchema =
|
|
22116
|
-
filename:
|
|
23533
|
+
import { z as z156 } from "zod";
|
|
23534
|
+
var writeAgentGuideSchema = z156.object({
|
|
23535
|
+
filename: z156.string().default("CLAUDE.md").describe(
|
|
22117
23536
|
"Name of the guide file to emit, e.g. CLAUDE.md or AGENTS.md. Defaults to CLAUDE.md."
|
|
22118
23537
|
),
|
|
22119
|
-
output_dir:
|
|
23538
|
+
output_dir: z156.string().optional().describe(
|
|
22120
23539
|
"Absolute path on the machine running TouchDesigner where the guide file should be written. If omitted the guide is returned in the result but not written to disk."
|
|
22121
23540
|
),
|
|
22122
|
-
path:
|
|
23541
|
+
path: z156.string().default("/project1").describe(
|
|
22123
23542
|
"TouchDesigner project/COMP path to summarise in the guide header, e.g. /project1. A one-line dynamic summary (node count + top families) is prepended to the static body."
|
|
22124
23543
|
)
|
|
22125
23544
|
});
|
|
@@ -22292,11 +23711,11 @@ ${lookupTable}
|
|
|
22292
23711
|
*End of guide \u2014 re-run ${BT}write_agent_guide${BT} any time to refresh the project summary.*
|
|
22293
23712
|
`;
|
|
22294
23713
|
}
|
|
22295
|
-
var writeAgentGuideOutputSchema =
|
|
22296
|
-
filename:
|
|
22297
|
-
written:
|
|
22298
|
-
path:
|
|
22299
|
-
guide:
|
|
23714
|
+
var writeAgentGuideOutputSchema = z156.object({
|
|
23715
|
+
filename: z156.string().describe("Name of the guide file."),
|
|
23716
|
+
written: z156.boolean().describe("Whether the file was written to disk."),
|
|
23717
|
+
path: z156.string().optional().describe("Absolute path the file was written to (if written)."),
|
|
23718
|
+
guide: z156.string().describe("The full guide markdown text.")
|
|
22300
23719
|
});
|
|
22301
23720
|
async function tryBridgeReport(ctx, payload) {
|
|
22302
23721
|
try {
|
|
@@ -22364,7 +23783,7 @@ import {
|
|
|
22364
23783
|
writeFileSync as writeFileSync3
|
|
22365
23784
|
} from "fs";
|
|
22366
23785
|
import { basename as basename2, dirname as dirname4, extname, join as join7, parse, resolve as resolve4, sep as sep2 } from "path";
|
|
22367
|
-
import { z as
|
|
23786
|
+
import { z as z157 } from "zod";
|
|
22368
23787
|
|
|
22369
23788
|
// src/packages/archive.ts
|
|
22370
23789
|
import { execFileSync } from "child_process";
|
|
@@ -22468,16 +23887,16 @@ function listZipEntryInfo(zipPath) {
|
|
|
22468
23887
|
}
|
|
22469
23888
|
|
|
22470
23889
|
// src/tools/library/index.ts
|
|
22471
|
-
var ComponentManifestSchema =
|
|
22472
|
-
id:
|
|
22473
|
-
name:
|
|
22474
|
-
version:
|
|
22475
|
-
description:
|
|
22476
|
-
type:
|
|
22477
|
-
tox:
|
|
22478
|
-
assets:
|
|
22479
|
-
docs:
|
|
22480
|
-
recipes:
|
|
23890
|
+
var ComponentManifestSchema = z157.object({
|
|
23891
|
+
id: z157.string().optional(),
|
|
23892
|
+
name: z157.string().optional(),
|
|
23893
|
+
version: z157.string().optional(),
|
|
23894
|
+
description: z157.string().optional(),
|
|
23895
|
+
type: z157.string().optional(),
|
|
23896
|
+
tox: z157.string().optional(),
|
|
23897
|
+
assets: z157.array(z157.string()).default([]),
|
|
23898
|
+
docs: z157.array(z157.string()).default([]),
|
|
23899
|
+
recipes: z157.array(z157.string()).default([])
|
|
22481
23900
|
}).passthrough();
|
|
22482
23901
|
function manifestCandidates(path2) {
|
|
22483
23902
|
const full = resolve4(path2);
|
|
@@ -22603,12 +24022,12 @@ function extractZip(zipPath, destDir, exec = execFileSync2, listEntries = listZi
|
|
|
22603
24022
|
const { command, args } = zipExtractCommand(zipPath, destDir);
|
|
22604
24023
|
exec(command, args, { stdio: "pipe" });
|
|
22605
24024
|
}
|
|
22606
|
-
var browseLibrarySchema =
|
|
22607
|
-
query:
|
|
22608
|
-
tags:
|
|
22609
|
-
package_dir:
|
|
22610
|
-
include_recipes:
|
|
22611
|
-
include_packages:
|
|
24025
|
+
var browseLibrarySchema = z157.object({
|
|
24026
|
+
query: z157.string().optional(),
|
|
24027
|
+
tags: z157.array(z157.string()).default([]),
|
|
24028
|
+
package_dir: z157.string().optional(),
|
|
24029
|
+
include_recipes: z157.boolean().default(true),
|
|
24030
|
+
include_packages: z157.boolean().default(true)
|
|
22612
24031
|
});
|
|
22613
24032
|
async function browseLibraryImpl(ctx, args) {
|
|
22614
24033
|
const query = args.query?.toLowerCase();
|
|
@@ -22638,7 +24057,7 @@ async function browseLibraryImpl(ctx, args) {
|
|
|
22638
24057
|
packages
|
|
22639
24058
|
});
|
|
22640
24059
|
}
|
|
22641
|
-
var inspectComponentManifestSchema =
|
|
24060
|
+
var inspectComponentManifestSchema = z157.object({ path: z157.string() });
|
|
22642
24061
|
async function inspectComponentManifestImpl(_ctx, args) {
|
|
22643
24062
|
try {
|
|
22644
24063
|
const { path: path2, manifest } = readManifest(args.path);
|
|
@@ -22655,11 +24074,11 @@ async function inspectComponentManifestImpl(_ctx, args) {
|
|
|
22655
24074
|
return errorResult(err instanceof Error ? err.message : String(err));
|
|
22656
24075
|
}
|
|
22657
24076
|
}
|
|
22658
|
-
var makePortableToxSchema =
|
|
22659
|
-
comp_path:
|
|
22660
|
-
out_dir:
|
|
22661
|
-
name:
|
|
22662
|
-
docs:
|
|
24077
|
+
var makePortableToxSchema = z157.object({
|
|
24078
|
+
comp_path: z157.string(),
|
|
24079
|
+
out_dir: z157.string(),
|
|
24080
|
+
name: z157.string().optional(),
|
|
24081
|
+
docs: z157.array(z157.string()).default([])
|
|
22663
24082
|
});
|
|
22664
24083
|
var SAVE_TOX_SCRIPT = `
|
|
22665
24084
|
import json, base64, os, traceback
|
|
@@ -22718,10 +24137,10 @@ async function makePortableToxImpl(ctx, args) {
|
|
|
22718
24137
|
}
|
|
22719
24138
|
);
|
|
22720
24139
|
}
|
|
22721
|
-
var exportRecipeBundleSchema =
|
|
22722
|
-
out_file:
|
|
22723
|
-
recipe_ids:
|
|
22724
|
-
include_all:
|
|
24140
|
+
var exportRecipeBundleSchema = z157.object({
|
|
24141
|
+
out_file: z157.string(),
|
|
24142
|
+
recipe_ids: z157.array(z157.string()).default([]),
|
|
24143
|
+
include_all: z157.boolean().default(false)
|
|
22725
24144
|
});
|
|
22726
24145
|
async function exportRecipeBundleImpl(ctx, args) {
|
|
22727
24146
|
try {
|
|
@@ -22740,15 +24159,15 @@ async function exportRecipeBundleImpl(ctx, args) {
|
|
|
22740
24159
|
return errorResult(err instanceof Error ? err.message : String(err));
|
|
22741
24160
|
}
|
|
22742
24161
|
}
|
|
22743
|
-
var importRecipeBundleSchema =
|
|
22744
|
-
bundle_file:
|
|
22745
|
-
out_dir:
|
|
22746
|
-
overwrite:
|
|
24162
|
+
var importRecipeBundleSchema = z157.object({
|
|
24163
|
+
bundle_file: z157.string(),
|
|
24164
|
+
out_dir: z157.string(),
|
|
24165
|
+
overwrite: z157.boolean().default(false)
|
|
22747
24166
|
});
|
|
22748
24167
|
async function importRecipeBundleImpl(_ctx, args) {
|
|
22749
24168
|
try {
|
|
22750
24169
|
const raw = JSON.parse(readFileSync6(args.bundle_file, "utf8"));
|
|
22751
|
-
const recipes =
|
|
24170
|
+
const recipes = z157.array(RecipeSchema).parse(raw.recipes ?? []);
|
|
22752
24171
|
const targets = recipes.map((recipe) => ({
|
|
22753
24172
|
recipe,
|
|
22754
24173
|
out: join7(args.out_dir, recipeFileName(recipe))
|
|
@@ -22780,9 +24199,9 @@ async function importRecipeBundleImpl(_ctx, args) {
|
|
|
22780
24199
|
return errorResult(err instanceof Error ? err.message : String(err));
|
|
22781
24200
|
}
|
|
22782
24201
|
}
|
|
22783
|
-
var validateLibraryAssetSchema =
|
|
22784
|
-
path:
|
|
22785
|
-
manifest_path:
|
|
24202
|
+
var validateLibraryAssetSchema = z157.object({
|
|
24203
|
+
path: z157.string(),
|
|
24204
|
+
manifest_path: z157.string().optional()
|
|
22786
24205
|
});
|
|
22787
24206
|
async function validateLibraryAssetImpl(_ctx, args) {
|
|
22788
24207
|
const issues = [];
|
|
@@ -22821,11 +24240,11 @@ async function validateLibraryAssetImpl(_ctx, args) {
|
|
|
22821
24240
|
}
|
|
22822
24241
|
);
|
|
22823
24242
|
}
|
|
22824
|
-
var scaffoldRecipeTemplateSchema =
|
|
22825
|
-
out_file:
|
|
22826
|
-
id:
|
|
22827
|
-
name:
|
|
22828
|
-
overwrite:
|
|
24243
|
+
var scaffoldRecipeTemplateSchema = z157.object({
|
|
24244
|
+
out_file: z157.string(),
|
|
24245
|
+
id: z157.string(),
|
|
24246
|
+
name: z157.string(),
|
|
24247
|
+
overwrite: z157.boolean().default(false)
|
|
22829
24248
|
});
|
|
22830
24249
|
async function scaffoldRecipeTemplateImpl(_ctx, args) {
|
|
22831
24250
|
try {
|
|
@@ -22858,10 +24277,10 @@ async function scaffoldRecipeTemplateImpl(_ctx, args) {
|
|
|
22858
24277
|
return errorResult(err instanceof Error ? err.message : String(err));
|
|
22859
24278
|
}
|
|
22860
24279
|
}
|
|
22861
|
-
var attachDocsAsAssetsSchema =
|
|
22862
|
-
manifest_path:
|
|
22863
|
-
docs:
|
|
22864
|
-
asset_dir:
|
|
24280
|
+
var attachDocsAsAssetsSchema = z157.object({
|
|
24281
|
+
manifest_path: z157.string(),
|
|
24282
|
+
docs: z157.array(z157.string()).min(1),
|
|
24283
|
+
asset_dir: z157.string().default("docs")
|
|
22865
24284
|
});
|
|
22866
24285
|
async function attachDocsAsAssetsImpl(_ctx, args) {
|
|
22867
24286
|
try {
|
|
@@ -22887,9 +24306,9 @@ async function attachDocsAsAssetsImpl(_ctx, args) {
|
|
|
22887
24306
|
return errorResult(err instanceof Error ? err.message : String(err));
|
|
22888
24307
|
}
|
|
22889
24308
|
}
|
|
22890
|
-
var localMarketplaceIndexSchema =
|
|
22891
|
-
package_dir:
|
|
22892
|
-
out_file:
|
|
24309
|
+
var localMarketplaceIndexSchema = z157.object({
|
|
24310
|
+
package_dir: z157.string(),
|
|
24311
|
+
out_file: z157.string().optional()
|
|
22893
24312
|
});
|
|
22894
24313
|
async function localMarketplaceIndexImpl(_ctx, args) {
|
|
22895
24314
|
try {
|
|
@@ -22923,9 +24342,9 @@ async function localMarketplaceIndexImpl(_ctx, args) {
|
|
|
22923
24342
|
return errorResult(err instanceof Error ? err.message : String(err));
|
|
22924
24343
|
}
|
|
22925
24344
|
}
|
|
22926
|
-
var componentLinkHealthSchema =
|
|
22927
|
-
paths:
|
|
22928
|
-
parent_path:
|
|
24345
|
+
var componentLinkHealthSchema = z157.object({
|
|
24346
|
+
paths: z157.array(z157.string()).default([]),
|
|
24347
|
+
parent_path: z157.string().default("/project1")
|
|
22929
24348
|
});
|
|
22930
24349
|
var LINK_HEALTH_SCRIPT = `
|
|
22931
24350
|
import json, base64, os, traceback
|
|
@@ -22979,10 +24398,10 @@ async function componentLinkHealthImpl(ctx, args) {
|
|
|
22979
24398
|
}
|
|
22980
24399
|
);
|
|
22981
24400
|
}
|
|
22982
|
-
var refreshAssetPreviewsSchema =
|
|
22983
|
-
targets:
|
|
22984
|
-
width:
|
|
22985
|
-
height:
|
|
24401
|
+
var refreshAssetPreviewsSchema = z157.object({
|
|
24402
|
+
targets: z157.array(z157.object({ node_path: z157.string(), file_path: z157.string() })).min(1),
|
|
24403
|
+
width: z157.coerce.number().int().positive().default(640),
|
|
24404
|
+
height: z157.coerce.number().int().positive().default(360)
|
|
22986
24405
|
});
|
|
22987
24406
|
async function refreshAssetPreviewsImpl(ctx, args) {
|
|
22988
24407
|
const written = [];
|
|
@@ -23010,10 +24429,10 @@ async function refreshAssetPreviewsImpl(ctx, args) {
|
|
|
23010
24429
|
if (written.length === 0) return errorResult(summary, report);
|
|
23011
24430
|
return jsonResult(summary, report);
|
|
23012
24431
|
}
|
|
23013
|
-
var installLibraryPackageSchema =
|
|
23014
|
-
source:
|
|
23015
|
-
dest_dir:
|
|
23016
|
-
overwrite:
|
|
24432
|
+
var installLibraryPackageSchema = z157.object({
|
|
24433
|
+
source: z157.string().describe("Local package folder, .zip, .tox, or manifest file."),
|
|
24434
|
+
dest_dir: z157.string(),
|
|
24435
|
+
overwrite: z157.boolean().default(false)
|
|
23017
24436
|
});
|
|
23018
24437
|
async function installLibraryPackageImpl(_ctx, args) {
|
|
23019
24438
|
try {
|
|
@@ -23042,6 +24461,192 @@ async function installLibraryPackageImpl(_ctx, args) {
|
|
|
23042
24461
|
}
|
|
23043
24462
|
}
|
|
23044
24463
|
|
|
24464
|
+
// src/tools/vault/generateLibraryIndex.ts
|
|
24465
|
+
import { basename as basename3, extname as extname2 } from "path";
|
|
24466
|
+
import { z as z158 } from "zod";
|
|
24467
|
+
|
|
24468
|
+
// src/tools/vault/shared.ts
|
|
24469
|
+
var NO_VAULT = "No Obsidian vault is configured. Set TDMCP_VAULT_PATH to your vault folder (e.g. ~/Documents/MyVault) and restart the server, then try again.";
|
|
24470
|
+
function requireVault(ctx) {
|
|
24471
|
+
if (!ctx.vault) return { error: errorResult(NO_VAULT) };
|
|
24472
|
+
return { vault: ctx.vault };
|
|
24473
|
+
}
|
|
24474
|
+
function readNoteSafe(vault, relPath) {
|
|
24475
|
+
try {
|
|
24476
|
+
return vault.readNote(relPath);
|
|
24477
|
+
} catch (err) {
|
|
24478
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
24479
|
+
return {
|
|
24480
|
+
error: errorResult(
|
|
24481
|
+
`Could not read vault note "${relPath}": ${reason}. Check the file is readable and its YAML frontmatter is valid.`
|
|
24482
|
+
)
|
|
24483
|
+
};
|
|
24484
|
+
}
|
|
24485
|
+
}
|
|
24486
|
+
|
|
24487
|
+
// src/tools/vault/generateLibraryIndex.ts
|
|
24488
|
+
var generateLibraryIndexSchema = z158.object({
|
|
24489
|
+
kinds: z158.array(z158.enum(["recipes", "shaders", "presets", "components", "setlists", "all"])).default(["all"]).describe("Which library categories to include. 'all' = every category."),
|
|
24490
|
+
output: z158.string().default("Library Index.md").describe("Vault-relative path of the contact-sheet note to write."),
|
|
24491
|
+
include_thumbnails: z158.boolean().default(true).describe("Embed each asset's <stem>.png sibling when present; false = text-only."),
|
|
24492
|
+
columns: z158.coerce.number().int().min(1).max(6).default(3).describe("Cards per row in the contact-sheet grid."),
|
|
24493
|
+
query: z158.string().optional().describe("Case-insensitive substring filter on title/tags."),
|
|
24494
|
+
overwrite: z158.boolean().default(true).describe("When false, refuse to overwrite an existing index note.")
|
|
24495
|
+
});
|
|
24496
|
+
var CATEGORY_FOLDERS = {
|
|
24497
|
+
recipes: "Recipes",
|
|
24498
|
+
shaders: "Shaders",
|
|
24499
|
+
presets: "Presets",
|
|
24500
|
+
components: "Components",
|
|
24501
|
+
setlists: "Setlists"
|
|
24502
|
+
};
|
|
24503
|
+
var ALL_KINDS = Object.keys(CATEGORY_FOLDERS);
|
|
24504
|
+
function fmString(data, key) {
|
|
24505
|
+
const v = data[key];
|
|
24506
|
+
return typeof v === "string" ? v : void 0;
|
|
24507
|
+
}
|
|
24508
|
+
function fmTags(data) {
|
|
24509
|
+
const v = data.tags;
|
|
24510
|
+
if (Array.isArray(v)) return v.map((t) => String(t)).filter(Boolean);
|
|
24511
|
+
if (typeof v === "string")
|
|
24512
|
+
return v.split(",").map((t) => t.trim()).filter(Boolean);
|
|
24513
|
+
return [];
|
|
24514
|
+
}
|
|
24515
|
+
function matchesQuery(title, tags, query) {
|
|
24516
|
+
const q59 = query.toLowerCase();
|
|
24517
|
+
if (title.toLowerCase().includes(q59)) return true;
|
|
24518
|
+
return tags.some((t) => t.toLowerCase().includes(q59));
|
|
24519
|
+
}
|
|
24520
|
+
var CATEGORY_TITLES = {
|
|
24521
|
+
recipes: "Recipes",
|
|
24522
|
+
shaders: "Shaders",
|
|
24523
|
+
presets: "Presets",
|
|
24524
|
+
components: "Components",
|
|
24525
|
+
setlists: "Setlists"
|
|
24526
|
+
};
|
|
24527
|
+
function loadSnippet(kind, stem, relPath, data) {
|
|
24528
|
+
switch (kind) {
|
|
24529
|
+
case "recipes": {
|
|
24530
|
+
const id = fmString(data, "id") ?? stem;
|
|
24531
|
+
return `\`apply_recipe id=${id}\``;
|
|
24532
|
+
}
|
|
24533
|
+
case "components": {
|
|
24534
|
+
const tox = fmString(data, "tox") ?? `Components/${stem}.tox`;
|
|
24535
|
+
return `\`manage_component action=load file_path=<vault>/${tox}\``;
|
|
24536
|
+
}
|
|
24537
|
+
case "shaders":
|
|
24538
|
+
return `\`apply_shader_from_vault note=${relPath}\``;
|
|
24539
|
+
case "setlists":
|
|
24540
|
+
return `\`import_setlist note=${relPath}\``;
|
|
24541
|
+
default:
|
|
24542
|
+
return `[[${stem}]]`;
|
|
24543
|
+
}
|
|
24544
|
+
}
|
|
24545
|
+
function mdCell(s) {
|
|
24546
|
+
return s.replace(/\|/g, "\\|").replace(/\r?\n/g, " ");
|
|
24547
|
+
}
|
|
24548
|
+
function renderCell(card) {
|
|
24549
|
+
const thumb = card.png ? `![[${basename3(card.png)}]]` : "_(no preview)_";
|
|
24550
|
+
const tags = mdCell(card.tags.join(", "));
|
|
24551
|
+
return `${thumb}<br>**${mdCell(card.title)}**<br><small>${tags}</small><br>${mdCell(card.snippet)}`;
|
|
24552
|
+
}
|
|
24553
|
+
function renderSection(title, cards, columns) {
|
|
24554
|
+
const lines = [`## ${title} (${cards.length})`, ""];
|
|
24555
|
+
lines.push(`|${" |".repeat(columns)}`);
|
|
24556
|
+
lines.push(`|${"---|".repeat(columns)}`);
|
|
24557
|
+
for (let i = 0; i < cards.length; i += columns) {
|
|
24558
|
+
const row = cards.slice(i, i + columns).map(renderCell);
|
|
24559
|
+
while (row.length < columns) row.push("");
|
|
24560
|
+
lines.push(`| ${row.join(" | ")} |`);
|
|
24561
|
+
}
|
|
24562
|
+
lines.push("");
|
|
24563
|
+
return lines.join("\n");
|
|
24564
|
+
}
|
|
24565
|
+
async function generateLibraryIndexImpl(ctx, args) {
|
|
24566
|
+
const v = requireVault(ctx);
|
|
24567
|
+
if ("error" in v) return v.error;
|
|
24568
|
+
const { vault } = v;
|
|
24569
|
+
let outResolveError;
|
|
24570
|
+
try {
|
|
24571
|
+
vault.resolve(args.output);
|
|
24572
|
+
} catch (err) {
|
|
24573
|
+
outResolveError = err instanceof Error ? err.message : String(err);
|
|
24574
|
+
}
|
|
24575
|
+
if (outResolveError) {
|
|
24576
|
+
return errorResult(`Cannot write the index note: ${outResolveError}`);
|
|
24577
|
+
}
|
|
24578
|
+
if (!args.overwrite && vault.exists(args.output)) {
|
|
24579
|
+
return errorResult(
|
|
24580
|
+
`An index note already exists at "${args.output}". Re-run with overwrite:true to replace it.`
|
|
24581
|
+
);
|
|
24582
|
+
}
|
|
24583
|
+
const requested = args.kinds.includes("all") ? ALL_KINDS : [...new Set(args.kinds)];
|
|
24584
|
+
const counts = {};
|
|
24585
|
+
const warnings = [];
|
|
24586
|
+
const sections = [];
|
|
24587
|
+
let withThumbnails = 0;
|
|
24588
|
+
let withoutThumbnails = 0;
|
|
24589
|
+
for (const kind of requested) {
|
|
24590
|
+
const folder = CATEGORY_FOLDERS[kind];
|
|
24591
|
+
if (!folder) continue;
|
|
24592
|
+
const files = vault.list(folder, ".md");
|
|
24593
|
+
const cards = [];
|
|
24594
|
+
for (const filename of files) {
|
|
24595
|
+
const relPath = `${folder}/${filename}`;
|
|
24596
|
+
const note = readNoteSafe(vault, relPath);
|
|
24597
|
+
if ("error" in note) {
|
|
24598
|
+
warnings.push(`Could not read ${relPath}: skipped.`);
|
|
24599
|
+
continue;
|
|
24600
|
+
}
|
|
24601
|
+
const { data } = note;
|
|
24602
|
+
const stem = basename3(filename, extname2(filename));
|
|
24603
|
+
const title = fmString(data, "title") ?? fmString(data, "name") ?? stem;
|
|
24604
|
+
const tags = fmTags(data);
|
|
24605
|
+
if (args.query && !matchesQuery(title, tags, args.query)) continue;
|
|
24606
|
+
const pngRel = `${folder}/${stem}.png`;
|
|
24607
|
+
const png = args.include_thumbnails && vault.exists(pngRel) ? pngRel : null;
|
|
24608
|
+
if (png) withThumbnails += 1;
|
|
24609
|
+
else withoutThumbnails += 1;
|
|
24610
|
+
cards.push({
|
|
24611
|
+
title,
|
|
24612
|
+
tags,
|
|
24613
|
+
png,
|
|
24614
|
+
snippet: loadSnippet(kind, stem, relPath, data)
|
|
24615
|
+
});
|
|
24616
|
+
}
|
|
24617
|
+
counts[kind] = cards.length;
|
|
24618
|
+
if (cards.length > 0) sections.push({ kind, cards });
|
|
24619
|
+
}
|
|
24620
|
+
const total = sections.reduce((n, s) => n + s.cards.length, 0);
|
|
24621
|
+
const categoryCount = sections.length;
|
|
24622
|
+
const generated = (/* @__PURE__ */ new Date()).toISOString();
|
|
24623
|
+
const bodyParts = ["# Library Index", ""];
|
|
24624
|
+
if (total === 0) {
|
|
24625
|
+
bodyParts.push(
|
|
24626
|
+
`_No library assets found${args.query ? ` matching "${args.query}"` : ""}. Save recipes/components to the vault, then regenerate with \`generate_library_index\`._`
|
|
24627
|
+
);
|
|
24628
|
+
} else {
|
|
24629
|
+
bodyParts.push(
|
|
24630
|
+
`_${total} asset(s) across ${categoryCount} categor${categoryCount === 1 ? "y" : "ies"}. Regenerate with \`generate_library_index\`._`,
|
|
24631
|
+
""
|
|
24632
|
+
);
|
|
24633
|
+
for (const { kind, cards } of sections) {
|
|
24634
|
+
bodyParts.push(renderSection(CATEGORY_TITLES[kind] ?? kind, cards, args.columns));
|
|
24635
|
+
}
|
|
24636
|
+
}
|
|
24637
|
+
const body = bodyParts.join("\n").trimEnd();
|
|
24638
|
+
vault.writeNote(args.output, { type: "library-index", generated, kinds: requested }, body);
|
|
24639
|
+
const summary = total === 0 ? `Wrote ${args.output} \u2014 no library assets found.` : `Wrote ${args.output} \u2014 ${total} asset(s) across ${categoryCount} categor${categoryCount === 1 ? "y" : "ies"} (${withThumbnails} with thumbnails).`;
|
|
24640
|
+
return structuredResult(summary, {
|
|
24641
|
+
index_path: args.output,
|
|
24642
|
+
total,
|
|
24643
|
+
counts,
|
|
24644
|
+
with_thumbnails: withThumbnails,
|
|
24645
|
+
without_thumbnails: withoutThumbnails,
|
|
24646
|
+
warnings
|
|
24647
|
+
});
|
|
24648
|
+
}
|
|
24649
|
+
|
|
23045
24650
|
// src/cli/doctor.ts
|
|
23046
24651
|
import { existsSync as existsSync8, statSync as statSync2 } from "fs";
|
|
23047
24652
|
import { homedir as homedir5 } from "os";
|
|
@@ -23427,7 +25032,7 @@ async function runDoctor(opts = {}) {
|
|
|
23427
25032
|
// src/cli/agent.ts
|
|
23428
25033
|
var r = (schema, run, summary, opts = {}) => ({ schema, run, summary, mutates: !!opts.mutates, unsafe: !!opts.unsafe });
|
|
23429
25034
|
var COMMANDS = {
|
|
23430
|
-
info: r(
|
|
25035
|
+
info: r(z159.object({}), (ctx) => getTdInfoImpl(ctx), "Health check + TD/bridge info."),
|
|
23431
25036
|
reload: r(
|
|
23432
25037
|
reloadBridgeSchema,
|
|
23433
25038
|
reloadBridgeImpl,
|
|
@@ -23441,6 +25046,11 @@ var COMMANDS = {
|
|
|
23441
25046
|
"nodes find": r(findTdNodesSchema, findTdNodesImpl, "Search nodes by name pattern and/or type."),
|
|
23442
25047
|
"nodes get": r(getTdNodeParametersSchema, getTdNodeParametersImpl, "Read a node's parameters."),
|
|
23443
25048
|
"nodes errors": r(getTdNodeErrorsSchema, getTdNodeErrorsImpl, "Check a node/network for errors."),
|
|
25049
|
+
"nodes flags": r(
|
|
25050
|
+
getTdNodeFlagsSchema,
|
|
25051
|
+
getTdNodeFlagsImpl,
|
|
25052
|
+
"Inspect node flags + wiring (why-is-it-black)."
|
|
25053
|
+
),
|
|
23444
25054
|
"nodes compare": r(compareTdNodesSchema, compareTdNodesImpl, "Diff two nodes' parameters."),
|
|
23445
25055
|
"nodes snapshot": r(snapshotTdGraphSchema, snapshotTdGraphImpl, "Capture a network snapshot."),
|
|
23446
25056
|
"nodes topology": r(getTdTopologySchema, getTdTopologyImpl, "Map nodes + connections."),
|
|
@@ -23756,6 +25366,18 @@ var COMMANDS = {
|
|
|
23756
25366
|
macro: r(createMacroSchema, createMacroImpl, "Add one knob that drives many parameters.", {
|
|
23757
25367
|
mutates: true
|
|
23758
25368
|
}),
|
|
25369
|
+
modulators: r(
|
|
25370
|
+
createModulatorsSchema,
|
|
25371
|
+
createModulatorsImpl,
|
|
25372
|
+
"Build a bank of BPM-synced LFOs on one Null (the 'everything breathes' lever).",
|
|
25373
|
+
{ mutates: true }
|
|
25374
|
+
),
|
|
25375
|
+
"look-bank": r(
|
|
25376
|
+
createLookBankSchema,
|
|
25377
|
+
createLookBankImpl,
|
|
25378
|
+
"Snapshot/morph look bank: store, recall, A\u2194B blend named looks on a control COMP.",
|
|
25379
|
+
{ mutates: true }
|
|
25380
|
+
),
|
|
23759
25381
|
randomize: r(
|
|
23760
25382
|
randomizeControlsSchema,
|
|
23761
25383
|
randomizeControlsImpl,
|
|
@@ -24265,6 +25887,12 @@ var COMMANDS = {
|
|
|
24265
25887
|
installLibraryPackageImpl,
|
|
24266
25888
|
"Install a local package folder, zip, tox, or manifest into a package directory.",
|
|
24267
25889
|
{ mutates: true }
|
|
25890
|
+
),
|
|
25891
|
+
"library-index": r(
|
|
25892
|
+
generateLibraryIndexSchema,
|
|
25893
|
+
generateLibraryIndexImpl,
|
|
25894
|
+
"Write a Markdown contact-sheet of the whole vault library (thumbnails + load snippets).",
|
|
25895
|
+
{ mutates: true }
|
|
24268
25896
|
)
|
|
24269
25897
|
};
|
|
24270
25898
|
function textOf(result) {
|
|
@@ -24485,18 +26113,18 @@ function assembleParams(values) {
|
|
|
24485
26113
|
}
|
|
24486
26114
|
return { raw };
|
|
24487
26115
|
}
|
|
24488
|
-
var runStepSchema =
|
|
24489
|
-
command:
|
|
24490
|
-
params:
|
|
24491
|
-
json:
|
|
24492
|
-
output:
|
|
24493
|
-
dry_run:
|
|
24494
|
-
allow_unsafe:
|
|
24495
|
-
quiet:
|
|
26116
|
+
var runStepSchema = z159.object({
|
|
26117
|
+
command: z159.union([z159.string(), z159.array(z159.string()).min(1)]),
|
|
26118
|
+
params: z159.record(z159.string(), z159.unknown()).optional(),
|
|
26119
|
+
json: z159.record(z159.string(), z159.unknown()).optional(),
|
|
26120
|
+
output: z159.enum(["json", "ndjson", "text"]).optional(),
|
|
26121
|
+
dry_run: z159.boolean().optional(),
|
|
26122
|
+
allow_unsafe: z159.boolean().optional(),
|
|
26123
|
+
quiet: z159.boolean().optional()
|
|
24496
26124
|
}).passthrough();
|
|
24497
|
-
var runFileSchema =
|
|
24498
|
-
|
|
24499
|
-
|
|
26125
|
+
var runFileSchema = z159.union([
|
|
26126
|
+
z159.array(runStepSchema),
|
|
26127
|
+
z159.object({ steps: z159.array(runStepSchema) })
|
|
24500
26128
|
]);
|
|
24501
26129
|
function runStepArgv(step) {
|
|
24502
26130
|
const argv = Array.isArray(step.command) ? [...step.command] : tokenizeLine(step.command);
|
|
@@ -24611,7 +26239,7 @@ async function runCli(argv, opts = {}) {
|
|
|
24611
26239
|
summary: cmd2.summary,
|
|
24612
26240
|
mutates: cmd2.mutates,
|
|
24613
26241
|
unsafe: cmd2.unsafe,
|
|
24614
|
-
input:
|
|
26242
|
+
input: z159.toJSONSchema(cmd2.schema)
|
|
24615
26243
|
};
|
|
24616
26244
|
return { stdout: `${JSON.stringify(doc, null, 2)}
|
|
24617
26245
|
`, stderr: "", code: 0 };
|