termdock 1.4.71 → 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-CG6vLwyw.js → ModelPreview-i9dS1A3I.js} +1 -1
- package/dist/client/assets/{abnfDiagram-VRR7QNED-CrXEZLWX.js → abnfDiagram-VRR7QNED-Cs-ty-HA.js} +1 -1
- package/dist/client/assets/{arc-DbKGR7Ex.js → arc-CIX5k_JD.js} +1 -1
- package/dist/client/assets/{architectureDiagram-ZJ3FMSHR-PF5BoUbI.js → architectureDiagram-ZJ3FMSHR-DnPnQoJy.js} +1 -1
- package/dist/client/assets/{blockDiagram-677ZJIJ3-D1gRzVWB.js → blockDiagram-677ZJIJ3-Dhew-L5v.js} +1 -1
- package/dist/client/assets/{c4Diagram-LMCZKHZV-CwvOLync.js → c4Diagram-LMCZKHZV-BSsEMeWn.js} +1 -1
- package/dist/client/assets/channel-CASnt1pP.js +1 -0
- package/dist/client/assets/{chunk-2Q5K7J3B-BeRdP-xP.js → chunk-2Q5K7J3B-BWm--1Mo.js} +1 -1
- package/dist/client/assets/{chunk-32BRIVSS-CrqR5rvl.js → chunk-32BRIVSS-D_WFmuvb.js} +1 -1
- package/dist/client/assets/{chunk-5VM5RSS4-CjbUr-ko.js → chunk-5VM5RSS4-CQdzqlQg.js} +1 -1
- package/dist/client/assets/{chunk-EX3LRPZG-BVEk3Wma.js → chunk-EX3LRPZG-Cm3zLQpt.js} +1 -1
- package/dist/client/assets/{chunk-JWPE2WC7-WitJe8kR.js → chunk-JWPE2WC7-Cmcv5sEN.js} +1 -1
- package/dist/client/assets/{chunk-MOJQB5TN-BCewlLYk.js → chunk-MOJQB5TN-Bam44EwJ.js} +1 -1
- package/dist/client/assets/{chunk-RYQCIY6F-CoP0AA4G.js → chunk-RYQCIY6F-DIVOuE8y.js} +1 -1
- package/dist/client/assets/{chunk-V7JOEXUC-CSZrnmp_.js → chunk-V7JOEXUC-BfOTf7yg.js} +1 -1
- package/dist/client/assets/{chunk-VR4S4FIN-BB-7zz-9.js → chunk-VR4S4FIN-DbiXn0dd.js} +1 -1
- package/dist/client/assets/{chunk-XXDRQBXY-CcZZlCik.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-DcmzUDwh.js → cose-bilkent-JH36ORCC-CCoSPmoc.js} +1 -1
- package/dist/client/assets/{cynefin-VYW2F7L2-C1XlDVoJ.js → cynefin-VYW2F7L2-B2lYz7sl.js} +1 -1
- package/dist/client/assets/{cynefinDiagram-TSTJHNR4-zY6Aet2S.js → cynefinDiagram-TSTJHNR4-Cd7crN9s.js} +1 -1
- package/dist/client/assets/{dagre-VKFMJZFB-DqGTi6zR.js → dagre-VKFMJZFB-q9Fj9HuQ.js} +1 -1
- package/dist/client/assets/{diagram-FQU43EPY-DStcsU7S.js → diagram-FQU43EPY-iqHy5YHW.js} +1 -1
- package/dist/client/assets/{diagram-G47NLZAW-Cs6IMNip.js → diagram-G47NLZAW-BE-jlh1B.js} +1 -1
- package/dist/client/assets/{diagram-NH7WQ7WH-yA2bpWN7.js → diagram-NH7WQ7WH-DmP7_WOC.js} +1 -1
- package/dist/client/assets/{diagram-OA4YK3LP-Dzf9M0PI.js → diagram-OA4YK3LP-CHx_k8Ar.js} +1 -1
- package/dist/client/assets/{diagram-WEI45ONY-D75lpQKA.js → diagram-WEI45ONY-D0wImpAF.js} +1 -1
- package/dist/client/assets/{ebnfDiagram-CCIWWBDH-CkQvIpWy.js → ebnfDiagram-CCIWWBDH-DyCB0rF8.js} +1 -1
- package/dist/client/assets/{erDiagram-Q63AITRT-Ca00Vjxz.js → erDiagram-Q63AITRT-SRcd1B2Z.js} +1 -1
- package/dist/client/assets/{flowDiagram-23GEKE2U-CGrnSTEp.js → flowDiagram-23GEKE2U-D0i9mFwt.js} +1 -1
- package/dist/client/assets/{ganttDiagram-NO4QXBWP-0SHfOIP4.js → ganttDiagram-NO4QXBWP-BfwGIYDn.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-IHSO6WYX-BeigYWvs.js → gitGraphDiagram-IHSO6WYX-BXkJQP7O.js} +1 -1
- package/dist/client/assets/index-BA-DqJM2.js +451 -0
- package/dist/client/assets/{infoDiagram-FWYZ7A6U-CiY1-xjN.js → infoDiagram-FWYZ7A6U-CM-Ebv9R.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-FXEZZL3T-CmtDjUm_.js → ishikawaDiagram-FXEZZL3T-U-qnP7xi.js} +1 -1
- package/dist/client/assets/{journeyDiagram-5HDEW3XC-B7qOr9LY.js → journeyDiagram-5HDEW3XC-D4RxvsuL.js} +1 -1
- package/dist/client/assets/{kanban-definition-HUTT4EX6-DO2P2Um3.js → kanban-definition-HUTT4EX6-Dm_asCYK.js} +1 -1
- package/dist/client/assets/{linear-B832hJt0.js → linear-Zhe8d635.js} +1 -1
- package/dist/client/assets/{mermaid.core-CsAzdiCU.js → mermaid.core-D_NWIuMY.js} +4 -4
- package/dist/client/assets/{mindmap-definition-LN4V7U3C-HI__vXhQ.js → mindmap-definition-LN4V7U3C-DXpNidd5.js} +1 -1
- package/dist/client/assets/{pegDiagram-2B236MQR--rZIwjXq.js → pegDiagram-2B236MQR-DdOwOEgu.js} +1 -1
- package/dist/client/assets/{pieDiagram-ENE6RG2P-D9jPf5x9.js → pieDiagram-ENE6RG2P-D_A9HoY_.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-ABIIQ3AL-DTCP5ji2.js → quadrantDiagram-ABIIQ3AL-D-Y3cLEF.js} +1 -1
- package/dist/client/assets/{railroadDiagram-RFXS5EU6-D2Gspvrl.js → railroadDiagram-RFXS5EU6-DD6eFV82.js} +1 -1
- package/dist/client/assets/{requirementDiagram-TGXJPOKE-DEJC02IW.js → requirementDiagram-TGXJPOKE-B59HJgL2.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-HTMAVEWB-opOhKPap.js → sankeyDiagram-HTMAVEWB-C2BvHMH8.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-DBY2YBRQ-_sz2CRAG.js → sequenceDiagram-DBY2YBRQ-3004tCRm.js} +1 -1
- package/dist/client/assets/{sizeCapture-X5ZJPWSS-D0Q2QkYF.js → sizeCapture-X5ZJPWSS-DCBhg8QW.js} +1 -1
- package/dist/client/assets/{stateDiagram-2N3HPSRC-VgeSQTtW.js → stateDiagram-2N3HPSRC-BVqiKVe0.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-6OUMAXLB-Z0GHWyoF.js +1 -0
- package/dist/client/assets/{swimlanes-5IMT3BWC-ClvBvSl0.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-BXNsrECV.js → timeline-definition-FHXFAJF6-wVOEmQwG.js} +1 -1
- package/dist/client/assets/{vennDiagram-L72KCM5P-BgwiqZd-.js → vennDiagram-L72KCM5P-DMZiSJDk.js} +1 -1
- package/dist/client/assets/{wardleyDiagram-EHGQE667-DiqN5ERx.js → wardleyDiagram-EHGQE667-Dx5Aob4Y.js} +1 -1
- package/dist/client/assets/{xychartDiagram-FW5EYKEG-DQjFVqC6.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 +3 -0
- package/dist/server/agent/pluginPackages.js +289 -0
- package/dist/server/agent/plugins.js +123 -19
- package/dist/server/agent/titleNamerCatalog.js +39 -8
- package/dist/server/cli.js +205 -14
- package/dist/server/entry.js +11 -2
- package/dist/server/routes/terminal.js +232 -29
- 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,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
|
};
|
|
@@ -1742,8 +1830,12 @@ function runPluginInitJson() {
|
|
|
1742
1830
|
commands: {
|
|
1743
1831
|
guide: 'td agent-plugin --json',
|
|
1744
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>',
|
|
1745
1836
|
emit: 'td agent-event <slug> <event> [status]',
|
|
1746
|
-
inspect: 'td plugin-list',
|
|
1837
|
+
inspect: 'td plugin-list --json',
|
|
1838
|
+
hooks: 'td plugin-hooks <slug> <install|uninstall>',
|
|
1747
1839
|
remove: 'td plugin-remove <slug>',
|
|
1748
1840
|
},
|
|
1749
1841
|
ui: {
|
|
@@ -1763,6 +1855,13 @@ function runPluginInitJson() {
|
|
|
1763
1855
|
indicator: 'Theme-safe visual indicator.',
|
|
1764
1856
|
tone: 'Theme token; arbitrary status colors are not accepted.',
|
|
1765
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
|
+
},
|
|
1766
1865
|
migrationV1: {
|
|
1767
1866
|
errorCode: 'AGENT_PLUGIN_MANIFEST_V1_UNSUPPORTED',
|
|
1768
1867
|
guideCommand: 'td agent-plugin --json',
|
|
@@ -1774,12 +1873,14 @@ function runPluginInitJson() {
|
|
|
1774
1873
|
example: 'printf \'%s\' \'{"session_id":"abc","message":"Indexing"}\' | td agent-event my-agent prompt-submit thinking',
|
|
1775
1874
|
},
|
|
1776
1875
|
integrationSteps: [
|
|
1777
|
-
'
|
|
1778
|
-
'
|
|
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.',
|
|
1779
1879
|
'Ask the user to open Settings > Detection Rules and click Install for this Agent. They can uninstall it there later.',
|
|
1780
1880
|
'If the Agent hook format is not a Claude-style hooks map, configure native hooks to call td agent-event directly.',
|
|
1781
1881
|
'Map the completed-turn hook to event "stop"; this is also the automatic-title trigger when the user enables titles for the Agent.',
|
|
1782
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.',
|
|
1783
1884
|
'Run td plugin-list to verify registration.',
|
|
1784
1885
|
],
|
|
1785
1886
|
manifest: AGENT_PLUGIN_EXAMPLE,
|
|
@@ -1862,7 +1963,7 @@ ${c.dim('───────────────────')}
|
|
|
1862
1963
|
|
|
1863
1964
|
${c.dim('// ── optional: hook installation (bring your own hooks.json surface) ──')}
|
|
1864
1965
|
"hooks": {
|
|
1865
|
-
${c.dim("//
|
|
1966
|
+
${c.dim("// JSON path under ~/ only; symlink traversal is rejected.")}
|
|
1866
1967
|
${c.dim('// termdock inserts marker-carrying entries; leaves user entries untouched.')}
|
|
1867
1968
|
"target": "~/.trae/settings.json",
|
|
1868
1969
|
|
|
@@ -1888,7 +1989,8 @@ ${c.dim('───────────────────')}
|
|
|
1888
1989
|
|
|
1889
1990
|
${c.dim('// ── optional: injectable automatic-title provider ──')}
|
|
1890
1991
|
"titleNamer": {
|
|
1891
|
-
${c.dim('// No shell expansion.
|
|
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.')}
|
|
1892
1994
|
"command": "trae",
|
|
1893
1995
|
"args": ["title", "--prompt", "{prompt}", "--model={model}"],
|
|
1894
1996
|
${c.dim('// Must print JSON: strings or {id, displayName, description, isDefault} objects.')}
|
|
@@ -1915,11 +2017,19 @@ ${c.dim('──────────────────')}
|
|
|
1915
2017
|
${c.dim('# Inject the Agent definition; it appears in the UI immediately')}
|
|
1916
2018
|
td plugin-create ./trae-manifest.json
|
|
1917
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
|
+
|
|
1918
2023
|
${c.dim('# The user then opens Settings > Detection Rules and clicks Install.')}
|
|
1919
2024
|
${c.dim('# The same UI button can uninstall the hooks later.')}
|
|
1920
2025
|
|
|
1921
2026
|
${c.dim('# Verify registration')}
|
|
1922
|
-
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
|
|
1923
2033
|
|
|
1924
2034
|
${c.dim('# Remove')}
|
|
1925
2035
|
td plugin-remove trae
|
|
@@ -1993,7 +2103,21 @@ async function runPluginCreate(manifestPath) {
|
|
|
1993
2103
|
console.log(` ${c.dim('Visible immediately in Settings > Detection Rules; no restart required.')}`);
|
|
1994
2104
|
console.log(` ${c.dim('The user can click Install or Uninstall there.')}`);
|
|
1995
2105
|
}
|
|
1996
|
-
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) {
|
|
1997
2121
|
const { baseUrl, token } = requireRunningServer();
|
|
1998
2122
|
const response = await getLocalJson(baseUrl, token, '/api/terminal/agent-plugins');
|
|
1999
2123
|
const body = JSON.parse(response.body || '{}');
|
|
@@ -2001,6 +2125,10 @@ async function runPluginList() {
|
|
|
2001
2125
|
console.error(`${ICON.err} ${c.red('Failed to list plugins')}`);
|
|
2002
2126
|
process.exit(1);
|
|
2003
2127
|
}
|
|
2128
|
+
if (json) {
|
|
2129
|
+
console.log(JSON.stringify(body, null, 2));
|
|
2130
|
+
return;
|
|
2131
|
+
}
|
|
2004
2132
|
const plugins = body.plugins ?? [];
|
|
2005
2133
|
if (plugins.length === 0) {
|
|
2006
2134
|
console.log(`${c.dim('No plugins installed.')}`);
|
|
@@ -2019,7 +2147,11 @@ async function runPluginList() {
|
|
|
2019
2147
|
flags.push('resume');
|
|
2020
2148
|
if (p.iconMode !== 'mask')
|
|
2021
2149
|
flags.push(p.iconMode);
|
|
2150
|
+
if (p.updateAvailable)
|
|
2151
|
+
flags.push('update');
|
|
2022
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)}` : ''}`)}`);
|
|
2023
2155
|
}
|
|
2024
2156
|
}
|
|
2025
2157
|
const errors = body.errors ?? [];
|
|
@@ -2037,6 +2169,37 @@ async function runPluginList() {
|
|
|
2037
2169
|
}
|
|
2038
2170
|
}
|
|
2039
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
|
+
}
|
|
2040
2203
|
async function runPluginRemove(slug) {
|
|
2041
2204
|
const { baseUrl, token } = requireRunningServer();
|
|
2042
2205
|
const response = await new Promise((resolve, reject) => {
|
|
@@ -3029,8 +3192,36 @@ async function main() {
|
|
|
3029
3192
|
await runPluginCreate(options.pluginCreate);
|
|
3030
3193
|
process.exit(0);
|
|
3031
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
|
+
}
|
|
3032
3203
|
if (options.pluginList) {
|
|
3033
|
-
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);
|
|
3034
3225
|
process.exit(0);
|
|
3035
3226
|
}
|
|
3036
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 保护)
|