termdock 1.4.70 → 1.4.72
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 +36 -0
- package/dist/client/assets/{ModelPreview-Cs14IQuQ.js → ModelPreview-i9dS1A3I.js} +1 -1
- package/dist/client/assets/{abnfDiagram-VRR7QNED-DuDfdv-6.js → abnfDiagram-VRR7QNED-Cs-ty-HA.js} +1 -1
- package/dist/client/assets/{arc-CSYzGV_2.js → arc-CIX5k_JD.js} +1 -1
- package/dist/client/assets/{architectureDiagram-ZJ3FMSHR-YiZ0HXNi.js → architectureDiagram-ZJ3FMSHR-DnPnQoJy.js} +1 -1
- package/dist/client/assets/{blockDiagram-677ZJIJ3-IaQMzmYv.js → blockDiagram-677ZJIJ3-Dhew-L5v.js} +1 -1
- package/dist/client/assets/{c4Diagram-LMCZKHZV-BwyuIPRL.js → c4Diagram-LMCZKHZV-BSsEMeWn.js} +1 -1
- package/dist/client/assets/channel-CASnt1pP.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-BtHQYLBT.js → chunk-2Q5K7J3B-BWm--1Mo.js} +1 -1
- package/dist/client/assets/{chunk-32BRIVSS-CXrK07sW.js → chunk-32BRIVSS-D_WFmuvb.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-BfBshn11.js → chunk-5VM5RSS4-CQdzqlQg.js} +1 -1
- package/dist/client/assets/{chunk-EX3LRPZG-BJbilNaf.js → chunk-EX3LRPZG-Cm3zLQpt.js} +1 -1
- package/dist/client/assets/{chunk-JWPE2WC7-wgcmSRlL.js → chunk-JWPE2WC7-Cmcv5sEN.js} +1 -1
- package/dist/client/assets/{chunk-MOJQB5TN-8VZVUeDI.js → chunk-MOJQB5TN-Bam44EwJ.js} +1 -1
- package/dist/client/assets/{chunk-RYQCIY6F-B900YBFa.js → chunk-RYQCIY6F-DIVOuE8y.js} +1 -1
- package/dist/client/assets/{chunk-V7JOEXUC-BK4_RLz-.js → chunk-V7JOEXUC-BfOTf7yg.js} +1 -1
- package/dist/client/assets/{chunk-VR4S4FIN-Kj5_ycjB.js → chunk-VR4S4FIN-DbiXn0dd.js} +1 -1
- package/dist/client/assets/{chunk-XXDRQBXY-BolvPLRz.js → chunk-XXDRQBXY-C4pOe4OC.js} +1 -1
- package/dist/client/assets/classDiagram-OUVF2IWQ-Da_SQWEq.js +1 -0
- package/dist/client/assets/classDiagram-v2-EOCWNBFH-Da_SQWEq.js +1 -0
- package/dist/client/assets/{cose-bilkent-JH36ORCC-9_9_SLqv.js → cose-bilkent-JH36ORCC-CCoSPmoc.js} +1 -1
- package/dist/client/assets/{cynefin-VYW2F7L2-yEpoi3vG.js → cynefin-VYW2F7L2-B2lYz7sl.js} +1 -1
- package/dist/client/assets/{cynefinDiagram-TSTJHNR4-Dy-gvup_.js → cynefinDiagram-TSTJHNR4-Cd7crN9s.js} +1 -1
- package/dist/client/assets/{dagre-VKFMJZFB-CNjJ5pRK.js → dagre-VKFMJZFB-q9Fj9HuQ.js} +1 -1
- package/dist/client/assets/{diagram-FQU43EPY-uZ73Rj3T.js → diagram-FQU43EPY-iqHy5YHW.js} +1 -1
- package/dist/client/assets/{diagram-G47NLZAW-Cxt7TYZT.js → diagram-G47NLZAW-BE-jlh1B.js} +1 -1
- package/dist/client/assets/{diagram-NH7WQ7WH-B4TKinuZ.js → diagram-NH7WQ7WH-DmP7_WOC.js} +1 -1
- package/dist/client/assets/{diagram-OA4YK3LP-CGSuenHU.js → diagram-OA4YK3LP-CHx_k8Ar.js} +1 -1
- package/dist/client/assets/{diagram-WEI45ONY-DqqzDaUZ.js → diagram-WEI45ONY-D0wImpAF.js} +1 -1
- package/dist/client/assets/{ebnfDiagram-CCIWWBDH-B2SgXL5w.js → ebnfDiagram-CCIWWBDH-DyCB0rF8.js} +1 -1
- package/dist/client/assets/{erDiagram-Q63AITRT-D-ibkr8y.js → erDiagram-Q63AITRT-SRcd1B2Z.js} +1 -1
- package/dist/client/assets/{flowDiagram-23GEKE2U-BPeyMqr-.js → flowDiagram-23GEKE2U-D0i9mFwt.js} +1 -1
- package/dist/client/assets/{ganttDiagram-NO4QXBWP-DcEqEzzu.js → ganttDiagram-NO4QXBWP-BfwGIYDn.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-IHSO6WYX-DTZ6UCQY.js → gitGraphDiagram-IHSO6WYX-BXkJQP7O.js} +1 -1
- package/dist/client/assets/index-BA-DqJM2.js +451 -0
- package/dist/client/assets/{infoDiagram-FWYZ7A6U-BHrzVXeX.js → infoDiagram-FWYZ7A6U-CM-Ebv9R.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-FXEZZL3T-CwuoQJku.js → ishikawaDiagram-FXEZZL3T-U-qnP7xi.js} +1 -1
- package/dist/client/assets/{journeyDiagram-5HDEW3XC-ZEcy0rDZ.js → journeyDiagram-5HDEW3XC-D4RxvsuL.js} +1 -1
- package/dist/client/assets/{kanban-definition-HUTT4EX6-BDBawNbr.js → kanban-definition-HUTT4EX6-Dm_asCYK.js} +1 -1
- package/dist/client/assets/{linear-smYQWzDV.js → linear-Zhe8d635.js} +1 -1
- package/dist/client/assets/{mermaid.core-eJs7Ouvw.js → mermaid.core-D_NWIuMY.js} +4 -4
- package/dist/client/assets/{mindmap-definition-LN4V7U3C-BK1IZ9zg.js → mindmap-definition-LN4V7U3C-DXpNidd5.js} +1 -1
- package/dist/client/assets/{pegDiagram-2B236MQR-B3-mVNgX.js → pegDiagram-2B236MQR-DdOwOEgu.js} +1 -1
- package/dist/client/assets/{pieDiagram-ENE6RG2P-Bkeeyx5l.js → pieDiagram-ENE6RG2P-D_A9HoY_.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-ABIIQ3AL-DouIklEO.js → quadrantDiagram-ABIIQ3AL-D-Y3cLEF.js} +1 -1
- package/dist/client/assets/{railroadDiagram-RFXS5EU6-BB_jXQd2.js → railroadDiagram-RFXS5EU6-DD6eFV82.js} +1 -1
- package/dist/client/assets/{requirementDiagram-TGXJPOKE-u56IHfiR.js → requirementDiagram-TGXJPOKE-B59HJgL2.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-HTMAVEWB-eDi2cs5E.js → sankeyDiagram-HTMAVEWB-C2BvHMH8.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-DBY2YBRQ-DnAA2xYa.js → sequenceDiagram-DBY2YBRQ-3004tCRm.js} +1 -1
- package/dist/client/assets/{sizeCapture-X5ZJPWSS-BogPui79.js → sizeCapture-X5ZJPWSS-DCBhg8QW.js} +1 -1
- package/dist/client/assets/{stateDiagram-2N3HPSRC--EeXDy2M.js → stateDiagram-2N3HPSRC-BVqiKVe0.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-6OUMAXLB-Z0GHWyoF.js +1 -0
- package/dist/client/assets/{swimlanes-5IMT3BWC-DZAw7-kY.js → swimlanes-5IMT3BWC-DS6-sdEW.js} +2 -2
- package/dist/client/assets/swimlanesDiagram-G3AALYLV-el4HDX0G.js +8 -0
- package/dist/client/assets/{timeline-definition-FHXFAJF6-DkcnpB-Y.js → timeline-definition-FHXFAJF6-wVOEmQwG.js} +1 -1
- package/dist/client/assets/{vennDiagram-L72KCM5P-BStcDGEf.js → vennDiagram-L72KCM5P-DMZiSJDk.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-EHGQE667-Cj4CQn1M.js → wardleyDiagram-EHGQE667-Dx5Aob4Y.js} +1 -1
- package/dist/client/assets/{xychartDiagram-FW5EYKEG-_uBCjai8.js → xychartDiagram-FW5EYKEG-BqMhkhmW.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/client/sw.js +1 -1
- package/dist/server/agent/autoTitle.js +16 -36
- package/dist/server/agent/installers.js +98 -15
- package/dist/server/agent/pluginPackages.js +289 -0
- package/dist/server/agent/plugins.js +164 -15
- package/dist/server/agent/registry.js +1 -1
- package/dist/server/agent/session.js +10 -1
- package/dist/server/agent/titleNamerCatalog.js +131 -2
- package/dist/server/agentHook.js +47 -0
- package/dist/server/cli.js +224 -13
- package/dist/server/entry.js +11 -2
- package/dist/server/routes/terminal.js +267 -37
- package/dist/server/utils/csrfProtection.js +4 -1
- package/dist/server/utils/settings.js +18 -5
- package/package.json +1 -1
- package/runtime-manifest.json +2 -2
- package/dist/client/assets/channel-BotxtiNE.js +0 -1
- package/dist/client/assets/classDiagram-OUVF2IWQ-CLWlbTSC.js +0 -1
- package/dist/client/assets/classDiagram-v2-EOCWNBFH-CLWlbTSC.js +0 -1
- package/dist/client/assets/index-ivnGnbVg.js +0 -451
- package/dist/client/assets/stateDiagram-v2-6OUMAXLB-Dc-3HKTs.js +0 -1
- package/dist/client/assets/swimlanesDiagram-G3AALYLV-CI1Wlr0V.js +0 -8
package/dist/server/cli.js
CHANGED
|
@@ -159,7 +159,15 @@ 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-hooks <slug> <install|uninstall>
|
|
170
|
+
Manage the Agent-native hook entries for a plugin
|
|
163
171
|
--plugin-remove <slug>
|
|
164
172
|
Remove an installed agent plugin
|
|
165
173
|
-h, --help Show this help message
|
|
@@ -173,7 +181,16 @@ Short commands:
|
|
|
173
181
|
pi Same as agent-plugin
|
|
174
182
|
plugin-create <file>
|
|
175
183
|
Same as --plugin-create <file>
|
|
176
|
-
plugin-
|
|
184
|
+
plugin-install <source>
|
|
185
|
+
Install a shareable plugin repository or local package
|
|
186
|
+
plugin-list [--json]
|
|
187
|
+
List plugin packages and their recorded sources
|
|
188
|
+
plugin-check <slug>
|
|
189
|
+
Check one plugin for updates
|
|
190
|
+
plugin-update <slug>
|
|
191
|
+
Update one plugin from its recorded source
|
|
192
|
+
plugin-hooks <slug> <install|uninstall>
|
|
193
|
+
Install or uninstall only that plugin's Agent hooks
|
|
177
194
|
plugin-remove <slug>
|
|
178
195
|
Same as --plugin-remove <slug>
|
|
179
196
|
s Same as --status
|
|
@@ -513,7 +530,12 @@ function parseArgs(argv) {
|
|
|
513
530
|
let pluginInit = false;
|
|
514
531
|
let pluginInitJson = false;
|
|
515
532
|
let pluginCreate;
|
|
533
|
+
let pluginInstall;
|
|
516
534
|
let pluginList = false;
|
|
535
|
+
let pluginListJson = false;
|
|
536
|
+
let pluginCheck;
|
|
537
|
+
let pluginUpdate;
|
|
538
|
+
let pluginHooks;
|
|
517
539
|
let pluginRemove;
|
|
518
540
|
let agentEvent;
|
|
519
541
|
// Short command aliases for the common path. Keep these positional-only so
|
|
@@ -638,13 +660,37 @@ function parseArgs(argv) {
|
|
|
638
660
|
argv = argv.slice(2);
|
|
639
661
|
}
|
|
640
662
|
else {
|
|
641
|
-
pluginCreate =
|
|
663
|
+
pluginCreate = '';
|
|
642
664
|
argv = argv.slice(1);
|
|
643
665
|
}
|
|
644
666
|
}
|
|
667
|
+
else if (command === 'pinstall' || command === 'plugin-install') {
|
|
668
|
+
pluginInstall = next && !next.startsWith('-') ? next : '';
|
|
669
|
+
argv = argv.slice(pluginInstall ? 2 : 1);
|
|
670
|
+
}
|
|
645
671
|
else if (command === 'pinfo' || command === 'plugin-list') {
|
|
646
672
|
pluginList = true;
|
|
647
|
-
|
|
673
|
+
pluginListJson = next === '--json';
|
|
674
|
+
argv = argv.slice(pluginListJson ? 2 : 1);
|
|
675
|
+
}
|
|
676
|
+
else if (command === 'pcheck' || command === 'plugin-check') {
|
|
677
|
+
pluginCheck = next && !next.startsWith('-') ? next : '';
|
|
678
|
+
argv = argv.slice(pluginCheck ? 2 : 1);
|
|
679
|
+
}
|
|
680
|
+
else if (command === 'pupdate' || command === 'plugin-update') {
|
|
681
|
+
pluginUpdate = next && !next.startsWith('-') ? next : '';
|
|
682
|
+
argv = argv.slice(pluginUpdate ? 2 : 1);
|
|
683
|
+
}
|
|
684
|
+
else if (command === 'phooks' || command === 'plugin-hooks') {
|
|
685
|
+
const action = argv[2];
|
|
686
|
+
if (next && (action === 'install' || action === 'uninstall')) {
|
|
687
|
+
pluginHooks = { slug: next, action };
|
|
688
|
+
argv = argv.slice(3);
|
|
689
|
+
}
|
|
690
|
+
else {
|
|
691
|
+
console.error(`${ICON.err} ${c.red('Usage: td plugin-hooks <slug> <install|uninstall>')}`);
|
|
692
|
+
process.exit(1);
|
|
693
|
+
}
|
|
648
694
|
}
|
|
649
695
|
else if (command === 'premove' || command === 'plugin-remove') {
|
|
650
696
|
if (next && !next.startsWith('-')) {
|
|
@@ -652,7 +698,7 @@ function parseArgs(argv) {
|
|
|
652
698
|
argv = argv.slice(2);
|
|
653
699
|
}
|
|
654
700
|
else {
|
|
655
|
-
pluginRemove =
|
|
701
|
+
pluginRemove = '';
|
|
656
702
|
argv = argv.slice(1);
|
|
657
703
|
}
|
|
658
704
|
}
|
|
@@ -737,6 +783,8 @@ function parseArgs(argv) {
|
|
|
737
783
|
if (arg === '--json') {
|
|
738
784
|
if (pluginInit)
|
|
739
785
|
pluginInitJson = true;
|
|
786
|
+
else if (pluginList)
|
|
787
|
+
pluginListJson = true;
|
|
740
788
|
else
|
|
741
789
|
tlsJson = true;
|
|
742
790
|
continue;
|
|
@@ -850,7 +898,7 @@ function parseArgs(argv) {
|
|
|
850
898
|
index += 1;
|
|
851
899
|
}
|
|
852
900
|
else {
|
|
853
|
-
pluginCreate =
|
|
901
|
+
pluginCreate = '';
|
|
854
902
|
}
|
|
855
903
|
continue;
|
|
856
904
|
}
|
|
@@ -858,6 +906,41 @@ function parseArgs(argv) {
|
|
|
858
906
|
pluginList = true;
|
|
859
907
|
continue;
|
|
860
908
|
}
|
|
909
|
+
if (arg === '--plugin-install' || arg === '--plugin-check' || arg === '--plugin-update') {
|
|
910
|
+
const next = argv[index + 1];
|
|
911
|
+
if (next && !next.startsWith('-')) {
|
|
912
|
+
if (arg === '--plugin-install')
|
|
913
|
+
pluginInstall = next;
|
|
914
|
+
else if (arg === '--plugin-check')
|
|
915
|
+
pluginCheck = next;
|
|
916
|
+
else
|
|
917
|
+
pluginUpdate = next;
|
|
918
|
+
index += 1;
|
|
919
|
+
}
|
|
920
|
+
else if (arg === '--plugin-install') {
|
|
921
|
+
pluginInstall = '';
|
|
922
|
+
}
|
|
923
|
+
else if (arg === '--plugin-check') {
|
|
924
|
+
pluginCheck = '';
|
|
925
|
+
}
|
|
926
|
+
else {
|
|
927
|
+
pluginUpdate = '';
|
|
928
|
+
}
|
|
929
|
+
continue;
|
|
930
|
+
}
|
|
931
|
+
if (arg === '--plugin-hooks') {
|
|
932
|
+
const slug = argv[index + 1];
|
|
933
|
+
const action = argv[index + 2];
|
|
934
|
+
if (slug && (action === 'install' || action === 'uninstall')) {
|
|
935
|
+
pluginHooks = { slug, action };
|
|
936
|
+
index += 2;
|
|
937
|
+
}
|
|
938
|
+
else {
|
|
939
|
+
console.error(`${ICON.err} ${c.red('Usage: td --plugin-hooks <slug> <install|uninstall>')}`);
|
|
940
|
+
process.exit(1);
|
|
941
|
+
}
|
|
942
|
+
continue;
|
|
943
|
+
}
|
|
861
944
|
if (arg === '--plugin-remove') {
|
|
862
945
|
const next = argv[index + 1];
|
|
863
946
|
if (next && !next.startsWith('-')) {
|
|
@@ -865,7 +948,7 @@ function parseArgs(argv) {
|
|
|
865
948
|
index += 1;
|
|
866
949
|
}
|
|
867
950
|
else {
|
|
868
|
-
pluginRemove =
|
|
951
|
+
pluginRemove = '';
|
|
869
952
|
}
|
|
870
953
|
continue;
|
|
871
954
|
}
|
|
@@ -914,7 +997,12 @@ function parseArgs(argv) {
|
|
|
914
997
|
pluginInit,
|
|
915
998
|
pluginInitJson,
|
|
916
999
|
pluginCreate,
|
|
1000
|
+
pluginInstall,
|
|
917
1001
|
pluginList,
|
|
1002
|
+
pluginListJson,
|
|
1003
|
+
pluginCheck,
|
|
1004
|
+
pluginUpdate,
|
|
1005
|
+
pluginHooks,
|
|
918
1006
|
pluginRemove,
|
|
919
1007
|
agentEvent,
|
|
920
1008
|
};
|
|
@@ -1725,6 +1813,14 @@ const AGENT_PLUGIN_EXAMPLE = {
|
|
|
1725
1813
|
command: 'my-agent --resume {sessionId}',
|
|
1726
1814
|
staleFlags: ['--resume'],
|
|
1727
1815
|
},
|
|
1816
|
+
titleNamer: {
|
|
1817
|
+
command: 'my-agent',
|
|
1818
|
+
args: ['title', '--json', '--prompt', '{prompt}', '--model={model}'],
|
|
1819
|
+
models: {
|
|
1820
|
+
command: 'my-agent',
|
|
1821
|
+
args: ['models', '--json'],
|
|
1822
|
+
},
|
|
1823
|
+
},
|
|
1728
1824
|
};
|
|
1729
1825
|
function runPluginInitJson() {
|
|
1730
1826
|
console.log(JSON.stringify({
|
|
@@ -1734,8 +1830,12 @@ function runPluginInitJson() {
|
|
|
1734
1830
|
commands: {
|
|
1735
1831
|
guide: 'td agent-plugin --json',
|
|
1736
1832
|
register: 'td plugin-create ./manifest.json',
|
|
1833
|
+
installPackage: 'td plugin-install https://github.com/owner/repository',
|
|
1834
|
+
checkUpdate: 'td plugin-check <slug>',
|
|
1835
|
+
updatePackage: 'td plugin-update <slug>',
|
|
1737
1836
|
emit: 'td agent-event <slug> <event> [status]',
|
|
1738
|
-
inspect: 'td plugin-list',
|
|
1837
|
+
inspect: 'td plugin-list --json',
|
|
1838
|
+
hooks: 'td plugin-hooks <slug> <install|uninstall>',
|
|
1739
1839
|
remove: 'td plugin-remove <slug>',
|
|
1740
1840
|
},
|
|
1741
1841
|
ui: {
|
|
@@ -1755,6 +1855,13 @@ function runPluginInitJson() {
|
|
|
1755
1855
|
indicator: 'Theme-safe visual indicator.',
|
|
1756
1856
|
tone: 'Theme token; arbitrary status colors are not accepted.',
|
|
1757
1857
|
},
|
|
1858
|
+
security: {
|
|
1859
|
+
source: 'Only public HTTPS repository roots on approved Git hosts or explicit local paths are accepted; URL credentials are rejected.',
|
|
1860
|
+
package: 'Symlinks, active/external SVG content, oversized files, submodules, and excessive unpacked size are rejected.',
|
|
1861
|
+
hooks: 'hooks.target must be a non-symlinked ~/ JSON path. Termdock generates the hook command; manifests cannot inject shell.',
|
|
1862
|
+
titleCommands: 'Installing or inspecting a plugin never executes its title/model commands. They run only after the user enables automatic titles for that plugin.',
|
|
1863
|
+
updates: 'Changes to hook or title command declarations revoke the corresponding prior opt-in.',
|
|
1864
|
+
},
|
|
1758
1865
|
migrationV1: {
|
|
1759
1866
|
errorCode: 'AGENT_PLUGIN_MANIFEST_V1_UNSUPPORTED',
|
|
1760
1867
|
guideCommand: 'td agent-plugin --json',
|
|
@@ -1766,10 +1873,14 @@ function runPluginInitJson() {
|
|
|
1766
1873
|
example: 'printf \'%s\' \'{"session_id":"abc","message":"Indexing"}\' | td agent-event my-agent prompt-submit thinking',
|
|
1767
1874
|
},
|
|
1768
1875
|
integrationSteps: [
|
|
1769
|
-
'
|
|
1770
|
-
'
|
|
1876
|
+
'Create a repository with manifest.json at its root; icon.svg and helper files are optional.',
|
|
1877
|
+
'Publish the repository, then install it with td plugin-install <https-git-url>. Local directories are also supported.',
|
|
1878
|
+
'For manifest-only development, td plugin-create <manifest-file> remains available.',
|
|
1771
1879
|
'Ask the user to open Settings > Detection Rules and click Install for this Agent. They can uninstall it there later.',
|
|
1772
1880
|
'If the Agent hook format is not a Claude-style hooks map, configure native hooks to call td agent-event directly.',
|
|
1881
|
+
'Map the completed-turn hook to event "stop"; this is also the automatic-title trigger when the user enables titles for the Agent.',
|
|
1882
|
+
'Optionally declare titleNamer so this Agent can generate titles and dynamically publish its own model catalog.',
|
|
1883
|
+
'Tell the user that enabling a plugin title provider executes that plugin\'s declared CLI; installation alone never does.',
|
|
1773
1884
|
'Run td plugin-list to verify registration.',
|
|
1774
1885
|
],
|
|
1775
1886
|
manifest: AGENT_PLUGIN_EXAMPLE,
|
|
@@ -1852,7 +1963,7 @@ ${c.dim('───────────────────')}
|
|
|
1852
1963
|
|
|
1853
1964
|
${c.dim('// ── optional: hook installation (bring your own hooks.json surface) ──')}
|
|
1854
1965
|
"hooks": {
|
|
1855
|
-
${c.dim("//
|
|
1966
|
+
${c.dim("// JSON path under ~/ only; symlink traversal is rejected.")}
|
|
1856
1967
|
${c.dim('// termdock inserts marker-carrying entries; leaves user entries untouched.')}
|
|
1857
1968
|
"target": "~/.trae/settings.json",
|
|
1858
1969
|
|
|
@@ -1874,6 +1985,16 @@ ${c.dim('───────────────────')}
|
|
|
1874
1985
|
"command": "trae --resume {sessionId}",
|
|
1875
1986
|
${c.dim('// Flags to strip from the original launch argv when replaying the resume.')}
|
|
1876
1987
|
"staleFlags": ["--resume", "-r", "--session-id", "--continue"]
|
|
1988
|
+
},
|
|
1989
|
+
|
|
1990
|
+
${c.dim('// ── optional: injectable automatic-title provider ──')}
|
|
1991
|
+
"titleNamer": {
|
|
1992
|
+
${c.dim('// No shell expansion. Commands run only after the user enables this title provider.')}
|
|
1993
|
+
${c.dim('// {prompt} is required; a bare {model} arg is omitted when automatic.')}
|
|
1994
|
+
"command": "trae",
|
|
1995
|
+
"args": ["title", "--prompt", "{prompt}", "--model={model}"],
|
|
1996
|
+
${c.dim('// Must print JSON: strings or {id, displayName, description, isDefault} objects.')}
|
|
1997
|
+
"models": { "command": "trae", "args": ["models", "--json"] }
|
|
1877
1998
|
}
|
|
1878
1999
|
}
|
|
1879
2000
|
|
|
@@ -1887,6 +2008,7 @@ ${c.dim('─────────────────────')}
|
|
|
1887
2008
|
tool-complete A tool execution completed (unblocks waiting state)
|
|
1888
2009
|
notification Generic notification from the agent (heuristic gated)
|
|
1889
2010
|
stop Agent turn finished / went idle
|
|
2011
|
+
Also triggers automatic title generation when enabled
|
|
1890
2012
|
session-end Agent session closed / binary exited
|
|
1891
2013
|
|
|
1892
2014
|
${c.bold('Creating a plugin')}
|
|
@@ -1895,11 +2017,19 @@ ${c.dim('──────────────────')}
|
|
|
1895
2017
|
${c.dim('# Inject the Agent definition; it appears in the UI immediately')}
|
|
1896
2018
|
td plugin-create ./trae-manifest.json
|
|
1897
2019
|
|
|
2020
|
+
${c.dim('# Or install a complete, updateable package from a public HTTPS Git repository')}
|
|
2021
|
+
td plugin-install https://github.com/owner/trae-termdock-plugin
|
|
2022
|
+
|
|
1898
2023
|
${c.dim('# The user then opens Settings > Detection Rules and clicks Install.')}
|
|
1899
2024
|
${c.dim('# The same UI button can uninstall the hooks later.')}
|
|
1900
2025
|
|
|
1901
2026
|
${c.dim('# Verify registration')}
|
|
1902
|
-
td plugin-list
|
|
2027
|
+
td plugin-list --json
|
|
2028
|
+
|
|
2029
|
+
${c.dim('# Manage the package and its hooks independently')}
|
|
2030
|
+
td plugin-check trae
|
|
2031
|
+
td plugin-update trae
|
|
2032
|
+
td plugin-hooks trae install
|
|
1903
2033
|
|
|
1904
2034
|
${c.dim('# Remove')}
|
|
1905
2035
|
td plugin-remove trae
|
|
@@ -1973,7 +2103,21 @@ async function runPluginCreate(manifestPath) {
|
|
|
1973
2103
|
console.log(` ${c.dim('Visible immediately in Settings > Detection Rules; no restart required.')}`);
|
|
1974
2104
|
console.log(` ${c.dim('The user can click Install or Uninstall there.')}`);
|
|
1975
2105
|
}
|
|
1976
|
-
async function
|
|
2106
|
+
async function runPluginInstall(source) {
|
|
2107
|
+
const { baseUrl, token } = requireRunningServer();
|
|
2108
|
+
const normalizedSource = /^https:\/\//i.test(source) ? source : path.resolve(source);
|
|
2109
|
+
const response = await postLocalJson(baseUrl, token, '/api/terminal/agent-plugins/install', { source: normalizedSource });
|
|
2110
|
+
const body = JSON.parse(response.body || '{}');
|
|
2111
|
+
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
2112
|
+
console.error(`${ICON.err} ${c.red(body.code ? `${body.code}: ${body.error ?? 'Plugin installation failed'}` : body.error ?? 'Plugin installation failed')}`);
|
|
2113
|
+
process.exit(1);
|
|
2114
|
+
}
|
|
2115
|
+
console.log(`${ICON.ok} ${c.green(`Plugin "${body.slug}" installed.`)}`);
|
|
2116
|
+
if (body.dir)
|
|
2117
|
+
console.log(` ${c.dim('Directory:')} ${c.cyan(body.dir)}`);
|
|
2118
|
+
console.log(` ${c.dim('Next:')} ${c.cyan(`td plugin-hooks ${body.slug} install`)} ${c.dim('or use Settings > Detection Rules')}`);
|
|
2119
|
+
}
|
|
2120
|
+
async function runPluginList(json = false) {
|
|
1977
2121
|
const { baseUrl, token } = requireRunningServer();
|
|
1978
2122
|
const response = await getLocalJson(baseUrl, token, '/api/terminal/agent-plugins');
|
|
1979
2123
|
const body = JSON.parse(response.body || '{}');
|
|
@@ -1981,6 +2125,10 @@ async function runPluginList() {
|
|
|
1981
2125
|
console.error(`${ICON.err} ${c.red('Failed to list plugins')}`);
|
|
1982
2126
|
process.exit(1);
|
|
1983
2127
|
}
|
|
2128
|
+
if (json) {
|
|
2129
|
+
console.log(JSON.stringify(body, null, 2));
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
1984
2132
|
const plugins = body.plugins ?? [];
|
|
1985
2133
|
if (plugins.length === 0) {
|
|
1986
2134
|
console.log(`${c.dim('No plugins installed.')}`);
|
|
@@ -1999,7 +2147,11 @@ async function runPluginList() {
|
|
|
1999
2147
|
flags.push('resume');
|
|
2000
2148
|
if (p.iconMode !== 'mask')
|
|
2001
2149
|
flags.push(p.iconMode);
|
|
2150
|
+
if (p.updateAvailable)
|
|
2151
|
+
flags.push('update');
|
|
2002
2152
|
console.log(` ${c.cyan(p.slug)} ${c.dim(p.displayName)} ${c.gray(p.accentColor)} ${c.dim(flags.length > 0 ? `[${flags.join(', ')}]` : '')}`);
|
|
2153
|
+
if (p.source)
|
|
2154
|
+
console.log(` ${c.dim(`${p.sourceType ?? 'source'}: ${p.source}${p.revision ? ` @ ${p.revision.slice(0, 8)}` : ''}`)}`);
|
|
2003
2155
|
}
|
|
2004
2156
|
}
|
|
2005
2157
|
const errors = body.errors ?? [];
|
|
@@ -2017,6 +2169,37 @@ async function runPluginList() {
|
|
|
2017
2169
|
}
|
|
2018
2170
|
}
|
|
2019
2171
|
}
|
|
2172
|
+
async function runPluginSourceAction(slug, action) {
|
|
2173
|
+
const { baseUrl, token } = requireRunningServer();
|
|
2174
|
+
const response = await postLocalJson(baseUrl, token, `/api/terminal/agent-plugins/${encodeURIComponent(slug)}/${action}`, {});
|
|
2175
|
+
const body = JSON.parse(response.body || '{}');
|
|
2176
|
+
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
2177
|
+
console.error(`${ICON.err} ${c.red(body.error ?? `Plugin ${action} failed`)}`);
|
|
2178
|
+
process.exit(1);
|
|
2179
|
+
}
|
|
2180
|
+
if (action === 'check-update') {
|
|
2181
|
+
console.log(body.updateAvailable
|
|
2182
|
+
? `${ICON.warn} ${c.yellow(`Update available for ${slug}`)} ${c.dim(`${body.revision?.slice(0, 8) ?? '?'} → ${body.latestRevision?.slice(0, 8) ?? '?'}`)}`
|
|
2183
|
+
: `${ICON.ok} ${c.green(`${slug} is up to date.`)}`);
|
|
2184
|
+
}
|
|
2185
|
+
else {
|
|
2186
|
+
console.log(`${ICON.ok} ${c.green(`Plugin "${slug}" updated.`)}`);
|
|
2187
|
+
if (body.hookWarning)
|
|
2188
|
+
console.log(`${ICON.warn} ${c.yellow(`Hooks need attention: ${body.hookWarning}`)}`);
|
|
2189
|
+
if (body.titleWarning)
|
|
2190
|
+
console.log(`${ICON.warn} ${c.yellow(`Title commands need attention: ${body.titleWarning}`)}`);
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
async function runPluginHooks(slug, action) {
|
|
2194
|
+
const { baseUrl, token } = requireRunningServer();
|
|
2195
|
+
const response = await postLocalJson(baseUrl, token, `/api/terminal/agent-hooks/${encodeURIComponent(slug)}/${action}`, {});
|
|
2196
|
+
const body = JSON.parse(response.body || '{}');
|
|
2197
|
+
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
2198
|
+
console.error(`${ICON.err} ${c.red(body.error ?? `Plugin hook ${action} failed`)}`);
|
|
2199
|
+
process.exit(1);
|
|
2200
|
+
}
|
|
2201
|
+
console.log(`${ICON.ok} ${c.green(body.summary ?? `Hooks ${action === 'install' ? 'installed' : 'removed'}.`)}`);
|
|
2202
|
+
}
|
|
2020
2203
|
async function runPluginRemove(slug) {
|
|
2021
2204
|
const { baseUrl, token } = requireRunningServer();
|
|
2022
2205
|
const response = await new Promise((resolve, reject) => {
|
|
@@ -3009,8 +3192,36 @@ async function main() {
|
|
|
3009
3192
|
await runPluginCreate(options.pluginCreate);
|
|
3010
3193
|
process.exit(0);
|
|
3011
3194
|
}
|
|
3195
|
+
if (options.pluginInstall !== undefined) {
|
|
3196
|
+
if (!options.pluginInstall) {
|
|
3197
|
+
console.error(`${ICON.err} ${c.red('plugin-install requires an HTTPS Git URL or local package path')}`);
|
|
3198
|
+
process.exit(1);
|
|
3199
|
+
}
|
|
3200
|
+
await runPluginInstall(options.pluginInstall);
|
|
3201
|
+
process.exit(0);
|
|
3202
|
+
}
|
|
3012
3203
|
if (options.pluginList) {
|
|
3013
|
-
await runPluginList();
|
|
3204
|
+
await runPluginList(options.pluginListJson);
|
|
3205
|
+
process.exit(0);
|
|
3206
|
+
}
|
|
3207
|
+
if (options.pluginCheck !== undefined) {
|
|
3208
|
+
if (!options.pluginCheck) {
|
|
3209
|
+
console.error(`${ICON.err} ${c.red('plugin-check requires a plugin slug')}`);
|
|
3210
|
+
process.exit(1);
|
|
3211
|
+
}
|
|
3212
|
+
await runPluginSourceAction(options.pluginCheck, 'check-update');
|
|
3213
|
+
process.exit(0);
|
|
3214
|
+
}
|
|
3215
|
+
if (options.pluginUpdate !== undefined) {
|
|
3216
|
+
if (!options.pluginUpdate) {
|
|
3217
|
+
console.error(`${ICON.err} ${c.red('plugin-update requires a plugin slug')}`);
|
|
3218
|
+
process.exit(1);
|
|
3219
|
+
}
|
|
3220
|
+
await runPluginSourceAction(options.pluginUpdate, 'update');
|
|
3221
|
+
process.exit(0);
|
|
3222
|
+
}
|
|
3223
|
+
if (options.pluginHooks) {
|
|
3224
|
+
await runPluginHooks(options.pluginHooks.slug, options.pluginHooks.action);
|
|
3014
3225
|
process.exit(0);
|
|
3015
3226
|
}
|
|
3016
3227
|
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 保护)
|