@dpantani/tdmcp 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +150 -143
- package/dist/cli/agent.d.ts +24 -1
- package/dist/cli/agent.js +423 -365
- package/dist/cli/agent.js.map +1 -1
- package/dist/index.js +1723 -491
- package/dist/index.js.map +1 -1
- package/dist/knowledge/data/meta.json +1 -1
- package/dist/recipes/feedback_tunnel.json +4 -4
- package/dist/recipes/noise_landscape.json +102 -1
- package/dist/recipes/performable_feedback_tunnel.json +92 -0
- package/dist/recipes/webcam_glitch.json +5 -1
- package/package.json +1 -1
- package/recipes/feedback_tunnel.json +4 -4
- package/recipes/noise_landscape.json +102 -1
- package/recipes/performable_feedback_tunnel.json +92 -0
- package/recipes/webcam_glitch.json +5 -1
- package/td/modules/mcp/controllers/api_controller.py +29 -1
- package/td/modules/mcp/services/analysis_service.py +7 -2
- package/td/modules/mcp/services/api_service.py +25 -3
- package/td/modules/mcp/services/batch_service.py +36 -2
- package/td/modules/mcp/services/preview_service.py +84 -11
- package/td/tests/test_api_controller.py +34 -0
- package/td/tests/test_services.py +209 -0
package/dist/cli/agent.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/cli/agent.ts
|
|
4
4
|
import { pathToFileURL } from "url";
|
|
5
5
|
import { parseArgs } from "util";
|
|
6
|
-
import { z as
|
|
6
|
+
import { z as z22 } from "zod";
|
|
7
7
|
|
|
8
8
|
// src/knowledge/index.ts
|
|
9
9
|
import { existsSync as existsSync2, readdirSync, readFileSync } from "fs";
|
|
@@ -474,61 +474,164 @@ import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as
|
|
|
474
474
|
import { join as join2 } from "path";
|
|
475
475
|
|
|
476
476
|
// src/recipes/schema.ts
|
|
477
|
+
import { z as z2 } from "zod";
|
|
478
|
+
|
|
479
|
+
// src/tools/layer2/createControlPanel.ts
|
|
477
480
|
import { z } from "zod";
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
481
|
+
|
|
482
|
+
// src/td-client/types.ts
|
|
483
|
+
var TdError = class extends Error {
|
|
484
|
+
code;
|
|
485
|
+
constructor(message, code, options) {
|
|
486
|
+
super(message, options);
|
|
487
|
+
this.name = "TdError";
|
|
488
|
+
this.code = code;
|
|
489
|
+
}
|
|
490
|
+
};
|
|
491
|
+
var TdConnectionError = class extends TdError {
|
|
492
|
+
constructor(message, options) {
|
|
493
|
+
super(message, "TD_CONNECTION", options);
|
|
494
|
+
this.name = "TdConnectionError";
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
var TdTimeoutError = class extends TdError {
|
|
498
|
+
constructor(message, options) {
|
|
499
|
+
super(message, "TD_TIMEOUT", options);
|
|
500
|
+
this.name = "TdTimeoutError";
|
|
501
|
+
}
|
|
502
|
+
};
|
|
503
|
+
var TdApiError = class extends TdError {
|
|
504
|
+
status;
|
|
505
|
+
apiCode;
|
|
506
|
+
constructor(message, options) {
|
|
507
|
+
super(message, "TD_API", options);
|
|
508
|
+
this.name = "TdApiError";
|
|
509
|
+
this.status = options?.status;
|
|
510
|
+
this.apiCode = options?.apiCode;
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
function friendlyTdError(err) {
|
|
514
|
+
if (err instanceof TdError) return err.message;
|
|
515
|
+
if (err instanceof Error) return err.message;
|
|
516
|
+
return String(err);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// src/tools/result.ts
|
|
520
|
+
function textResult(text) {
|
|
521
|
+
return { content: [{ type: "text", text }] };
|
|
522
|
+
}
|
|
523
|
+
function errorResult(message) {
|
|
524
|
+
return { isError: true, content: [{ type: "text", text: message }] };
|
|
525
|
+
}
|
|
526
|
+
function jsonResult(summary, data) {
|
|
527
|
+
const text = `${summary}
|
|
528
|
+
|
|
529
|
+
\`\`\`json
|
|
530
|
+
${JSON.stringify(data, null, 2)}
|
|
531
|
+
\`\`\``;
|
|
532
|
+
return { content: [{ type: "text", text }] };
|
|
533
|
+
}
|
|
534
|
+
function structuredResult(summary, data) {
|
|
535
|
+
return {
|
|
536
|
+
content: [{ type: "text", text: summary }],
|
|
537
|
+
structuredContent: data
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
async function guardTd(fn, onOk) {
|
|
541
|
+
try {
|
|
542
|
+
return onOk(await fn());
|
|
543
|
+
} catch (err) {
|
|
544
|
+
return errorResult(friendlyTdError(err));
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// src/tools/layer2/createControlPanel.ts
|
|
549
|
+
var controlSchema = z.object({
|
|
550
|
+
name: z.string().describe(
|
|
551
|
+
"Control label; also sanitized into a valid TD custom-parameter name (e.g. 'blur amount' \u2192 'Bluramount')."
|
|
552
|
+
),
|
|
553
|
+
type: z.enum(["float", "int", "toggle", "menu", "rgb", "pulse", "string"]).default("float").describe(
|
|
554
|
+
"Widget kind: float/int sliders, a toggle, a dropdown menu, an RGB swatch, a momentary pulse, or a text field."
|
|
555
|
+
),
|
|
556
|
+
label: z.string().optional().describe("Display label (defaults to `name`)."),
|
|
557
|
+
min: z.coerce.number().optional().describe("Slider lower bound (float/int) \u2014 also hard-clamped."),
|
|
558
|
+
max: z.coerce.number().optional().describe("Slider upper bound (float/int) \u2014 also hard-clamped."),
|
|
559
|
+
default: z.union([z.number(), z.boolean(), z.string()]).optional().describe("Initial value."),
|
|
560
|
+
menu_items: z.array(z.string()).optional().describe("Options for a 'menu' control."),
|
|
561
|
+
bind_to: z.array(z.string()).optional().describe(
|
|
562
|
+
"Parameters this control should drive, each written as 'nodePath.parName' (e.g. '/project1/sys/blur1.size'). Each target is switched to expression mode so moving the control moves the parameter live. Not supported for 'rgb'/'pulse'."
|
|
563
|
+
)
|
|
564
|
+
});
|
|
565
|
+
var createControlPanelSchema = z.object({
|
|
566
|
+
comp_path: z.string().default("/project1").describe(
|
|
567
|
+
"COMP that will receive the custom parameters \u2014 usually a generated system's container."
|
|
568
|
+
),
|
|
569
|
+
page: z.string().default("Controls").describe("Name of the custom-parameter page to add the controls to."),
|
|
570
|
+
controls: z.array(controlSchema).min(1).describe("The controls (knobs/sliders/toggles/menus) to expose.")
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
// src/recipes/schema.ts
|
|
574
|
+
var RecipeNodeSchema = z2.object({
|
|
575
|
+
name: z2.string().describe("Unique node name within the recipe (used for wiring)."),
|
|
576
|
+
type: z2.string().describe("Operator type, e.g. 'noiseTOP'."),
|
|
577
|
+
parameters: z2.record(z2.string(), z2.unknown()).default({}),
|
|
578
|
+
parent: z2.string().optional().describe(
|
|
483
579
|
"Name of another recipe node (a COMP, e.g. a geometryCOMP) to nest this node inside of. The parent must appear earlier in `nodes`. Used to place SOPs inside a Geometry COMP."
|
|
484
580
|
),
|
|
485
|
-
render:
|
|
581
|
+
render: z2.boolean().optional().describe(
|
|
486
582
|
"For a SOP nested in a geometryCOMP: make this the rendered geometry. Sets the render/display flags on it and clears its siblings, so the COMP renders this instead of its default torus."
|
|
487
583
|
),
|
|
488
|
-
comment:
|
|
584
|
+
comment: z2.string().optional()
|
|
489
585
|
});
|
|
490
|
-
var RecipeConnectionSchema =
|
|
491
|
-
from:
|
|
492
|
-
to:
|
|
493
|
-
from_output:
|
|
494
|
-
to_input:
|
|
586
|
+
var RecipeConnectionSchema = z2.object({
|
|
587
|
+
from: z2.string().describe("Source node name."),
|
|
588
|
+
to: z2.string().describe("Target node name."),
|
|
589
|
+
from_output: z2.number().int().nonnegative().default(0),
|
|
590
|
+
to_input: z2.number().int().nonnegative().default(0)
|
|
495
591
|
});
|
|
496
|
-
var RecipeParameterSchema =
|
|
497
|
-
name:
|
|
498
|
-
node:
|
|
499
|
-
param:
|
|
500
|
-
value:
|
|
501
|
-
label:
|
|
502
|
-
min:
|
|
503
|
-
max:
|
|
504
|
-
description:
|
|
592
|
+
var RecipeParameterSchema = z2.object({
|
|
593
|
+
name: z2.string().describe("Friendly name of the exposed control."),
|
|
594
|
+
node: z2.string().describe("Recipe node name the parameter belongs to."),
|
|
595
|
+
param: z2.string().describe("TD parameter name on that node."),
|
|
596
|
+
value: z2.unknown().optional(),
|
|
597
|
+
label: z2.string().optional(),
|
|
598
|
+
min: z2.number().optional(),
|
|
599
|
+
max: z2.number().optional(),
|
|
600
|
+
description: z2.string().optional()
|
|
505
601
|
});
|
|
506
|
-
var RecipeGlslUniformSchema =
|
|
507
|
-
node:
|
|
508
|
-
name:
|
|
509
|
-
kind:
|
|
602
|
+
var RecipeGlslUniformSchema = z2.object({
|
|
603
|
+
node: z2.string().describe("Recipe node name of the GLSL TOP that declares the uniform."),
|
|
604
|
+
name: z2.string().describe("Uniform name as referenced in the shader, e.g. 'uFeed'."),
|
|
605
|
+
kind: z2.enum(["float", "vec", "color"]).default("float").describe(
|
|
510
606
|
"Uniform kind: float (uniform float), vec (uniform vec2/3/4), color (rgba). float/vec use the Vectors page; color uses the Colors page."
|
|
511
607
|
),
|
|
512
|
-
value:
|
|
513
|
-
label:
|
|
514
|
-
min:
|
|
515
|
-
max:
|
|
516
|
-
description:
|
|
608
|
+
value: z2.union([z2.number(), z2.array(z2.number())]).optional().describe("Initial value: a number for float, or an array of components for vec/color."),
|
|
609
|
+
label: z2.string().optional(),
|
|
610
|
+
min: z2.number().optional(),
|
|
611
|
+
max: z2.number().optional(),
|
|
612
|
+
description: z2.string().optional()
|
|
517
613
|
});
|
|
518
|
-
var RecipeSchema =
|
|
519
|
-
id:
|
|
520
|
-
name:
|
|
521
|
-
description:
|
|
522
|
-
tags:
|
|
523
|
-
difficulty:
|
|
524
|
-
td_version_min:
|
|
525
|
-
nodes:
|
|
526
|
-
connections:
|
|
527
|
-
parameters:
|
|
528
|
-
glsl_uniforms:
|
|
529
|
-
glsl_code:
|
|
530
|
-
python_code:
|
|
531
|
-
|
|
614
|
+
var RecipeSchema = z2.object({
|
|
615
|
+
id: z2.string(),
|
|
616
|
+
name: z2.string(),
|
|
617
|
+
description: z2.string().default(""),
|
|
618
|
+
tags: z2.array(z2.string()).default([]),
|
|
619
|
+
difficulty: z2.enum(["beginner", "intermediate", "advanced"]).default("intermediate"),
|
|
620
|
+
td_version_min: z2.string().default("2023"),
|
|
621
|
+
nodes: z2.array(RecipeNodeSchema).min(1),
|
|
622
|
+
connections: z2.array(RecipeConnectionSchema).default([]),
|
|
623
|
+
parameters: z2.array(RecipeParameterSchema).default([]),
|
|
624
|
+
glsl_uniforms: z2.array(RecipeGlslUniformSchema).default([]),
|
|
625
|
+
glsl_code: z2.record(z2.string(), z2.string()).optional(),
|
|
626
|
+
python_code: z2.record(z2.string(), z2.string()).optional(),
|
|
627
|
+
/**
|
|
628
|
+
* Live controls to auto-expose on the system container: custom parameters (knobs/
|
|
629
|
+
* sliders/toggles) bound to node parameters so the built system is immediately
|
|
630
|
+
* playable. Each control's `bind_to` uses recipe node *names* ("nodeName.parName");
|
|
631
|
+
* buildFromRecipe rewrites them to the real created paths.
|
|
632
|
+
*/
|
|
633
|
+
controls: z2.array(controlSchema).default([]),
|
|
634
|
+
preview_description: z2.string().default("")
|
|
532
635
|
});
|
|
533
636
|
|
|
534
637
|
// src/recipes/loader.ts
|
|
@@ -596,145 +699,110 @@ var RecipeLibrary = class {
|
|
|
596
699
|
}
|
|
597
700
|
};
|
|
598
701
|
|
|
599
|
-
// src/td-client/types.ts
|
|
600
|
-
var TdError = class extends Error {
|
|
601
|
-
code;
|
|
602
|
-
constructor(message, code, options) {
|
|
603
|
-
super(message, options);
|
|
604
|
-
this.name = "TdError";
|
|
605
|
-
this.code = code;
|
|
606
|
-
}
|
|
607
|
-
};
|
|
608
|
-
var TdConnectionError = class extends TdError {
|
|
609
|
-
constructor(message, options) {
|
|
610
|
-
super(message, "TD_CONNECTION", options);
|
|
611
|
-
this.name = "TdConnectionError";
|
|
612
|
-
}
|
|
613
|
-
};
|
|
614
|
-
var TdTimeoutError = class extends TdError {
|
|
615
|
-
constructor(message, options) {
|
|
616
|
-
super(message, "TD_TIMEOUT", options);
|
|
617
|
-
this.name = "TdTimeoutError";
|
|
618
|
-
}
|
|
619
|
-
};
|
|
620
|
-
var TdApiError = class extends TdError {
|
|
621
|
-
status;
|
|
622
|
-
apiCode;
|
|
623
|
-
constructor(message, options) {
|
|
624
|
-
super(message, "TD_API", options);
|
|
625
|
-
this.name = "TdApiError";
|
|
626
|
-
this.status = options?.status;
|
|
627
|
-
this.apiCode = options?.apiCode;
|
|
628
|
-
}
|
|
629
|
-
};
|
|
630
|
-
function friendlyTdError(err) {
|
|
631
|
-
if (err instanceof TdError) return err.message;
|
|
632
|
-
if (err instanceof Error) return err.message;
|
|
633
|
-
return String(err);
|
|
634
|
-
}
|
|
635
|
-
|
|
636
702
|
// src/td-client/validators.ts
|
|
637
|
-
import { z as
|
|
638
|
-
var ApiEnvelopeSchema =
|
|
639
|
-
ok:
|
|
640
|
-
data:
|
|
641
|
-
error:
|
|
703
|
+
import { z as z3 } from "zod";
|
|
704
|
+
var ApiEnvelopeSchema = z3.object({
|
|
705
|
+
ok: z3.boolean(),
|
|
706
|
+
data: z3.unknown().optional(),
|
|
707
|
+
error: z3.object({ code: z3.string().optional(), message: z3.string() }).optional()
|
|
642
708
|
});
|
|
643
|
-
var NodeRefSchema =
|
|
644
|
-
path:
|
|
645
|
-
type:
|
|
646
|
-
name:
|
|
709
|
+
var NodeRefSchema = z3.object({
|
|
710
|
+
path: z3.string(),
|
|
711
|
+
type: z3.string().default(""),
|
|
712
|
+
name: z3.string().default(""),
|
|
713
|
+
/** Parameters that could not be applied at create time (unknown name or bad value). */
|
|
714
|
+
parameter_warnings: z3.array(z3.string()).optional()
|
|
647
715
|
});
|
|
648
716
|
var NodeDetailSchema = NodeRefSchema.extend({
|
|
649
|
-
parameters:
|
|
650
|
-
inputs:
|
|
651
|
-
outputs:
|
|
652
|
-
family:
|
|
653
|
-
errors:
|
|
717
|
+
parameters: z3.record(z3.string(), z3.unknown()).default({}),
|
|
718
|
+
inputs: z3.array(z3.string()).optional(),
|
|
719
|
+
outputs: z3.array(z3.string()).optional(),
|
|
720
|
+
family: z3.string().optional(),
|
|
721
|
+
errors: z3.array(z3.string()).optional()
|
|
654
722
|
});
|
|
655
|
-
var NodeListSchema =
|
|
656
|
-
var InfoSchema =
|
|
657
|
-
td_version:
|
|
658
|
-
python_version:
|
|
659
|
-
build:
|
|
660
|
-
bridge_version:
|
|
661
|
-
project:
|
|
723
|
+
var NodeListSchema = z3.object({ nodes: z3.array(NodeRefSchema).default([]) });
|
|
724
|
+
var InfoSchema = z3.object({
|
|
725
|
+
td_version: z3.string().optional(),
|
|
726
|
+
python_version: z3.string().optional(),
|
|
727
|
+
build: z3.string().optional(),
|
|
728
|
+
bridge_version: z3.string().optional(),
|
|
729
|
+
project: z3.string().optional()
|
|
662
730
|
});
|
|
663
|
-
var NodeErrorSchema =
|
|
664
|
-
path:
|
|
665
|
-
message:
|
|
666
|
-
type:
|
|
731
|
+
var NodeErrorSchema = z3.object({
|
|
732
|
+
path: z3.string(),
|
|
733
|
+
message: z3.string(),
|
|
734
|
+
type: z3.string().optional()
|
|
667
735
|
});
|
|
668
|
-
var NodeErrorsSchema =
|
|
669
|
-
var PreviewSchema =
|
|
670
|
-
path:
|
|
671
|
-
width:
|
|
672
|
-
height:
|
|
673
|
-
format:
|
|
674
|
-
base64:
|
|
736
|
+
var NodeErrorsSchema = z3.object({ errors: z3.array(NodeErrorSchema).default([]) });
|
|
737
|
+
var PreviewSchema = z3.object({
|
|
738
|
+
path: z3.string(),
|
|
739
|
+
width: z3.number().int().positive(),
|
|
740
|
+
height: z3.number().int().positive(),
|
|
741
|
+
format: z3.string().default("png"),
|
|
742
|
+
base64: z3.string()
|
|
675
743
|
});
|
|
676
|
-
var ExecResultSchema =
|
|
677
|
-
result:
|
|
678
|
-
stdout:
|
|
679
|
-
printed:
|
|
744
|
+
var ExecResultSchema = z3.object({
|
|
745
|
+
result: z3.unknown().optional(),
|
|
746
|
+
stdout: z3.string().optional(),
|
|
747
|
+
printed: z3.array(z3.string()).optional()
|
|
680
748
|
});
|
|
681
|
-
var MethodResultSchema =
|
|
682
|
-
var DeleteResultSchema =
|
|
683
|
-
var ConnectionSchema =
|
|
684
|
-
source_path:
|
|
685
|
-
source_output:
|
|
686
|
-
target_path:
|
|
687
|
-
target_input:
|
|
749
|
+
var MethodResultSchema = z3.object({ result: z3.unknown() });
|
|
750
|
+
var DeleteResultSchema = z3.object({ deleted: z3.string() });
|
|
751
|
+
var ConnectionSchema = z3.object({
|
|
752
|
+
source_path: z3.string(),
|
|
753
|
+
source_output: z3.number().int().default(0),
|
|
754
|
+
target_path: z3.string(),
|
|
755
|
+
target_input: z3.number().int().default(0)
|
|
688
756
|
});
|
|
689
|
-
var TopologySchema =
|
|
690
|
-
nodes:
|
|
691
|
-
connections:
|
|
757
|
+
var TopologySchema = z3.object({
|
|
758
|
+
nodes: z3.array(NodeRefSchema).default([]),
|
|
759
|
+
connections: z3.array(ConnectionSchema).default([])
|
|
692
760
|
});
|
|
693
|
-
var PerformanceSchema =
|
|
694
|
-
nodes:
|
|
695
|
-
|
|
696
|
-
path:
|
|
697
|
-
cook_time_ms:
|
|
698
|
-
cook_count:
|
|
761
|
+
var PerformanceSchema = z3.object({
|
|
762
|
+
nodes: z3.array(
|
|
763
|
+
z3.object({
|
|
764
|
+
path: z3.string(),
|
|
765
|
+
cook_time_ms: z3.number().default(0),
|
|
766
|
+
cook_count: z3.number().optional()
|
|
699
767
|
})
|
|
700
768
|
).default([]),
|
|
701
|
-
total_cook_time_ms:
|
|
702
|
-
gpu_memory_mb:
|
|
769
|
+
total_cook_time_ms: z3.number().optional(),
|
|
770
|
+
gpu_memory_mb: z3.number().optional()
|
|
703
771
|
});
|
|
704
|
-
var BatchOpResultSchema =
|
|
705
|
-
action:
|
|
706
|
-
ok:
|
|
707
|
-
path:
|
|
708
|
-
data:
|
|
709
|
-
error:
|
|
772
|
+
var BatchOpResultSchema = z3.object({
|
|
773
|
+
action: z3.string(),
|
|
774
|
+
ok: z3.boolean(),
|
|
775
|
+
path: z3.string().optional(),
|
|
776
|
+
data: z3.unknown().optional(),
|
|
777
|
+
error: z3.string().optional()
|
|
710
778
|
});
|
|
711
|
-
var BatchResultSchema =
|
|
712
|
-
var CreateNodeInputSchema =
|
|
713
|
-
parent_path:
|
|
714
|
-
type:
|
|
715
|
-
name:
|
|
716
|
-
parameters:
|
|
779
|
+
var BatchResultSchema = z3.object({ results: z3.array(BatchOpResultSchema).default([]) });
|
|
780
|
+
var CreateNodeInputSchema = z3.object({
|
|
781
|
+
parent_path: z3.string(),
|
|
782
|
+
type: z3.string(),
|
|
783
|
+
name: z3.string().optional(),
|
|
784
|
+
parameters: z3.record(z3.string(), z3.unknown()).optional()
|
|
717
785
|
});
|
|
718
|
-
var BatchOperationSchema =
|
|
719
|
-
|
|
720
|
-
action:
|
|
721
|
-
parent_path:
|
|
722
|
-
type:
|
|
723
|
-
name:
|
|
724
|
-
parameters:
|
|
786
|
+
var BatchOperationSchema = z3.discriminatedUnion("action", [
|
|
787
|
+
z3.object({
|
|
788
|
+
action: z3.literal("create"),
|
|
789
|
+
parent_path: z3.string(),
|
|
790
|
+
type: z3.string(),
|
|
791
|
+
name: z3.string().optional(),
|
|
792
|
+
parameters: z3.record(z3.string(), z3.unknown()).optional()
|
|
725
793
|
}),
|
|
726
|
-
|
|
727
|
-
action:
|
|
728
|
-
path:
|
|
729
|
-
parameters:
|
|
794
|
+
z3.object({
|
|
795
|
+
action: z3.literal("update"),
|
|
796
|
+
path: z3.string(),
|
|
797
|
+
parameters: z3.record(z3.string(), z3.unknown())
|
|
730
798
|
}),
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
action:
|
|
734
|
-
source_path:
|
|
735
|
-
target_path:
|
|
736
|
-
source_output:
|
|
737
|
-
target_input:
|
|
799
|
+
z3.object({ action: z3.literal("delete"), path: z3.string() }),
|
|
800
|
+
z3.object({
|
|
801
|
+
action: z3.literal("connect"),
|
|
802
|
+
source_path: z3.string(),
|
|
803
|
+
target_path: z3.string(),
|
|
804
|
+
source_output: z3.number().int().default(0),
|
|
805
|
+
target_input: z3.number().int().default(0)
|
|
738
806
|
})
|
|
739
807
|
]);
|
|
740
808
|
|
|
@@ -893,40 +961,46 @@ var TouchDesignerClient = class {
|
|
|
893
961
|
recursive ? { recursive: true } : void 0
|
|
894
962
|
);
|
|
895
963
|
}
|
|
896
|
-
getNetworkPerformance(path) {
|
|
897
|
-
return this.request(
|
|
964
|
+
getNetworkPerformance(path, recursive = false) {
|
|
965
|
+
return this.request(
|
|
966
|
+
"GET",
|
|
967
|
+
`/api/network/${segment(path)}/performance`,
|
|
968
|
+
PerformanceSchema,
|
|
969
|
+
void 0,
|
|
970
|
+
recursive ? { recursive: true } : void 0
|
|
971
|
+
);
|
|
898
972
|
}
|
|
899
973
|
};
|
|
900
974
|
|
|
901
975
|
// src/utils/config.ts
|
|
902
|
-
import { z as
|
|
903
|
-
var ConfigSchema =
|
|
976
|
+
import { z as z4 } from "zod";
|
|
977
|
+
var ConfigSchema = z4.object({
|
|
904
978
|
/** TouchDesigner bridge host. */
|
|
905
|
-
tdHost:
|
|
979
|
+
tdHost: z4.string().min(1).default("127.0.0.1"),
|
|
906
980
|
/** TouchDesigner bridge port (WebServer DAT). */
|
|
907
|
-
tdPort:
|
|
981
|
+
tdPort: z4.coerce.number().int().positive().max(65535).default(9980),
|
|
908
982
|
/** MCP transport: `stdio` (default, for local clients) or `http` (Streamable HTTP, loopback-only). */
|
|
909
|
-
transport:
|
|
983
|
+
transport: z4.enum(["stdio", "http"]).default("stdio"),
|
|
910
984
|
/** Log verbosity (written to stderr). */
|
|
911
|
-
logLevel:
|
|
985
|
+
logLevel: z4.enum(["debug", "info", "warn", "error", "silent"]).default("info"),
|
|
912
986
|
/** Per-request timeout against the TD bridge, in milliseconds. */
|
|
913
|
-
requestTimeoutMs:
|
|
987
|
+
requestTimeoutMs: z4.coerce.number().int().positive().default(1e4),
|
|
914
988
|
/** HTTP transport port (only used when transport=http). */
|
|
915
|
-
httpPort:
|
|
989
|
+
httpPort: z4.coerce.number().int().positive().max(65535).default(3939),
|
|
916
990
|
/** Subscribe to TD WebSocket events and forward them as MCP logging notifications. */
|
|
917
|
-
events:
|
|
991
|
+
events: z4.enum(["on", "off"]).default("on"),
|
|
918
992
|
/**
|
|
919
993
|
* Raw Python escape-hatch tools (`execute_python_script`, `exec_node_method`).
|
|
920
994
|
* Set to "off" to lock them out for restricted setups; on by default.
|
|
921
995
|
*/
|
|
922
|
-
rawPython:
|
|
996
|
+
rawPython: z4.enum(["on", "off"]).default("on"),
|
|
923
997
|
/**
|
|
924
998
|
* Optional shared bearer token for the TD bridge. When set, the server sends it
|
|
925
999
|
* as `Authorization: Bearer <token>` and the bridge requires a match. Leave unset
|
|
926
1000
|
* (default) for the zero-config local flow. Set the SAME value in TouchDesigner's
|
|
927
1001
|
* environment (`TDMCP_BRIDGE_TOKEN`) to turn enforcement on.
|
|
928
1002
|
*/
|
|
929
|
-
bridgeToken:
|
|
1003
|
+
bridgeToken: z4.string().min(1).optional()
|
|
930
1004
|
});
|
|
931
1005
|
function loadConfig(env = process.env) {
|
|
932
1006
|
return ConfigSchema.parse({
|
|
@@ -982,53 +1056,22 @@ function buildToolContext(config, overrides = {}) {
|
|
|
982
1056
|
}
|
|
983
1057
|
|
|
984
1058
|
// src/tools/layer3/compareTdNodes.ts
|
|
985
|
-
import { z as
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
}
|
|
991
|
-
function errorResult(message) {
|
|
992
|
-
return { isError: true, content: [{ type: "text", text: message }] };
|
|
993
|
-
}
|
|
994
|
-
function jsonResult(summary, data) {
|
|
995
|
-
const text = `${summary}
|
|
996
|
-
|
|
997
|
-
\`\`\`json
|
|
998
|
-
${JSON.stringify(data, null, 2)}
|
|
999
|
-
\`\`\``;
|
|
1000
|
-
return { content: [{ type: "text", text }] };
|
|
1001
|
-
}
|
|
1002
|
-
function structuredResult(summary, data) {
|
|
1003
|
-
return {
|
|
1004
|
-
content: [{ type: "text", text: summary }],
|
|
1005
|
-
structuredContent: data
|
|
1006
|
-
};
|
|
1007
|
-
}
|
|
1008
|
-
async function guardTd(fn, onOk) {
|
|
1009
|
-
try {
|
|
1010
|
-
return onOk(await fn());
|
|
1011
|
-
} catch (err) {
|
|
1012
|
-
return errorResult(friendlyTdError(err));
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
|
|
1016
|
-
// src/tools/layer3/compareTdNodes.ts
|
|
1017
|
-
var compareTdNodesSchema = z4.object({
|
|
1018
|
-
path_a: z4.string().describe("First node path."),
|
|
1019
|
-
path_b: z4.string().describe("Second node path."),
|
|
1020
|
-
only_diff: z4.boolean().default(true).describe("Return only the parameters that differ (true) or also list the identical ones.")
|
|
1059
|
+
import { z as z5 } from "zod";
|
|
1060
|
+
var compareTdNodesSchema = z5.object({
|
|
1061
|
+
path_a: z5.string().describe("First node path."),
|
|
1062
|
+
path_b: z5.string().describe("Second node path."),
|
|
1063
|
+
only_diff: z5.boolean().default(true).describe("Return only the parameters that differ (true) or also list the identical ones.")
|
|
1021
1064
|
});
|
|
1022
|
-
var compareTdNodesOutputSchema =
|
|
1023
|
-
a:
|
|
1024
|
-
b:
|
|
1025
|
-
type_a:
|
|
1026
|
-
type_b:
|
|
1027
|
-
type_match:
|
|
1028
|
-
differing_count:
|
|
1029
|
-
same_count:
|
|
1030
|
-
differing:
|
|
1031
|
-
identical:
|
|
1065
|
+
var compareTdNodesOutputSchema = z5.object({
|
|
1066
|
+
a: z5.string(),
|
|
1067
|
+
b: z5.string(),
|
|
1068
|
+
type_a: z5.string(),
|
|
1069
|
+
type_b: z5.string(),
|
|
1070
|
+
type_match: z5.boolean(),
|
|
1071
|
+
differing_count: z5.number(),
|
|
1072
|
+
same_count: z5.number(),
|
|
1073
|
+
differing: z5.array(z5.object({ param: z5.string(), a: z5.unknown(), b: z5.unknown() })),
|
|
1074
|
+
identical: z5.array(z5.string()).optional()
|
|
1032
1075
|
});
|
|
1033
1076
|
async function compareTdNodesImpl(ctx, args) {
|
|
1034
1077
|
return guardTd(
|
|
@@ -1069,12 +1112,12 @@ async function compareTdNodesImpl(ctx, args) {
|
|
|
1069
1112
|
}
|
|
1070
1113
|
|
|
1071
1114
|
// src/tools/layer3/createTdNode.ts
|
|
1072
|
-
import { z as
|
|
1073
|
-
var createTdNodeSchema =
|
|
1074
|
-
parent_path:
|
|
1075
|
-
type:
|
|
1076
|
-
name:
|
|
1077
|
-
parameters:
|
|
1115
|
+
import { z as z6 } from "zod";
|
|
1116
|
+
var createTdNodeSchema = z6.object({
|
|
1117
|
+
parent_path: z6.string().default("/project1").describe("Parent COMP path to create the node inside."),
|
|
1118
|
+
type: z6.string().describe("Operator type string, e.g. 'noiseTOP', 'feedbackTOP', 'nullTOP', 'constantCHOP'."),
|
|
1119
|
+
name: z6.string().optional().describe("Optional node name (auto-generated if omitted)."),
|
|
1120
|
+
parameters: z6.record(z6.string(), z6.unknown()).optional().describe("Optional initial parameter overrides as key\u2192value pairs.")
|
|
1078
1121
|
});
|
|
1079
1122
|
async function createTdNodeImpl(ctx, args) {
|
|
1080
1123
|
const warnings = [];
|
|
@@ -1091,14 +1134,25 @@ async function createTdNodeImpl(ctx, args) {
|
|
|
1091
1134
|
name: args.name,
|
|
1092
1135
|
parameters: args.parameters
|
|
1093
1136
|
}),
|
|
1094
|
-
(node) =>
|
|
1137
|
+
(node) => {
|
|
1138
|
+
const allWarnings = [...warnings];
|
|
1139
|
+
if (node.parameter_warnings?.length) {
|
|
1140
|
+
allWarnings.push(
|
|
1141
|
+
`These parameter(s) were not applied (unknown name or bad value): ${node.parameter_warnings.join(", ")}.`
|
|
1142
|
+
);
|
|
1143
|
+
}
|
|
1144
|
+
return jsonResult(`Created ${node.type || args.type} at ${node.path}.`, {
|
|
1145
|
+
node,
|
|
1146
|
+
warnings: allWarnings
|
|
1147
|
+
});
|
|
1148
|
+
}
|
|
1095
1149
|
);
|
|
1096
1150
|
}
|
|
1097
1151
|
|
|
1098
1152
|
// src/tools/layer3/deleteTdNode.ts
|
|
1099
|
-
import { z as
|
|
1100
|
-
var deleteTdNodeSchema =
|
|
1101
|
-
path:
|
|
1153
|
+
import { z as z7 } from "zod";
|
|
1154
|
+
var deleteTdNodeSchema = z7.object({
|
|
1155
|
+
path: z7.string().describe("Full path of the node to delete, e.g. '/project1/noise1'.")
|
|
1102
1156
|
});
|
|
1103
1157
|
async function deleteTdNodeImpl(ctx, args) {
|
|
1104
1158
|
return guardTd(
|
|
@@ -1108,12 +1162,12 @@ async function deleteTdNodeImpl(ctx, args) {
|
|
|
1108
1162
|
}
|
|
1109
1163
|
|
|
1110
1164
|
// src/tools/layer3/execNodeMethod.ts
|
|
1111
|
-
import { z as
|
|
1112
|
-
var execNodeMethodSchema =
|
|
1113
|
-
path:
|
|
1114
|
-
method:
|
|
1115
|
-
args:
|
|
1116
|
-
kwargs:
|
|
1165
|
+
import { z as z8 } from "zod";
|
|
1166
|
+
var execNodeMethodSchema = z8.object({
|
|
1167
|
+
path: z8.string().describe("Full path of the node to call the method on."),
|
|
1168
|
+
method: z8.string().describe("Method name to call, e.g. 'cook', 'par', 'destroy', 'copy'."),
|
|
1169
|
+
args: z8.array(z8.unknown()).default([]).describe("Positional arguments."),
|
|
1170
|
+
kwargs: z8.record(z8.string(), z8.unknown()).default({}).describe("Keyword arguments.")
|
|
1117
1171
|
});
|
|
1118
1172
|
async function execNodeMethodImpl(ctx, args) {
|
|
1119
1173
|
return guardTd(
|
|
@@ -1123,12 +1177,12 @@ async function execNodeMethodImpl(ctx, args) {
|
|
|
1123
1177
|
}
|
|
1124
1178
|
|
|
1125
1179
|
// src/tools/layer3/executePythonScript.ts
|
|
1126
|
-
import { z as
|
|
1127
|
-
var executePythonScriptSchema =
|
|
1128
|
-
script:
|
|
1180
|
+
import { z as z9 } from "zod";
|
|
1181
|
+
var executePythonScriptSchema = z9.object({
|
|
1182
|
+
script: z9.string().min(1).describe(
|
|
1129
1183
|
"Python source to execute inside TouchDesigner (runs in the TD process, not locally)."
|
|
1130
1184
|
),
|
|
1131
|
-
return_output:
|
|
1185
|
+
return_output: z9.boolean().default(true).describe("Capture stdout / the value of the last expression and return it.")
|
|
1132
1186
|
});
|
|
1133
1187
|
async function executePythonScriptImpl(ctx, args) {
|
|
1134
1188
|
return guardTd(
|
|
@@ -1138,7 +1192,7 @@ async function executePythonScriptImpl(ctx, args) {
|
|
|
1138
1192
|
}
|
|
1139
1193
|
|
|
1140
1194
|
// src/tools/layer3/findTdNodes.ts
|
|
1141
|
-
import { z as
|
|
1195
|
+
import { z as z10 } from "zod";
|
|
1142
1196
|
|
|
1143
1197
|
// src/tools/layer3/nodeMatch.ts
|
|
1144
1198
|
function globToRegExp(pattern) {
|
|
@@ -1151,21 +1205,21 @@ function parentOf(path) {
|
|
|
1151
1205
|
}
|
|
1152
1206
|
|
|
1153
1207
|
// src/tools/layer3/findTdNodes.ts
|
|
1154
|
-
var findTdNodesSchema =
|
|
1155
|
-
parent_path:
|
|
1156
|
-
pattern:
|
|
1157
|
-
type:
|
|
1158
|
-
recursive:
|
|
1159
|
-
path_only:
|
|
1160
|
-
limit:
|
|
1208
|
+
var findTdNodesSchema = z10.object({
|
|
1209
|
+
parent_path: z10.string().default("/project1").describe("Where to search from."),
|
|
1210
|
+
pattern: z10.string().optional().describe("Case-insensitive name/path filter with '*' wildcards (e.g. 'text*', '*noise*')."),
|
|
1211
|
+
type: z10.string().optional().describe("Case-insensitive operator-type substring (e.g. 'TOP', 'noise')."),
|
|
1212
|
+
recursive: z10.boolean().default(true).describe("Search the whole sub-network (true) or only direct children (false)."),
|
|
1213
|
+
path_only: z10.boolean().default(false).describe("Return only matching paths."),
|
|
1214
|
+
limit: z10.number().int().positive().default(50).describe("Max matches to return.")
|
|
1161
1215
|
});
|
|
1162
|
-
var findTdNodesOutputSchema =
|
|
1163
|
-
parent_path:
|
|
1164
|
-
recursive:
|
|
1165
|
-
count:
|
|
1166
|
-
truncated:
|
|
1167
|
-
paths:
|
|
1168
|
-
matches:
|
|
1216
|
+
var findTdNodesOutputSchema = z10.object({
|
|
1217
|
+
parent_path: z10.string(),
|
|
1218
|
+
recursive: z10.boolean(),
|
|
1219
|
+
count: z10.number(),
|
|
1220
|
+
truncated: z10.boolean(),
|
|
1221
|
+
paths: z10.array(z10.string()).optional(),
|
|
1222
|
+
matches: z10.array(NodeRefSchema).optional()
|
|
1169
1223
|
});
|
|
1170
1224
|
async function findTdNodesImpl(ctx, args) {
|
|
1171
1225
|
const fetch2 = args.recursive ? async () => (await ctx.client.getNetworkTopology(args.parent_path, true)).nodes : async () => (await ctx.client.getNodes(args.parent_path)).nodes;
|
|
@@ -1189,9 +1243,9 @@ async function findTdNodesImpl(ctx, args) {
|
|
|
1189
1243
|
}
|
|
1190
1244
|
|
|
1191
1245
|
// src/tools/layer3/getModuleHelp.ts
|
|
1192
|
-
import { z as
|
|
1193
|
-
var getModuleHelpSchema =
|
|
1194
|
-
name:
|
|
1246
|
+
import { z as z11 } from "zod";
|
|
1247
|
+
var getModuleHelpSchema = z11.object({
|
|
1248
|
+
name: z11.string().describe("Class or module name to get help for, e.g. 'OP', 'App', 'Project'.")
|
|
1195
1249
|
});
|
|
1196
1250
|
function getModuleHelpImpl(ctx, args) {
|
|
1197
1251
|
const cls = ctx.knowledge.getPythonClass(args.name);
|
|
@@ -1220,9 +1274,9 @@ function getModuleHelpImpl(ctx, args) {
|
|
|
1220
1274
|
}
|
|
1221
1275
|
|
|
1222
1276
|
// src/tools/layer3/getTdClassDetails.ts
|
|
1223
|
-
import { z as
|
|
1224
|
-
var getTdClassDetailsSchema =
|
|
1225
|
-
class_name:
|
|
1277
|
+
import { z as z12 } from "zod";
|
|
1278
|
+
var getTdClassDetailsSchema = z12.object({
|
|
1279
|
+
class_name: z12.string().describe("Python class name, e.g. 'OP', 'TOP', 'App', 'CHOP'.")
|
|
1226
1280
|
});
|
|
1227
1281
|
function getTdClassDetailsImpl(ctx, args) {
|
|
1228
1282
|
const cls = ctx.knowledge.getPythonClass(args.class_name);
|
|
@@ -1240,9 +1294,9 @@ function getTdClassDetailsImpl(ctx, args) {
|
|
|
1240
1294
|
}
|
|
1241
1295
|
|
|
1242
1296
|
// src/tools/layer3/getTdClasses.ts
|
|
1243
|
-
import { z as
|
|
1244
|
-
var getTdClassesSchema =
|
|
1245
|
-
filter:
|
|
1297
|
+
import { z as z13 } from "zod";
|
|
1298
|
+
var getTdClassesSchema = z13.object({
|
|
1299
|
+
filter: z13.string().optional().describe("Optional case-insensitive substring to filter class names by.")
|
|
1246
1300
|
});
|
|
1247
1301
|
function getTdClassesImpl(ctx, args) {
|
|
1248
1302
|
let classes = ctx.knowledge.listPythonClasses();
|
|
@@ -1279,17 +1333,17 @@ async function getTdInfoImpl(ctx) {
|
|
|
1279
1333
|
}
|
|
1280
1334
|
|
|
1281
1335
|
// src/tools/layer3/getTdNodeErrors.ts
|
|
1282
|
-
import { z as
|
|
1283
|
-
var getTdNodeErrorsSchema =
|
|
1284
|
-
path:
|
|
1285
|
-
recursive:
|
|
1286
|
-
summary:
|
|
1336
|
+
import { z as z14 } from "zod";
|
|
1337
|
+
var getTdNodeErrorsSchema = z14.object({
|
|
1338
|
+
path: z14.string().describe("Full path of the node (or network root) to check for errors."),
|
|
1339
|
+
recursive: z14.boolean().default(false).describe("If true, check the whole network under `path`; otherwise just that node."),
|
|
1340
|
+
summary: z14.boolean().default(false).describe("Return only counts grouped by error type instead of the full error list.")
|
|
1287
1341
|
});
|
|
1288
|
-
var getTdNodeErrorsOutputSchema =
|
|
1289
|
-
path:
|
|
1290
|
-
total:
|
|
1291
|
-
errors:
|
|
1292
|
-
by_type:
|
|
1342
|
+
var getTdNodeErrorsOutputSchema = z14.object({
|
|
1343
|
+
path: z14.string(),
|
|
1344
|
+
total: z14.number(),
|
|
1345
|
+
errors: z14.array(NodeErrorSchema).optional(),
|
|
1346
|
+
by_type: z14.record(z14.string(), z14.number()).optional()
|
|
1293
1347
|
});
|
|
1294
1348
|
async function getTdNodeErrorsImpl(ctx, args) {
|
|
1295
1349
|
return guardTd(
|
|
@@ -1318,11 +1372,11 @@ async function getTdNodeErrorsImpl(ctx, args) {
|
|
|
1318
1372
|
}
|
|
1319
1373
|
|
|
1320
1374
|
// src/tools/layer3/getTdNodeParameters.ts
|
|
1321
|
-
import { z as
|
|
1322
|
-
var getTdNodeParametersSchema =
|
|
1323
|
-
path:
|
|
1324
|
-
keys:
|
|
1325
|
-
omit_io:
|
|
1375
|
+
import { z as z15 } from "zod";
|
|
1376
|
+
var getTdNodeParametersSchema = z15.object({
|
|
1377
|
+
path: z15.string().describe("Full path of the node to inspect."),
|
|
1378
|
+
keys: z15.array(z15.string()).optional().describe("Only return these parameter names (case-sensitive). Omit to return all parameters."),
|
|
1379
|
+
omit_io: z15.boolean().default(false).describe("Drop the inputs/outputs lists from the result to save context.")
|
|
1326
1380
|
});
|
|
1327
1381
|
async function getTdNodeParametersImpl(ctx, args) {
|
|
1328
1382
|
return guardTd(
|
|
@@ -1352,29 +1406,29 @@ async function getTdNodeParametersImpl(ctx, args) {
|
|
|
1352
1406
|
}
|
|
1353
1407
|
|
|
1354
1408
|
// src/tools/layer3/getTdNodes.ts
|
|
1355
|
-
import { z as
|
|
1356
|
-
var getTdNodesSchema =
|
|
1357
|
-
parent_path:
|
|
1358
|
-
pattern:
|
|
1409
|
+
import { z as z16 } from "zod";
|
|
1410
|
+
var getTdNodesSchema = z16.object({
|
|
1411
|
+
parent_path: z16.string().default("/project1").describe("Parent COMP whose direct children should be listed."),
|
|
1412
|
+
pattern: z16.string().optional().describe(
|
|
1359
1413
|
"Case-insensitive filter on node name/path. Supports '*' wildcards (e.g. 'text*', '*noise*')."
|
|
1360
1414
|
),
|
|
1361
|
-
path_only:
|
|
1362
|
-
limit:
|
|
1363
|
-
detail_level:
|
|
1415
|
+
path_only: z16.boolean().default(false).describe("Return only the list of node paths, dropping type/name."),
|
|
1416
|
+
limit: z16.number().int().positive().optional().describe("Cap the number of nodes returned."),
|
|
1417
|
+
detail_level: z16.enum(["summary", "full"]).default("summary").describe(
|
|
1364
1418
|
"'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."
|
|
1365
1419
|
)
|
|
1366
1420
|
});
|
|
1367
1421
|
var SAMPLE_SIZE = 10;
|
|
1368
|
-
var getTdNodesOutputSchema =
|
|
1369
|
-
parent_path:
|
|
1370
|
-
count:
|
|
1371
|
-
detail_level:
|
|
1372
|
-
truncated:
|
|
1373
|
-
by_type:
|
|
1374
|
-
sample:
|
|
1375
|
-
paths:
|
|
1376
|
-
nodes:
|
|
1377
|
-
hint:
|
|
1422
|
+
var getTdNodesOutputSchema = z16.object({
|
|
1423
|
+
parent_path: z16.string(),
|
|
1424
|
+
count: z16.number(),
|
|
1425
|
+
detail_level: z16.enum(["summary", "full"]),
|
|
1426
|
+
truncated: z16.boolean(),
|
|
1427
|
+
by_type: z16.record(z16.string(), z16.number()).optional(),
|
|
1428
|
+
sample: z16.array(z16.string()).optional(),
|
|
1429
|
+
paths: z16.array(z16.string()).optional(),
|
|
1430
|
+
nodes: z16.array(NodeRefSchema).optional(),
|
|
1431
|
+
hint: z16.string().optional()
|
|
1378
1432
|
});
|
|
1379
1433
|
async function getTdNodesImpl(ctx, args) {
|
|
1380
1434
|
return guardTd(
|
|
@@ -1429,11 +1483,11 @@ async function getTdNodesImpl(ctx, args) {
|
|
|
1429
1483
|
}
|
|
1430
1484
|
|
|
1431
1485
|
// src/tools/layer3/getTdPerformance.ts
|
|
1432
|
-
import { z as
|
|
1486
|
+
import { z as z17 } from "zod";
|
|
1433
1487
|
|
|
1434
1488
|
// src/feedback/performanceMonitor.ts
|
|
1435
|
-
async function checkPerformance(client, path, targetFps = 60) {
|
|
1436
|
-
const perf = await client.getNetworkPerformance(path);
|
|
1489
|
+
async function checkPerformance(client, path, targetFps = 60, recursive = true) {
|
|
1490
|
+
const perf = await client.getNetworkPerformance(path, recursive);
|
|
1437
1491
|
const frameBudgetMs = 1e3 / targetFps;
|
|
1438
1492
|
const warnings = [];
|
|
1439
1493
|
for (const node of perf.nodes) {
|
|
@@ -1449,31 +1503,35 @@ async function checkPerformance(client, path, targetFps = 60) {
|
|
|
1449
1503
|
`Total cook time ${totalCookMs.toFixed(2)}ms exceeds the ${frameBudgetMs.toFixed(2)}ms budget at ${targetFps}fps.`
|
|
1450
1504
|
);
|
|
1451
1505
|
}
|
|
1452
|
-
|
|
1506
|
+
const nodes = [...perf.nodes].sort((a, b) => b.cook_time_ms - a.cook_time_ms);
|
|
1507
|
+
return { path, targetFps, frameBudgetMs, totalCookMs, nodes, warnings };
|
|
1453
1508
|
}
|
|
1454
1509
|
|
|
1455
1510
|
// src/tools/layer3/getTdPerformance.ts
|
|
1456
|
-
var getTdPerformanceSchema =
|
|
1457
|
-
root_path:
|
|
1458
|
-
target_fps:
|
|
1511
|
+
var getTdPerformanceSchema = z17.object({
|
|
1512
|
+
root_path: z17.string().default("/project1").describe("Network root to measure cook times under."),
|
|
1513
|
+
target_fps: z17.number().positive().default(60).describe("Frame-rate target used to flag slow nodes."),
|
|
1514
|
+
recursive: z17.boolean().default(true).describe(
|
|
1515
|
+
"Measure every descendant (true, default) so cook time inside generated containers is counted, not just the root's direct children."
|
|
1516
|
+
)
|
|
1459
1517
|
});
|
|
1460
|
-
var getTdPerformanceOutputSchema =
|
|
1461
|
-
path:
|
|
1462
|
-
targetFps:
|
|
1463
|
-
frameBudgetMs:
|
|
1464
|
-
totalCookMs:
|
|
1465
|
-
nodes:
|
|
1466
|
-
|
|
1467
|
-
path:
|
|
1468
|
-
cook_time_ms:
|
|
1469
|
-
cook_count:
|
|
1518
|
+
var getTdPerformanceOutputSchema = z17.object({
|
|
1519
|
+
path: z17.string(),
|
|
1520
|
+
targetFps: z17.number(),
|
|
1521
|
+
frameBudgetMs: z17.number(),
|
|
1522
|
+
totalCookMs: z17.number(),
|
|
1523
|
+
nodes: z17.array(
|
|
1524
|
+
z17.object({
|
|
1525
|
+
path: z17.string(),
|
|
1526
|
+
cook_time_ms: z17.number(),
|
|
1527
|
+
cook_count: z17.number().optional()
|
|
1470
1528
|
})
|
|
1471
1529
|
),
|
|
1472
|
-
warnings:
|
|
1530
|
+
warnings: z17.array(z17.string())
|
|
1473
1531
|
});
|
|
1474
1532
|
async function getTdPerformanceImpl(ctx, args) {
|
|
1475
1533
|
return guardTd(
|
|
1476
|
-
() => checkPerformance(ctx.client, args.root_path, args.target_fps),
|
|
1534
|
+
() => checkPerformance(ctx.client, args.root_path, args.target_fps, args.recursive),
|
|
1477
1535
|
(report) => structuredResult(
|
|
1478
1536
|
report.warnings.length === 0 ? `Within budget: ${report.totalCookMs.toFixed(2)}ms total under ${args.root_path} (${args.target_fps}fps).` : `${report.warnings.length} performance warning(s) under ${args.root_path}.`,
|
|
1479
1537
|
report
|
|
@@ -1482,7 +1540,7 @@ async function getTdPerformanceImpl(ctx, args) {
|
|
|
1482
1540
|
}
|
|
1483
1541
|
|
|
1484
1542
|
// src/tools/layer3/getTdTopology.ts
|
|
1485
|
-
import { z as
|
|
1543
|
+
import { z as z18 } from "zod";
|
|
1486
1544
|
|
|
1487
1545
|
// src/feedback/networkVerifier.ts
|
|
1488
1546
|
async function verifyNetwork(client, path) {
|
|
@@ -1503,14 +1561,14 @@ async function verifyNetwork(client, path) {
|
|
|
1503
1561
|
}
|
|
1504
1562
|
|
|
1505
1563
|
// src/tools/layer3/getTdTopology.ts
|
|
1506
|
-
var getTdTopologySchema =
|
|
1507
|
-
root_path:
|
|
1564
|
+
var getTdTopologySchema = z18.object({
|
|
1565
|
+
root_path: z18.string().default("/project1").describe("Network root to map.")
|
|
1508
1566
|
});
|
|
1509
|
-
var getTdTopologyOutputSchema =
|
|
1510
|
-
path:
|
|
1511
|
-
nodeCount:
|
|
1512
|
-
connectionCount:
|
|
1513
|
-
issues:
|
|
1567
|
+
var getTdTopologyOutputSchema = z18.object({
|
|
1568
|
+
path: z18.string(),
|
|
1569
|
+
nodeCount: z18.number(),
|
|
1570
|
+
connectionCount: z18.number(),
|
|
1571
|
+
issues: z18.array(z18.string()),
|
|
1514
1572
|
topology: TopologySchema
|
|
1515
1573
|
});
|
|
1516
1574
|
async function getTdTopologyImpl(ctx, args) {
|
|
@@ -1524,27 +1582,27 @@ async function getTdTopologyImpl(ctx, args) {
|
|
|
1524
1582
|
}
|
|
1525
1583
|
|
|
1526
1584
|
// src/tools/layer3/snapshotTdGraph.ts
|
|
1527
|
-
import { z as
|
|
1585
|
+
import { z as z19 } from "zod";
|
|
1528
1586
|
var MAX_PARAM_NODES = 60;
|
|
1529
|
-
var snapshotTdGraphSchema =
|
|
1530
|
-
path:
|
|
1531
|
-
include_params:
|
|
1587
|
+
var snapshotTdGraphSchema = z19.object({
|
|
1588
|
+
path: z19.string().default("/project1").describe("Network root to snapshot."),
|
|
1589
|
+
include_params: z19.boolean().default(false).describe("Also fetch each node's parameters (one request per node; capped for large graphs).")
|
|
1532
1590
|
});
|
|
1533
|
-
var snapshotTdGraphOutputSchema =
|
|
1534
|
-
path:
|
|
1535
|
-
nodeCount:
|
|
1536
|
-
connectionCount:
|
|
1537
|
-
issues:
|
|
1538
|
-
params_truncated:
|
|
1539
|
-
nodes:
|
|
1540
|
-
|
|
1541
|
-
path:
|
|
1542
|
-
type:
|
|
1543
|
-
name:
|
|
1544
|
-
parameters:
|
|
1591
|
+
var snapshotTdGraphOutputSchema = z19.object({
|
|
1592
|
+
path: z19.string(),
|
|
1593
|
+
nodeCount: z19.number(),
|
|
1594
|
+
connectionCount: z19.number(),
|
|
1595
|
+
issues: z19.array(z19.string()),
|
|
1596
|
+
params_truncated: z19.boolean(),
|
|
1597
|
+
nodes: z19.array(
|
|
1598
|
+
z19.object({
|
|
1599
|
+
path: z19.string(),
|
|
1600
|
+
type: z19.string(),
|
|
1601
|
+
name: z19.string(),
|
|
1602
|
+
parameters: z19.record(z19.string(), z19.unknown()).optional()
|
|
1545
1603
|
})
|
|
1546
1604
|
),
|
|
1547
|
-
connections:
|
|
1605
|
+
connections: z19.array(ConnectionSchema)
|
|
1548
1606
|
});
|
|
1549
1607
|
async function snapshotTdGraphImpl(ctx, args) {
|
|
1550
1608
|
return guardTd(
|
|
@@ -1585,25 +1643,25 @@ async function snapshotTdGraphImpl(ctx, args) {
|
|
|
1585
1643
|
}
|
|
1586
1644
|
|
|
1587
1645
|
// src/tools/layer3/summarizeTdErrors.ts
|
|
1588
|
-
import { z as
|
|
1589
|
-
var summarizeTdErrorsSchema =
|
|
1590
|
-
path:
|
|
1591
|
-
group_by:
|
|
1646
|
+
import { z as z20 } from "zod";
|
|
1647
|
+
var summarizeTdErrorsSchema = z20.object({
|
|
1648
|
+
path: z20.string().default("/project1").describe("Network root to collect errors under."),
|
|
1649
|
+
group_by: z20.enum(["message", "type", "parent"]).default("message").describe(
|
|
1592
1650
|
"How to cluster errors: by exact message, by error type, or by parent container (to find a common upstream cause)."
|
|
1593
1651
|
)
|
|
1594
1652
|
});
|
|
1595
|
-
var summarizeTdErrorsOutputSchema =
|
|
1596
|
-
path:
|
|
1597
|
-
total:
|
|
1598
|
-
group_by:
|
|
1599
|
-
groups:
|
|
1600
|
-
|
|
1601
|
-
key:
|
|
1602
|
-
count:
|
|
1603
|
-
sample:
|
|
1653
|
+
var summarizeTdErrorsOutputSchema = z20.object({
|
|
1654
|
+
path: z20.string(),
|
|
1655
|
+
total: z20.number(),
|
|
1656
|
+
group_by: z20.enum(["message", "type", "parent"]),
|
|
1657
|
+
groups: z20.array(
|
|
1658
|
+
z20.object({
|
|
1659
|
+
key: z20.string(),
|
|
1660
|
+
count: z20.number(),
|
|
1661
|
+
sample: z20.object({ path: z20.string(), message: z20.string() })
|
|
1604
1662
|
})
|
|
1605
1663
|
),
|
|
1606
|
-
suggestions:
|
|
1664
|
+
suggestions: z20.array(z20.string())
|
|
1607
1665
|
});
|
|
1608
1666
|
async function summarizeTdErrorsImpl(ctx, args) {
|
|
1609
1667
|
return guardTd(
|
|
@@ -1650,10 +1708,10 @@ async function summarizeTdErrorsImpl(ctx, args) {
|
|
|
1650
1708
|
}
|
|
1651
1709
|
|
|
1652
1710
|
// src/tools/layer3/updateTdNodeParameters.ts
|
|
1653
|
-
import { z as
|
|
1654
|
-
var updateTdNodeParametersSchema =
|
|
1655
|
-
path:
|
|
1656
|
-
parameters:
|
|
1711
|
+
import { z as z21 } from "zod";
|
|
1712
|
+
var updateTdNodeParametersSchema = z21.object({
|
|
1713
|
+
path: z21.string().describe("Full path of the node whose parameters to update."),
|
|
1714
|
+
parameters: z21.record(z21.string(), z21.unknown()).describe("Parameter overrides as key\u2192value pairs, e.g. { period: 4, amplitude: 0.5 }.")
|
|
1657
1715
|
});
|
|
1658
1716
|
async function updateTdNodeParametersImpl(ctx, args) {
|
|
1659
1717
|
return guardTd(
|
|
@@ -1667,7 +1725,7 @@ async function updateTdNodeParametersImpl(ctx, args) {
|
|
|
1667
1725
|
// src/cli/agent.ts
|
|
1668
1726
|
var r = (schema, run, summary, opts = {}) => ({ schema, run, summary, mutates: !!opts.mutates, unsafe: !!opts.unsafe });
|
|
1669
1727
|
var COMMANDS = {
|
|
1670
|
-
info: r(
|
|
1728
|
+
info: r(z22.object({}), (ctx) => getTdInfoImpl(ctx), "Health check + TD/bridge info."),
|
|
1671
1729
|
"nodes list": r(
|
|
1672
1730
|
getTdNodesSchema,
|
|
1673
1731
|
getTdNodesImpl,
|
|
@@ -1803,7 +1861,7 @@ async function runCli(argv, opts = {}) {
|
|
|
1803
1861
|
summary: cmd2.summary,
|
|
1804
1862
|
mutates: cmd2.mutates,
|
|
1805
1863
|
unsafe: cmd2.unsafe,
|
|
1806
|
-
input:
|
|
1864
|
+
input: z22.toJSONSchema(cmd2.schema)
|
|
1807
1865
|
};
|
|
1808
1866
|
return { stdout: `${JSON.stringify(doc, null, 2)}
|
|
1809
1867
|
`, stderr: "", code: 0 };
|