termdock 1.4.71 → 1.4.73
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 +59 -0
- package/dist/client/assets/{ModelPreview-CG6vLwyw.js → ModelPreview-BYS46cJp.js} +1 -1
- package/dist/client/assets/{abnfDiagram-VRR7QNED-CrXEZLWX.js → abnfDiagram-VRR7QNED-Df5iizBM.js} +1 -1
- package/dist/client/assets/{arc-DbKGR7Ex.js → arc-BRLIrHaj.js} +1 -1
- package/dist/client/assets/{architectureDiagram-ZJ3FMSHR-PF5BoUbI.js → architectureDiagram-ZJ3FMSHR-BmIKhrrq.js} +1 -1
- package/dist/client/assets/{blockDiagram-677ZJIJ3-D1gRzVWB.js → blockDiagram-677ZJIJ3-PBtq2qLG.js} +1 -1
- package/dist/client/assets/{c4Diagram-LMCZKHZV-CwvOLync.js → c4Diagram-LMCZKHZV-6pevynP4.js} +1 -1
- package/dist/client/assets/channel-CfecnPVa.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-BeRdP-xP.js → chunk-2Q5K7J3B-78EZViva.js} +1 -1
- package/dist/client/assets/{chunk-32BRIVSS-CrqR5rvl.js → chunk-32BRIVSS-CekczK5l.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-CjbUr-ko.js → chunk-5VM5RSS4-fMhlGbVA.js} +1 -1
- package/dist/client/assets/{chunk-EX3LRPZG-BVEk3Wma.js → chunk-EX3LRPZG-DOBewV4Z.js} +1 -1
- package/dist/client/assets/{chunk-JWPE2WC7-WitJe8kR.js → chunk-JWPE2WC7-rtk-mGC7.js} +1 -1
- package/dist/client/assets/{chunk-MOJQB5TN-BCewlLYk.js → chunk-MOJQB5TN-CWUZocq_.js} +1 -1
- package/dist/client/assets/{chunk-RYQCIY6F-CoP0AA4G.js → chunk-RYQCIY6F-DcHAjeyV.js} +1 -1
- package/dist/client/assets/{chunk-V7JOEXUC-CSZrnmp_.js → chunk-V7JOEXUC-C2paDSKz.js} +1 -1
- package/dist/client/assets/{chunk-VR4S4FIN-BB-7zz-9.js → chunk-VR4S4FIN-Cq0ZrFUq.js} +1 -1
- package/dist/client/assets/{chunk-XXDRQBXY-CcZZlCik.js → chunk-XXDRQBXY-BboySYmP.js} +1 -1
- package/dist/client/assets/classDiagram-OUVF2IWQ-CFpMarVm.js +1 -0
- package/dist/client/assets/classDiagram-v2-EOCWNBFH-CFpMarVm.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-DcmzUDwh.js → cose-bilkent-JH36ORCC-CMnHIr6y.js} +1 -1
- package/dist/client/assets/{cynefin-VYW2F7L2-C1XlDVoJ.js → cynefin-VYW2F7L2-BpOtLuYh.js} +1 -1
- package/dist/client/assets/{cynefinDiagram-TSTJHNR4-zY6Aet2S.js → cynefinDiagram-TSTJHNR4-CmrMqA7z.js} +1 -1
- package/dist/client/assets/{dagre-VKFMJZFB-DqGTi6zR.js → dagre-VKFMJZFB-iXOV-rm7.js} +1 -1
- package/dist/client/assets/{diagram-FQU43EPY-DStcsU7S.js → diagram-FQU43EPY-D5KagnmX.js} +1 -1
- package/dist/client/assets/{diagram-G47NLZAW-Cs6IMNip.js → diagram-G47NLZAW-CglgkfyY.js} +1 -1
- package/dist/client/assets/{diagram-NH7WQ7WH-yA2bpWN7.js → diagram-NH7WQ7WH-Dos-y1pl.js} +1 -1
- package/dist/client/assets/{diagram-OA4YK3LP-Dzf9M0PI.js → diagram-OA4YK3LP-Cw6yJqFX.js} +1 -1
- package/dist/client/assets/{diagram-WEI45ONY-D75lpQKA.js → diagram-WEI45ONY-C_MHHYRA.js} +1 -1
- package/dist/client/assets/{ebnfDiagram-CCIWWBDH-CkQvIpWy.js → ebnfDiagram-CCIWWBDH-BB7x8X2l.js} +1 -1
- package/dist/client/assets/{erDiagram-Q63AITRT-Ca00Vjxz.js → erDiagram-Q63AITRT-BznrrU3P.js} +1 -1
- package/dist/client/assets/{flowDiagram-23GEKE2U-CGrnSTEp.js → flowDiagram-23GEKE2U-CMzPvwGa.js} +1 -1
- package/dist/client/assets/{ganttDiagram-NO4QXBWP-0SHfOIP4.js → ganttDiagram-NO4QXBWP-Dc9JCHv3.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-IHSO6WYX-BeigYWvs.js → gitGraphDiagram-IHSO6WYX-WlPpmZQm.js} +1 -1
- package/dist/client/assets/index-wX3Y6Ks7.js +451 -0
- package/dist/client/assets/{infoDiagram-FWYZ7A6U-CiY1-xjN.js → infoDiagram-FWYZ7A6U-DC6PBJiN.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-FXEZZL3T-CmtDjUm_.js → ishikawaDiagram-FXEZZL3T-DPfxPu0W.js} +1 -1
- package/dist/client/assets/{journeyDiagram-5HDEW3XC-B7qOr9LY.js → journeyDiagram-5HDEW3XC-aA2ovr_f.js} +1 -1
- package/dist/client/assets/{kanban-definition-HUTT4EX6-DO2P2Um3.js → kanban-definition-HUTT4EX6-_T30f7Or.js} +1 -1
- package/dist/client/assets/{linear-B832hJt0.js → linear-DRZcO7_m.js} +1 -1
- package/dist/client/assets/{mermaid.core-CsAzdiCU.js → mermaid.core-BGXjYfdb.js} +4 -4
- package/dist/client/assets/{mindmap-definition-LN4V7U3C-HI__vXhQ.js → mindmap-definition-LN4V7U3C-Bv0lhpqs.js} +1 -1
- package/dist/client/assets/{pegDiagram-2B236MQR--rZIwjXq.js → pegDiagram-2B236MQR-D-EIIOZf.js} +1 -1
- package/dist/client/assets/{pieDiagram-ENE6RG2P-D9jPf5x9.js → pieDiagram-ENE6RG2P-CATAcPgl.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-ABIIQ3AL-DTCP5ji2.js → quadrantDiagram-ABIIQ3AL-2tglHVSk.js} +1 -1
- package/dist/client/assets/{railroadDiagram-RFXS5EU6-D2Gspvrl.js → railroadDiagram-RFXS5EU6-BMKC-e8R.js} +1 -1
- package/dist/client/assets/{requirementDiagram-TGXJPOKE-DEJC02IW.js → requirementDiagram-TGXJPOKE-B5J5_wFW.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-HTMAVEWB-opOhKPap.js → sankeyDiagram-HTMAVEWB-Djlm71ae.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-DBY2YBRQ-_sz2CRAG.js → sequenceDiagram-DBY2YBRQ-BtR8OPSJ.js} +1 -1
- package/dist/client/assets/{sizeCapture-X5ZJPWSS-D0Q2QkYF.js → sizeCapture-X5ZJPWSS-CDelD9h_.js} +1 -1
- package/dist/client/assets/{stateDiagram-2N3HPSRC-VgeSQTtW.js → stateDiagram-2N3HPSRC-C50kGru1.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-6OUMAXLB-gIr1L4WZ.js +1 -0
- package/dist/client/assets/{swimlanes-5IMT3BWC-ClvBvSl0.js → swimlanes-5IMT3BWC-COHxl86-.js} +2 -2
- package/dist/client/assets/swimlanesDiagram-G3AALYLV-COE4p0kX.js +8 -0
- package/dist/client/assets/{timeline-definition-FHXFAJF6-BXNsrECV.js → timeline-definition-FHXFAJF6-br-VI8Fd.js} +1 -1
- package/dist/client/assets/{vennDiagram-L72KCM5P-BgwiqZd-.js → vennDiagram-L72KCM5P-zwMtEv81.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-EHGQE667-DiqN5ERx.js → wardleyDiagram-EHGQE667-_C8v4nnr.js} +1 -1
- package/dist/client/assets/{xychartDiagram-FW5EYKEG-DQjFVqC6.js → xychartDiagram-FW5EYKEG-CSU2R6pt.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/sw.js +1 -1
- package/dist/server/agent/autoTitle.js +3 -0
- package/dist/server/agent/pluginPackages.js +289 -0
- package/dist/server/agent/plugins.js +139 -20
- package/dist/server/agent/titleNamerCatalog.js +235 -40
- package/dist/server/cli.js +277 -17
- package/dist/server/entry.js +11 -2
- package/dist/server/routes/terminal.js +241 -30
- package/dist/server/utils/csrfProtection.js +4 -1
- package/dist/server/utils/settings.js +14 -0
- package/package.json +1 -1
- package/runtime-manifest.json +2 -2
- package/dist/client/assets/channel-JTEuKVy6.js +0 -1
- package/dist/client/assets/classDiagram-OUVF2IWQ-BiFJB_5s.js +0 -1
- package/dist/client/assets/classDiagram-v2-EOCWNBFH-BiFJB_5s.js +0 -1
- package/dist/client/assets/index-D5PKdbhW.js +0 -451
- package/dist/client/assets/stateDiagram-v2-6OUMAXLB-BVh2FRO-.js +0 -1
- package/dist/client/assets/swimlanesDiagram-G3AALYLV-C17V-kgm.js +0 -8
package/dist/server/cli.js
CHANGED
|
@@ -159,7 +159,17 @@ Options:
|
|
|
159
159
|
--plugin-init Print the Agent integration protocol reference
|
|
160
160
|
--plugin-create <file>
|
|
161
161
|
Create a plugin from a manifest JSON file
|
|
162
|
+
--plugin-install <source>
|
|
163
|
+
Install a complete plugin package from HTTPS Git or a local path
|
|
162
164
|
--plugin-list List installed agent plugins
|
|
165
|
+
--plugin-check <slug>
|
|
166
|
+
Check the installed plugin source for updates
|
|
167
|
+
--plugin-update <slug>
|
|
168
|
+
Update a plugin package from its recorded source
|
|
169
|
+
--plugin-doctor <slug>
|
|
170
|
+
Probe one plugin's title/model integration
|
|
171
|
+
--plugin-hooks <slug> <install|uninstall>
|
|
172
|
+
Manage the Agent-native hook entries for a plugin
|
|
163
173
|
--plugin-remove <slug>
|
|
164
174
|
Remove an installed agent plugin
|
|
165
175
|
-h, --help Show this help message
|
|
@@ -173,7 +183,18 @@ Short commands:
|
|
|
173
183
|
pi Same as agent-plugin
|
|
174
184
|
plugin-create <file>
|
|
175
185
|
Same as --plugin-create <file>
|
|
176
|
-
plugin-
|
|
186
|
+
plugin-install <source>
|
|
187
|
+
Install a shareable plugin repository or local package
|
|
188
|
+
plugin-list [--json]
|
|
189
|
+
List plugin packages and their recorded sources
|
|
190
|
+
plugin-check <slug>
|
|
191
|
+
Check one plugin for updates
|
|
192
|
+
plugin-update <slug>
|
|
193
|
+
Update one plugin from its recorded source
|
|
194
|
+
plugin-doctor <slug> [--json]
|
|
195
|
+
Validate title capability and dynamically probe models
|
|
196
|
+
plugin-hooks <slug> <install|uninstall>
|
|
197
|
+
Install or uninstall only that plugin's Agent hooks
|
|
177
198
|
plugin-remove <slug>
|
|
178
199
|
Same as --plugin-remove <slug>
|
|
179
200
|
s Same as --status
|
|
@@ -513,7 +534,13 @@ function parseArgs(argv) {
|
|
|
513
534
|
let pluginInit = false;
|
|
514
535
|
let pluginInitJson = false;
|
|
515
536
|
let pluginCreate;
|
|
537
|
+
let pluginInstall;
|
|
516
538
|
let pluginList = false;
|
|
539
|
+
let pluginListJson = false;
|
|
540
|
+
let pluginCheck;
|
|
541
|
+
let pluginUpdate;
|
|
542
|
+
let pluginDoctor;
|
|
543
|
+
let pluginHooks;
|
|
517
544
|
let pluginRemove;
|
|
518
545
|
let agentEvent;
|
|
519
546
|
// Short command aliases for the common path. Keep these positional-only so
|
|
@@ -638,13 +665,41 @@ function parseArgs(argv) {
|
|
|
638
665
|
argv = argv.slice(2);
|
|
639
666
|
}
|
|
640
667
|
else {
|
|
641
|
-
pluginCreate =
|
|
668
|
+
pluginCreate = '';
|
|
642
669
|
argv = argv.slice(1);
|
|
643
670
|
}
|
|
644
671
|
}
|
|
672
|
+
else if (command === 'pinstall' || command === 'plugin-install') {
|
|
673
|
+
pluginInstall = next && !next.startsWith('-') ? next : '';
|
|
674
|
+
argv = argv.slice(pluginInstall ? 2 : 1);
|
|
675
|
+
}
|
|
645
676
|
else if (command === 'pinfo' || command === 'plugin-list') {
|
|
646
677
|
pluginList = true;
|
|
647
|
-
|
|
678
|
+
pluginListJson = next === '--json';
|
|
679
|
+
argv = argv.slice(pluginListJson ? 2 : 1);
|
|
680
|
+
}
|
|
681
|
+
else if (command === 'pcheck' || command === 'plugin-check') {
|
|
682
|
+
pluginCheck = next && !next.startsWith('-') ? next : '';
|
|
683
|
+
argv = argv.slice(pluginCheck ? 2 : 1);
|
|
684
|
+
}
|
|
685
|
+
else if (command === 'pupdate' || command === 'plugin-update') {
|
|
686
|
+
pluginUpdate = next && !next.startsWith('-') ? next : '';
|
|
687
|
+
argv = argv.slice(pluginUpdate ? 2 : 1);
|
|
688
|
+
}
|
|
689
|
+
else if (command === 'pdoctor' || command === 'plugin-doctor') {
|
|
690
|
+
pluginDoctor = { slug: next && !next.startsWith('-') ? next : '', json: argv[2] === '--json' };
|
|
691
|
+
argv = argv.slice(pluginDoctor.slug ? (pluginDoctor.json ? 3 : 2) : 1);
|
|
692
|
+
}
|
|
693
|
+
else if (command === 'phooks' || command === 'plugin-hooks') {
|
|
694
|
+
const action = argv[2];
|
|
695
|
+
if (next && (action === 'install' || action === 'uninstall')) {
|
|
696
|
+
pluginHooks = { slug: next, action };
|
|
697
|
+
argv = argv.slice(3);
|
|
698
|
+
}
|
|
699
|
+
else {
|
|
700
|
+
console.error(`${ICON.err} ${c.red('Usage: td plugin-hooks <slug> <install|uninstall>')}`);
|
|
701
|
+
process.exit(1);
|
|
702
|
+
}
|
|
648
703
|
}
|
|
649
704
|
else if (command === 'premove' || command === 'plugin-remove') {
|
|
650
705
|
if (next && !next.startsWith('-')) {
|
|
@@ -652,7 +707,7 @@ function parseArgs(argv) {
|
|
|
652
707
|
argv = argv.slice(2);
|
|
653
708
|
}
|
|
654
709
|
else {
|
|
655
|
-
pluginRemove =
|
|
710
|
+
pluginRemove = '';
|
|
656
711
|
argv = argv.slice(1);
|
|
657
712
|
}
|
|
658
713
|
}
|
|
@@ -737,6 +792,8 @@ function parseArgs(argv) {
|
|
|
737
792
|
if (arg === '--json') {
|
|
738
793
|
if (pluginInit)
|
|
739
794
|
pluginInitJson = true;
|
|
795
|
+
else if (pluginList)
|
|
796
|
+
pluginListJson = true;
|
|
740
797
|
else
|
|
741
798
|
tlsJson = true;
|
|
742
799
|
continue;
|
|
@@ -850,7 +907,7 @@ function parseArgs(argv) {
|
|
|
850
907
|
index += 1;
|
|
851
908
|
}
|
|
852
909
|
else {
|
|
853
|
-
pluginCreate =
|
|
910
|
+
pluginCreate = '';
|
|
854
911
|
}
|
|
855
912
|
continue;
|
|
856
913
|
}
|
|
@@ -858,6 +915,46 @@ function parseArgs(argv) {
|
|
|
858
915
|
pluginList = true;
|
|
859
916
|
continue;
|
|
860
917
|
}
|
|
918
|
+
if (arg === '--plugin-install' || arg === '--plugin-check' || arg === '--plugin-update' || arg === '--plugin-doctor') {
|
|
919
|
+
const next = argv[index + 1];
|
|
920
|
+
if (next && !next.startsWith('-')) {
|
|
921
|
+
if (arg === '--plugin-install')
|
|
922
|
+
pluginInstall = next;
|
|
923
|
+
else if (arg === '--plugin-check')
|
|
924
|
+
pluginCheck = next;
|
|
925
|
+
else if (arg === '--plugin-update')
|
|
926
|
+
pluginUpdate = next;
|
|
927
|
+
else
|
|
928
|
+
pluginDoctor = { slug: next, json: argv[index + 2] === '--json' };
|
|
929
|
+
index += pluginDoctor?.json && arg === '--plugin-doctor' ? 2 : 1;
|
|
930
|
+
}
|
|
931
|
+
else if (arg === '--plugin-install') {
|
|
932
|
+
pluginInstall = '';
|
|
933
|
+
}
|
|
934
|
+
else if (arg === '--plugin-check') {
|
|
935
|
+
pluginCheck = '';
|
|
936
|
+
}
|
|
937
|
+
else if (arg === '--plugin-update') {
|
|
938
|
+
pluginUpdate = '';
|
|
939
|
+
}
|
|
940
|
+
else {
|
|
941
|
+
pluginDoctor = { slug: '', json: false };
|
|
942
|
+
}
|
|
943
|
+
continue;
|
|
944
|
+
}
|
|
945
|
+
if (arg === '--plugin-hooks') {
|
|
946
|
+
const slug = argv[index + 1];
|
|
947
|
+
const action = argv[index + 2];
|
|
948
|
+
if (slug && (action === 'install' || action === 'uninstall')) {
|
|
949
|
+
pluginHooks = { slug, action };
|
|
950
|
+
index += 2;
|
|
951
|
+
}
|
|
952
|
+
else {
|
|
953
|
+
console.error(`${ICON.err} ${c.red('Usage: td --plugin-hooks <slug> <install|uninstall>')}`);
|
|
954
|
+
process.exit(1);
|
|
955
|
+
}
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
861
958
|
if (arg === '--plugin-remove') {
|
|
862
959
|
const next = argv[index + 1];
|
|
863
960
|
if (next && !next.startsWith('-')) {
|
|
@@ -865,7 +962,7 @@ function parseArgs(argv) {
|
|
|
865
962
|
index += 1;
|
|
866
963
|
}
|
|
867
964
|
else {
|
|
868
|
-
pluginRemove =
|
|
965
|
+
pluginRemove = '';
|
|
869
966
|
}
|
|
870
967
|
continue;
|
|
871
968
|
}
|
|
@@ -914,7 +1011,13 @@ function parseArgs(argv) {
|
|
|
914
1011
|
pluginInit,
|
|
915
1012
|
pluginInitJson,
|
|
916
1013
|
pluginCreate,
|
|
1014
|
+
pluginInstall,
|
|
917
1015
|
pluginList,
|
|
1016
|
+
pluginListJson,
|
|
1017
|
+
pluginCheck,
|
|
1018
|
+
pluginUpdate,
|
|
1019
|
+
pluginDoctor,
|
|
1020
|
+
pluginHooks,
|
|
918
1021
|
pluginRemove,
|
|
919
1022
|
agentEvent,
|
|
920
1023
|
};
|
|
@@ -1727,7 +1830,8 @@ const AGENT_PLUGIN_EXAMPLE = {
|
|
|
1727
1830
|
},
|
|
1728
1831
|
titleNamer: {
|
|
1729
1832
|
command: 'my-agent',
|
|
1730
|
-
|
|
1833
|
+
modelArgs: ['--model', '{model}'],
|
|
1834
|
+
args: ['title', '--json', '--prompt', '{prompt}'],
|
|
1731
1835
|
models: {
|
|
1732
1836
|
command: 'my-agent',
|
|
1733
1837
|
args: ['models', '--json'],
|
|
@@ -1742,8 +1846,13 @@ function runPluginInitJson() {
|
|
|
1742
1846
|
commands: {
|
|
1743
1847
|
guide: 'td agent-plugin --json',
|
|
1744
1848
|
register: 'td plugin-create ./manifest.json',
|
|
1849
|
+
installPackage: 'td plugin-install https://github.com/owner/repository',
|
|
1850
|
+
checkUpdate: 'td plugin-check <slug>',
|
|
1851
|
+
updatePackage: 'td plugin-update <slug>',
|
|
1852
|
+
doctor: 'td plugin-doctor <slug> --json',
|
|
1745
1853
|
emit: 'td agent-event <slug> <event> [status]',
|
|
1746
|
-
inspect: 'td plugin-list',
|
|
1854
|
+
inspect: 'td plugin-list --json',
|
|
1855
|
+
hooks: 'td plugin-hooks <slug> <install|uninstall>',
|
|
1747
1856
|
remove: 'td plugin-remove <slug>',
|
|
1748
1857
|
},
|
|
1749
1858
|
ui: {
|
|
@@ -1763,6 +1872,21 @@ function runPluginInitJson() {
|
|
|
1763
1872
|
indicator: 'Theme-safe visual indicator.',
|
|
1764
1873
|
tone: 'Theme token; arbitrary status colors are not accepted.',
|
|
1765
1874
|
},
|
|
1875
|
+
security: {
|
|
1876
|
+
source: 'Only public HTTPS repository roots on approved Git hosts or explicit local paths are accepted; URL credentials are rejected.',
|
|
1877
|
+
package: 'Symlinks, active/external SVG content, oversized files, submodules, and excessive unpacked size are rejected.',
|
|
1878
|
+
hooks: 'hooks.target must be a non-symlinked ~/ JSON path. Termdock generates the hook command; manifests cannot inject shell.',
|
|
1879
|
+
titleCommands: 'Installing or inspecting a plugin never executes its title/model commands. They run only after the user enables automatic titles for that plugin.',
|
|
1880
|
+
updates: 'Changes to hook or title command declarations revoke the corresponding prior opt-in.',
|
|
1881
|
+
},
|
|
1882
|
+
titleModelContract: {
|
|
1883
|
+
noCatalog: 'If titleNamer.models is absent or yields no recommendation, Termdock omits modelArgs and uses the Agent CLI default model.',
|
|
1884
|
+
optionalArguments: 'Put every model-only flag/value in titleNamer.modelArgs. The complete group is prepended only when a model is selected.',
|
|
1885
|
+
acceptedOutput: 'A JSON array or {models, recommendedModel}. Entries may use id, name, or model; optional fields: displayName/display_name, description, isDefault/is_default, isEconomical/is_economical.',
|
|
1886
|
+
automaticSelection: 'Top-level recommendedModel, then the first isEconomical model, then the first isDefault model; otherwise omit modelArgs and use the CLI default. Termdock never guesses price from names or descriptions.',
|
|
1887
|
+
adapters: 'Use command: node plus {pluginDir}/scripts/list-models.mjs when the native CLI output needs normalization. Never hardcode a model catalog.',
|
|
1888
|
+
verify: 'Run td plugin-doctor <slug> --json. This explicit command probes models but never spends a title-generation request.',
|
|
1889
|
+
},
|
|
1766
1890
|
migrationV1: {
|
|
1767
1891
|
errorCode: 'AGENT_PLUGIN_MANIFEST_V1_UNSUPPORTED',
|
|
1768
1892
|
guideCommand: 'td agent-plugin --json',
|
|
@@ -1774,12 +1898,16 @@ function runPluginInitJson() {
|
|
|
1774
1898
|
example: 'printf \'%s\' \'{"session_id":"abc","message":"Indexing"}\' | td agent-event my-agent prompt-submit thinking',
|
|
1775
1899
|
},
|
|
1776
1900
|
integrationSteps: [
|
|
1777
|
-
'
|
|
1778
|
-
'
|
|
1901
|
+
'Create a repository with manifest.json at its root; icon.svg and helper files are optional.',
|
|
1902
|
+
'Publish the repository, then install it with td plugin-install <https-git-url>. Local directories are also supported.',
|
|
1903
|
+
'For manifest-only development, td plugin-create <manifest-file> remains available.',
|
|
1779
1904
|
'Ask the user to open Settings > Detection Rules and click Install for this Agent. They can uninstall it there later.',
|
|
1780
1905
|
'If the Agent hook format is not a Claude-style hooks map, configure native hooks to call td agent-event directly.',
|
|
1781
1906
|
'Map the completed-turn hook to event "stop"; this is also the automatic-title trigger when the user enables titles for the Agent.',
|
|
1782
1907
|
'Optionally declare titleNamer so this Agent can generate titles and dynamically publish its own model catalog.',
|
|
1908
|
+
'Put optional model flags in titleNamer.modelArgs; keep the always-present prompt invocation in titleNamer.args.',
|
|
1909
|
+
'Run td plugin-doctor <slug> --json and fix every warning before sharing the repository.',
|
|
1910
|
+
'Tell the user that enabling a plugin title provider executes that plugin\'s declared CLI; installation alone never does.',
|
|
1783
1911
|
'Run td plugin-list to verify registration.',
|
|
1784
1912
|
],
|
|
1785
1913
|
manifest: AGENT_PLUGIN_EXAMPLE,
|
|
@@ -1862,7 +1990,7 @@ ${c.dim('───────────────────')}
|
|
|
1862
1990
|
|
|
1863
1991
|
${c.dim('// ── optional: hook installation (bring your own hooks.json surface) ──')}
|
|
1864
1992
|
"hooks": {
|
|
1865
|
-
${c.dim("//
|
|
1993
|
+
${c.dim("// JSON path under ~/ only; symlink traversal is rejected.")}
|
|
1866
1994
|
${c.dim('// termdock inserts marker-carrying entries; leaves user entries untouched.')}
|
|
1867
1995
|
"target": "~/.trae/settings.json",
|
|
1868
1996
|
|
|
@@ -1888,10 +2016,13 @@ ${c.dim('───────────────────')}
|
|
|
1888
2016
|
|
|
1889
2017
|
${c.dim('// ── optional: injectable automatic-title provider ──')}
|
|
1890
2018
|
"titleNamer": {
|
|
1891
|
-
${c.dim('// No shell expansion.
|
|
2019
|
+
${c.dim('// No shell expansion. Commands run only after the user enables this title provider.')}
|
|
2020
|
+
${c.dim('// modelArgs is an all-or-nothing group and is omitted for CLI-default mode.')}
|
|
1892
2021
|
"command": "trae",
|
|
1893
|
-
"
|
|
1894
|
-
|
|
2022
|
+
"modelArgs": ["--model", "{model}"],
|
|
2023
|
+
"args": ["title", "--prompt", "{prompt}"],
|
|
2024
|
+
${c.dim('// JSON array or {models,recommendedModel}; id/name/model are accepted.')}
|
|
2025
|
+
${c.dim('// Use node + {pluginDir}/scripts/... when native fields need conversion.')}
|
|
1895
2026
|
"models": { "command": "trae", "args": ["models", "--json"] }
|
|
1896
2027
|
}
|
|
1897
2028
|
}
|
|
@@ -1915,11 +2046,20 @@ ${c.dim('──────────────────')}
|
|
|
1915
2046
|
${c.dim('# Inject the Agent definition; it appears in the UI immediately')}
|
|
1916
2047
|
td plugin-create ./trae-manifest.json
|
|
1917
2048
|
|
|
2049
|
+
${c.dim('# Or install a complete, updateable package from a public HTTPS Git repository')}
|
|
2050
|
+
td plugin-install https://github.com/owner/trae-termdock-plugin
|
|
2051
|
+
|
|
1918
2052
|
${c.dim('# The user then opens Settings > Detection Rules and clicks Install.')}
|
|
1919
2053
|
${c.dim('# The same UI button can uninstall the hooks later.')}
|
|
1920
2054
|
|
|
1921
2055
|
${c.dim('# Verify registration')}
|
|
1922
|
-
td plugin-list
|
|
2056
|
+
td plugin-list --json
|
|
2057
|
+
|
|
2058
|
+
${c.dim('# Manage the package and its hooks independently')}
|
|
2059
|
+
td plugin-check trae
|
|
2060
|
+
td plugin-update trae
|
|
2061
|
+
td plugin-doctor trae --json
|
|
2062
|
+
td plugin-hooks trae install
|
|
1923
2063
|
|
|
1924
2064
|
${c.dim('# Remove')}
|
|
1925
2065
|
td plugin-remove trae
|
|
@@ -1993,7 +2133,21 @@ async function runPluginCreate(manifestPath) {
|
|
|
1993
2133
|
console.log(` ${c.dim('Visible immediately in Settings > Detection Rules; no restart required.')}`);
|
|
1994
2134
|
console.log(` ${c.dim('The user can click Install or Uninstall there.')}`);
|
|
1995
2135
|
}
|
|
1996
|
-
async function
|
|
2136
|
+
async function runPluginInstall(source) {
|
|
2137
|
+
const { baseUrl, token } = requireRunningServer();
|
|
2138
|
+
const normalizedSource = /^https:\/\//i.test(source) ? source : path.resolve(source);
|
|
2139
|
+
const response = await postLocalJson(baseUrl, token, '/api/terminal/agent-plugins/install', { source: normalizedSource });
|
|
2140
|
+
const body = JSON.parse(response.body || '{}');
|
|
2141
|
+
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
2142
|
+
console.error(`${ICON.err} ${c.red(body.code ? `${body.code}: ${body.error ?? 'Plugin installation failed'}` : body.error ?? 'Plugin installation failed')}`);
|
|
2143
|
+
process.exit(1);
|
|
2144
|
+
}
|
|
2145
|
+
console.log(`${ICON.ok} ${c.green(`Plugin "${body.slug}" installed.`)}`);
|
|
2146
|
+
if (body.dir)
|
|
2147
|
+
console.log(` ${c.dim('Directory:')} ${c.cyan(body.dir)}`);
|
|
2148
|
+
console.log(` ${c.dim('Next:')} ${c.cyan(`td plugin-hooks ${body.slug} install`)} ${c.dim('or use Settings > Detection Rules')}`);
|
|
2149
|
+
}
|
|
2150
|
+
async function runPluginList(json = false) {
|
|
1997
2151
|
const { baseUrl, token } = requireRunningServer();
|
|
1998
2152
|
const response = await getLocalJson(baseUrl, token, '/api/terminal/agent-plugins');
|
|
1999
2153
|
const body = JSON.parse(response.body || '{}');
|
|
@@ -2001,6 +2155,10 @@ async function runPluginList() {
|
|
|
2001
2155
|
console.error(`${ICON.err} ${c.red('Failed to list plugins')}`);
|
|
2002
2156
|
process.exit(1);
|
|
2003
2157
|
}
|
|
2158
|
+
if (json) {
|
|
2159
|
+
console.log(JSON.stringify(body, null, 2));
|
|
2160
|
+
return;
|
|
2161
|
+
}
|
|
2004
2162
|
const plugins = body.plugins ?? [];
|
|
2005
2163
|
if (plugins.length === 0) {
|
|
2006
2164
|
console.log(`${c.dim('No plugins installed.')}`);
|
|
@@ -2019,7 +2177,11 @@ async function runPluginList() {
|
|
|
2019
2177
|
flags.push('resume');
|
|
2020
2178
|
if (p.iconMode !== 'mask')
|
|
2021
2179
|
flags.push(p.iconMode);
|
|
2180
|
+
if (p.updateAvailable)
|
|
2181
|
+
flags.push('update');
|
|
2022
2182
|
console.log(` ${c.cyan(p.slug)} ${c.dim(p.displayName)} ${c.gray(p.accentColor)} ${c.dim(flags.length > 0 ? `[${flags.join(', ')}]` : '')}`);
|
|
2183
|
+
if (p.source)
|
|
2184
|
+
console.log(` ${c.dim(`${p.sourceType ?? 'source'}: ${p.source}${p.revision ? ` @ ${p.revision.slice(0, 8)}` : ''}`)}`);
|
|
2023
2185
|
}
|
|
2024
2186
|
}
|
|
2025
2187
|
const errors = body.errors ?? [];
|
|
@@ -2037,6 +2199,68 @@ async function runPluginList() {
|
|
|
2037
2199
|
}
|
|
2038
2200
|
}
|
|
2039
2201
|
}
|
|
2202
|
+
async function runPluginSourceAction(slug, action) {
|
|
2203
|
+
const { baseUrl, token } = requireRunningServer();
|
|
2204
|
+
const response = await postLocalJson(baseUrl, token, `/api/terminal/agent-plugins/${encodeURIComponent(slug)}/${action}`, {});
|
|
2205
|
+
const body = JSON.parse(response.body || '{}');
|
|
2206
|
+
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
2207
|
+
console.error(`${ICON.err} ${c.red(body.error ?? `Plugin ${action} failed`)}`);
|
|
2208
|
+
process.exit(1);
|
|
2209
|
+
}
|
|
2210
|
+
if (action === 'check-update') {
|
|
2211
|
+
console.log(body.updateAvailable
|
|
2212
|
+
? `${ICON.warn} ${c.yellow(`Update available for ${slug}`)} ${c.dim(`${body.revision?.slice(0, 8) ?? '?'} → ${body.latestRevision?.slice(0, 8) ?? '?'}`)}`
|
|
2213
|
+
: `${ICON.ok} ${c.green(`${slug} is up to date.`)}`);
|
|
2214
|
+
}
|
|
2215
|
+
else {
|
|
2216
|
+
console.log(`${ICON.ok} ${c.green(`Plugin "${slug}" updated.`)}`);
|
|
2217
|
+
if (body.hookWarning)
|
|
2218
|
+
console.log(`${ICON.warn} ${c.yellow(`Hooks need attention: ${body.hookWarning}`)}`);
|
|
2219
|
+
if (body.titleWarning)
|
|
2220
|
+
console.log(`${ICON.warn} ${c.yellow(`Title commands need attention: ${body.titleWarning}`)}`);
|
|
2221
|
+
}
|
|
2222
|
+
}
|
|
2223
|
+
async function runPluginDoctor(slug, json = false) {
|
|
2224
|
+
const { baseUrl, token } = requireRunningServer();
|
|
2225
|
+
const response = await postLocalJson(baseUrl, token, `/api/terminal/agent-plugins/${encodeURIComponent(slug)}/doctor`, {});
|
|
2226
|
+
const body = JSON.parse(response.body || '{}');
|
|
2227
|
+
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
2228
|
+
console.error(`${ICON.err} ${c.red(body.error ?? 'Plugin diagnosis failed')}`);
|
|
2229
|
+
process.exit(1);
|
|
2230
|
+
}
|
|
2231
|
+
if (json) {
|
|
2232
|
+
console.log(JSON.stringify(body, null, 2));
|
|
2233
|
+
}
|
|
2234
|
+
else {
|
|
2235
|
+
const ok = body.status === 'ok' || body.status === 'cli-default';
|
|
2236
|
+
console.log(`${ok ? ICON.ok : ICON.warn} ${ok ? c.green(body.displayName ?? slug) : c.yellow(body.displayName ?? slug)} ${c.dim(`(${body.status ?? 'unknown'})`)}`);
|
|
2237
|
+
console.log(` ${c.dim('Title provider:')} ${body.hasTitleNamer ? 'yes' : 'no'}`);
|
|
2238
|
+
console.log(` ${c.dim('Model probe:')} ${body.hasModelCommand ? `${body.models?.length ?? 0} usable model(s)` : 'not declared; CLI default'}`);
|
|
2239
|
+
if (body.recommendedModel)
|
|
2240
|
+
console.log(` ${c.dim('Automatic model:')} ${c.cyan(body.recommendedModel)}`);
|
|
2241
|
+
if (body.selectionBehavior)
|
|
2242
|
+
console.log(` ${c.dim('Behavior:')} ${body.selectionBehavior}`);
|
|
2243
|
+
if (body.error)
|
|
2244
|
+
console.log(` ${c.red('Error:')} ${body.error}`);
|
|
2245
|
+
for (const warning of body.warnings ?? [])
|
|
2246
|
+
console.log(` ${ICON.warn} ${c.yellow(warning)}`);
|
|
2247
|
+
for (const step of body.nextSteps ?? [])
|
|
2248
|
+
console.log(` ${c.dim('Next:')} ${step}`);
|
|
2249
|
+
}
|
|
2250
|
+
if (body.status === 'missing-title-namer' || body.status === 'probe-failed' || body.status === 'no-models') {
|
|
2251
|
+
process.exitCode = 1;
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
async function runPluginHooks(slug, action) {
|
|
2255
|
+
const { baseUrl, token } = requireRunningServer();
|
|
2256
|
+
const response = await postLocalJson(baseUrl, token, `/api/terminal/agent-hooks/${encodeURIComponent(slug)}/${action}`, {});
|
|
2257
|
+
const body = JSON.parse(response.body || '{}');
|
|
2258
|
+
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
2259
|
+
console.error(`${ICON.err} ${c.red(body.error ?? `Plugin hook ${action} failed`)}`);
|
|
2260
|
+
process.exit(1);
|
|
2261
|
+
}
|
|
2262
|
+
console.log(`${ICON.ok} ${c.green(body.summary ?? `Hooks ${action === 'install' ? 'installed' : 'removed'}.`)}`);
|
|
2263
|
+
}
|
|
2040
2264
|
async function runPluginRemove(slug) {
|
|
2041
2265
|
const { baseUrl, token } = requireRunningServer();
|
|
2042
2266
|
const response = await new Promise((resolve, reject) => {
|
|
@@ -3029,8 +3253,44 @@ async function main() {
|
|
|
3029
3253
|
await runPluginCreate(options.pluginCreate);
|
|
3030
3254
|
process.exit(0);
|
|
3031
3255
|
}
|
|
3256
|
+
if (options.pluginInstall !== undefined) {
|
|
3257
|
+
if (!options.pluginInstall) {
|
|
3258
|
+
console.error(`${ICON.err} ${c.red('plugin-install requires an HTTPS Git URL or local package path')}`);
|
|
3259
|
+
process.exit(1);
|
|
3260
|
+
}
|
|
3261
|
+
await runPluginInstall(options.pluginInstall);
|
|
3262
|
+
process.exit(0);
|
|
3263
|
+
}
|
|
3032
3264
|
if (options.pluginList) {
|
|
3033
|
-
await runPluginList();
|
|
3265
|
+
await runPluginList(options.pluginListJson);
|
|
3266
|
+
process.exit(0);
|
|
3267
|
+
}
|
|
3268
|
+
if (options.pluginCheck !== undefined) {
|
|
3269
|
+
if (!options.pluginCheck) {
|
|
3270
|
+
console.error(`${ICON.err} ${c.red('plugin-check requires a plugin slug')}`);
|
|
3271
|
+
process.exit(1);
|
|
3272
|
+
}
|
|
3273
|
+
await runPluginSourceAction(options.pluginCheck, 'check-update');
|
|
3274
|
+
process.exit(0);
|
|
3275
|
+
}
|
|
3276
|
+
if (options.pluginUpdate !== undefined) {
|
|
3277
|
+
if (!options.pluginUpdate) {
|
|
3278
|
+
console.error(`${ICON.err} ${c.red('plugin-update requires a plugin slug')}`);
|
|
3279
|
+
process.exit(1);
|
|
3280
|
+
}
|
|
3281
|
+
await runPluginSourceAction(options.pluginUpdate, 'update');
|
|
3282
|
+
process.exit(0);
|
|
3283
|
+
}
|
|
3284
|
+
if (options.pluginDoctor) {
|
|
3285
|
+
if (!options.pluginDoctor.slug) {
|
|
3286
|
+
console.error(`${ICON.err} ${c.red('plugin-doctor requires a plugin slug')}`);
|
|
3287
|
+
process.exit(1);
|
|
3288
|
+
}
|
|
3289
|
+
await runPluginDoctor(options.pluginDoctor.slug, options.pluginDoctor.json);
|
|
3290
|
+
process.exit(process.exitCode ?? 0);
|
|
3291
|
+
}
|
|
3292
|
+
if (options.pluginHooks) {
|
|
3293
|
+
await runPluginHooks(options.pluginHooks.slug, options.pluginHooks.action);
|
|
3034
3294
|
process.exit(0);
|
|
3035
3295
|
}
|
|
3036
3296
|
if (options.pluginRemove !== undefined) {
|
package/dist/server/entry.js
CHANGED
|
@@ -411,13 +411,22 @@ export function createApp(options = {}) {
|
|
|
411
411
|
app.get('/api/home', requireAuth(), (_req, res) => {
|
|
412
412
|
res.json({ home: homedir() });
|
|
413
413
|
});
|
|
414
|
+
// A locally installed Agent/plugin CLI uses the mode-0600 local API token.
|
|
415
|
+
// Keep this bypass loopback-only: possession of the token must not turn the
|
|
416
|
+
// LAN-facing terminal API into a bearer-token endpoint.
|
|
417
|
+
const isTrustedLocalCliRequest = (req) => {
|
|
418
|
+
if (!options.localApiToken || req.header('X-Termdock-Local-Token') !== options.localApiToken)
|
|
419
|
+
return false;
|
|
420
|
+
const address = req.socket.remoteAddress ?? '';
|
|
421
|
+
return address === '127.0.0.1' || address === '::1' || address === '::ffff:127.0.0.1';
|
|
422
|
+
};
|
|
414
423
|
// 应用鉴权 + CSRF保护(在终端路由之前)
|
|
415
424
|
// The HTML preview route authenticates itself: document requests use the
|
|
416
425
|
// session cookie, then get redirected to a short-lived URL token so the
|
|
417
426
|
// sandboxed iframe's subresource requests (which browsers refuse to send
|
|
418
427
|
// cookies for) can still load images/css/js.
|
|
419
|
-
app.use('/api/terminal', requireAuth({ bypass: (req) => req.path.startsWith('/fs/preview') }));
|
|
420
|
-
app.use('/api/terminal', csrfProtection.verifyMiddleware());
|
|
428
|
+
app.use('/api/terminal', requireAuth({ bypass: (req) => req.path.startsWith('/fs/preview') || isTrustedLocalCliRequest(req) }));
|
|
429
|
+
app.use('/api/terminal', csrfProtection.verifyMiddleware({ bypass: isTrustedLocalCliRequest }));
|
|
421
430
|
// 终端路由
|
|
422
431
|
app.use('/api/terminal', terminalRoutes);
|
|
423
432
|
// 文件系统路由(继承 /api/terminal 上的 auth + CSRF 保护)
|