termdock 1.4.4 → 1.4.6

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.
Files changed (68) hide show
  1. package/dist/client/assets/{abnfDiagram-VRR7QNED-l1RHVwtV.js → abnfDiagram-VRR7QNED-TJWKds8h.js} +1 -1
  2. package/dist/client/assets/{arc-DO19HDpA.js → arc-Bwq4gJDN.js} +1 -1
  3. package/dist/client/assets/{architectureDiagram-ZJ3FMSHR-CwkysCpv.js → architectureDiagram-ZJ3FMSHR-C_PaIGsp.js} +1 -1
  4. package/dist/client/assets/{blockDiagram-677ZJIJ3-BRctjbx4.js → blockDiagram-677ZJIJ3-DIQ1s4RG.js} +1 -1
  5. package/dist/client/assets/{c4Diagram-LMCZKHZV-CL1FBbko.js → c4Diagram-LMCZKHZV-Cw3HxHgb.js} +1 -1
  6. package/dist/client/assets/channel-DwhPbxV3.js +1 -0
  7. package/dist/client/assets/{chunk-2Q5K7J3B-Csh7Imvj.js → chunk-2Q5K7J3B-LMScDiKF.js} +1 -1
  8. package/dist/client/assets/{chunk-32BRIVSS-BbMLiuF9.js → chunk-32BRIVSS-Cgcdv9a_.js} +1 -1
  9. package/dist/client/assets/{chunk-5VM5RSS4-D48bXYje.js → chunk-5VM5RSS4-BN4q0bFP.js} +1 -1
  10. package/dist/client/assets/{chunk-EX3LRPZG-CZe-cckf.js → chunk-EX3LRPZG-f7v2lPuo.js} +1 -1
  11. package/dist/client/assets/{chunk-JWPE2WC7-Ciaj8hAC.js → chunk-JWPE2WC7-qRGfWvPl.js} +1 -1
  12. package/dist/client/assets/{chunk-MOJQB5TN-CBfP6hIj.js → chunk-MOJQB5TN-CcRVcjNk.js} +1 -1
  13. package/dist/client/assets/{chunk-RYQCIY6F-H-dwX8-8.js → chunk-RYQCIY6F-CiStzedH.js} +1 -1
  14. package/dist/client/assets/{chunk-V7JOEXUC-UyB55Fb4.js → chunk-V7JOEXUC-By8l1Yd1.js} +1 -1
  15. package/dist/client/assets/{chunk-VR4S4FIN-C-xnKkZn.js → chunk-VR4S4FIN-Cs9MYsyB.js} +1 -1
  16. package/dist/client/assets/{chunk-XXDRQBXY-DwM4iNfy.js → chunk-XXDRQBXY-BqymfWtI.js} +1 -1
  17. package/dist/client/assets/classDiagram-OUVF2IWQ-C5oIvoR8.js +1 -0
  18. package/dist/client/assets/classDiagram-v2-EOCWNBFH-C5oIvoR8.js +1 -0
  19. package/dist/client/assets/{cose-bilkent-JH36ORCC-DQ6wrpJd.js → cose-bilkent-JH36ORCC-D_ATAEN1.js} +1 -1
  20. package/dist/client/assets/{cynefin-VYW2F7L2-Du46UaXb.js → cynefin-VYW2F7L2-nMKGdMLr.js} +1 -1
  21. package/dist/client/assets/{cynefinDiagram-TSTJHNR4-BJmwLYq0.js → cynefinDiagram-TSTJHNR4-88CGto9V.js} +1 -1
  22. package/dist/client/assets/{dagre-VKFMJZFB-BKJvvyPu.js → dagre-VKFMJZFB-DbSHMc1E.js} +1 -1
  23. package/dist/client/assets/{diagram-FQU43EPY-BOVDpQ4_.js → diagram-FQU43EPY-CJ_zBTrl.js} +1 -1
  24. package/dist/client/assets/{diagram-G47NLZAW-D2kIGl0J.js → diagram-G47NLZAW-DBtAAmF_.js} +1 -1
  25. package/dist/client/assets/{diagram-NH7WQ7WH-wJmqwi0p.js → diagram-NH7WQ7WH-Bx0uckWy.js} +1 -1
  26. package/dist/client/assets/{diagram-OA4YK3LP-DUT1bEEW.js → diagram-OA4YK3LP-D5hbO3ke.js} +1 -1
  27. package/dist/client/assets/{diagram-WEI45ONY-Hzq15wub.js → diagram-WEI45ONY-DpBP-0oQ.js} +1 -1
  28. package/dist/client/assets/{ebnfDiagram-CCIWWBDH-BAywDP6W.js → ebnfDiagram-CCIWWBDH-DlWAndW4.js} +1 -1
  29. package/dist/client/assets/{erDiagram-Q63AITRT-B48EkAYi.js → erDiagram-Q63AITRT-Bigvvv40.js} +1 -1
  30. package/dist/client/assets/{flowDiagram-23GEKE2U-DMm-ZzDO.js → flowDiagram-23GEKE2U-HJ2jxf9M.js} +1 -1
  31. package/dist/client/assets/{ganttDiagram-NO4QXBWP-BQ-7Srcw.js → ganttDiagram-NO4QXBWP-ipKqpn0t.js} +1 -1
  32. package/dist/client/assets/{gitGraphDiagram-IHSO6WYX-BFmdjMso.js → gitGraphDiagram-IHSO6WYX-DBkaSbhB.js} +1 -1
  33. package/dist/client/assets/{index-uR9bCvK3.js → index-BH2cm4bW.js} +32 -32
  34. package/dist/client/assets/index-DF1hQ7NF.css +1 -0
  35. package/dist/client/assets/{infoDiagram-FWYZ7A6U-Bq5fgLNO.js → infoDiagram-FWYZ7A6U-DV7Vstzf.js} +1 -1
  36. package/dist/client/assets/{ishikawaDiagram-FXEZZL3T-DmLvJJcG.js → ishikawaDiagram-FXEZZL3T-Doar0fdM.js} +1 -1
  37. package/dist/client/assets/{journeyDiagram-5HDEW3XC-BEiSOBlj.js → journeyDiagram-5HDEW3XC-DJF-AHMR.js} +1 -1
  38. package/dist/client/assets/{kanban-definition-HUTT4EX6-tMWJSPAz.js → kanban-definition-HUTT4EX6-DOMPu4eG.js} +1 -1
  39. package/dist/client/assets/{linear-OUqi4t3W.js → linear-CncZrcBd.js} +1 -1
  40. package/dist/client/assets/{mermaid.core-D4Ax3qLz.js → mermaid.core-BGdwVdTT.js} +4 -4
  41. package/dist/client/assets/{mindmap-definition-LN4V7U3C-Dvz7Ium8.js → mindmap-definition-LN4V7U3C-Dx2SDF8J.js} +1 -1
  42. package/dist/client/assets/{pegDiagram-2B236MQR-Bkq7I7zD.js → pegDiagram-2B236MQR-CHGsPWYg.js} +1 -1
  43. package/dist/client/assets/{pieDiagram-ENE6RG2P-BbeI6TNI.js → pieDiagram-ENE6RG2P-CqsHXeDH.js} +1 -1
  44. package/dist/client/assets/{quadrantDiagram-ABIIQ3AL-BuzU3JxG.js → quadrantDiagram-ABIIQ3AL-Db61n-HD.js} +1 -1
  45. package/dist/client/assets/{railroadDiagram-RFXS5EU6-Bh48nt62.js → railroadDiagram-RFXS5EU6-DJH0mG1M.js} +1 -1
  46. package/dist/client/assets/{requirementDiagram-TGXJPOKE-D6K-3boL.js → requirementDiagram-TGXJPOKE-BZ_At6m6.js} +1 -1
  47. package/dist/client/assets/{sankeyDiagram-HTMAVEWB-xJQM56y0.js → sankeyDiagram-HTMAVEWB-CsNxLNrA.js} +1 -1
  48. package/dist/client/assets/{sequenceDiagram-DBY2YBRQ-CkiaqqKF.js → sequenceDiagram-DBY2YBRQ-qnZzQ6rT.js} +1 -1
  49. package/dist/client/assets/{sizeCapture-X5ZJPWSS-BRq8N0Fv.js → sizeCapture-X5ZJPWSS-BMeAZbXl.js} +1 -1
  50. package/dist/client/assets/{stateDiagram-2N3HPSRC-O9EoMMzY.js → stateDiagram-2N3HPSRC-BlnkxwAF.js} +1 -1
  51. package/dist/client/assets/stateDiagram-v2-6OUMAXLB-D7FRP5LD.js +1 -0
  52. package/dist/client/assets/{swimlanes-5IMT3BWC-BD_Or2Wo.js → swimlanes-5IMT3BWC-BcKd981V.js} +2 -2
  53. package/dist/client/assets/swimlanesDiagram-G3AALYLV-6XCfgXZ4.js +8 -0
  54. package/dist/client/assets/{timeline-definition-FHXFAJF6-Dmdy1Wn0.js → timeline-definition-FHXFAJF6-m8_M9iJb.js} +1 -1
  55. package/dist/client/assets/{vennDiagram-L72KCM5P-BnaYCKIs.js → vennDiagram-L72KCM5P-QUhrT66B.js} +1 -1
  56. package/dist/client/assets/{wardleyDiagram-EHGQE667-Vmk5-bqO.js → wardleyDiagram-EHGQE667-Bkj5mPbW.js} +1 -1
  57. package/dist/client/assets/{xychartDiagram-FW5EYKEG-B4t6sTC6.js → xychartDiagram-FW5EYKEG-CJTY9c9m.js} +1 -1
  58. package/dist/client/index.html +2 -2
  59. package/dist/client/sw.js +1 -1
  60. package/dist/server/cli.js +348 -1
  61. package/dist/server/routes/terminal.js +8 -2
  62. package/package.json +1 -1
  63. package/dist/client/assets/channel-CD9EYHvd.js +0 -1
  64. package/dist/client/assets/classDiagram-OUVF2IWQ-D46YHcok.js +0 -1
  65. package/dist/client/assets/classDiagram-v2-EOCWNBFH-D46YHcok.js +0 -1
  66. package/dist/client/assets/index-DgG9nafK.css +0 -1
  67. package/dist/client/assets/stateDiagram-v2-6OUMAXLB-B8vFTMC3.js +0 -1
  68. package/dist/client/assets/swimlanesDiagram-G3AALYLV-Bs0H1_c3.js +0 -8
@@ -147,10 +147,22 @@ Options:
147
147
  Print one hunk's diff/context by Termdock hunk ID.
148
148
  --change-audit-explain <id> [cwd]
149
149
  Read a natural-language explanation from stdin and inject
150
- it for the specified Termdock hunk ID.
150
+ it for the specified Termdock hunk ID.
151
+ --plugin-init Print the agent plugin manifest schema reference
152
+ --plugin-create <file>
153
+ Create a plugin from a manifest JSON file
154
+ --plugin-list List installed agent plugins
155
+ --plugin-remove <slug>
156
+ Remove an installed agent plugin
151
157
  -h, --help Show this help message
152
158
 
153
159
  Short commands:
160
+ pi Same as --plugin-init
161
+ plugin-create <file>
162
+ Same as --plugin-create <file>
163
+ plugin-list Same as --plugin-list
164
+ plugin-remove <slug>
165
+ Same as --plugin-remove <slug>
154
166
  s Same as --status
155
167
  st Same as --status
156
168
  x Same as --stop
@@ -466,6 +478,10 @@ function parseArgs(argv) {
466
478
  let changeAuditList;
467
479
  let changeAuditShow;
468
480
  let injectChangeAuditHunk;
481
+ let pluginInit = false;
482
+ let pluginCreate;
483
+ let pluginList = false;
484
+ let pluginRemove;
469
485
  // Short command aliases for the common path. Keep these positional-only so
470
486
  // long-form flags remain the single source of truth for option semantics.
471
487
  if (argv[0] && !argv[0].startsWith('-')) {
@@ -561,6 +577,34 @@ function parseArgs(argv) {
561
577
  injectChangeAuditHunk = { id: next, cwd };
562
578
  argv = argv.slice(cwd ? 3 : 2);
563
579
  }
580
+ else if (command === 'pi' || command === 'plugin-init') {
581
+ pluginInit = true;
582
+ argv = argv.slice(1);
583
+ }
584
+ else if (command === 'pcreate' || command === 'plugin-create') {
585
+ if (next && !next.startsWith('-')) {
586
+ pluginCreate = next;
587
+ argv = argv.slice(2);
588
+ }
589
+ else {
590
+ pluginCreate = undefined;
591
+ argv = argv.slice(1);
592
+ }
593
+ }
594
+ else if (command === 'pinfo' || command === 'plugin-list') {
595
+ pluginList = true;
596
+ argv = argv.slice(1);
597
+ }
598
+ else if (command === 'premove' || command === 'plugin-remove') {
599
+ if (next && !next.startsWith('-')) {
600
+ pluginRemove = next;
601
+ argv = argv.slice(2);
602
+ }
603
+ else {
604
+ pluginRemove = undefined;
605
+ argv = argv.slice(1);
606
+ }
607
+ }
564
608
  }
565
609
  for (let index = 0; index < argv.length; index += 1) {
566
610
  const arg = argv[index];
@@ -737,6 +781,36 @@ function parseArgs(argv) {
737
781
  index += cwd ? 2 : 1;
738
782
  continue;
739
783
  }
784
+ if (arg === '--plugin-init') {
785
+ pluginInit = true;
786
+ continue;
787
+ }
788
+ if (arg === '--plugin-create') {
789
+ const next = argv[index + 1];
790
+ if (next && !next.startsWith('-')) {
791
+ pluginCreate = next;
792
+ index += 1;
793
+ }
794
+ else {
795
+ pluginCreate = undefined;
796
+ }
797
+ continue;
798
+ }
799
+ if (arg === '--plugin-list') {
800
+ pluginList = true;
801
+ continue;
802
+ }
803
+ if (arg === '--plugin-remove') {
804
+ const next = argv[index + 1];
805
+ if (next && !next.startsWith('-')) {
806
+ pluginRemove = next;
807
+ index += 1;
808
+ }
809
+ else {
810
+ pluginRemove = undefined;
811
+ }
812
+ continue;
813
+ }
740
814
  if (!arg.startsWith('-')) {
741
815
  if (attachTmux && !attachTmuxName) {
742
816
  attachTmuxName = arg;
@@ -778,6 +852,10 @@ function parseArgs(argv) {
778
852
  changeAuditList,
779
853
  changeAuditShow,
780
854
  injectChangeAuditHunk,
855
+ pluginInit,
856
+ pluginCreate,
857
+ pluginList,
858
+ pluginRemove,
781
859
  };
782
860
  }
783
861
  // Reads a single line from stdin without echoing keystrokes. Used for password
@@ -936,6 +1014,32 @@ async function postLocalJson(baseUrl, token, endpoint, payload) {
936
1014
  req.end();
937
1015
  });
938
1016
  }
1017
+ async function getLocalJson(baseUrl, token, endpoint) {
1018
+ const url = new URL(endpoint, baseUrl);
1019
+ const isHttps = url.protocol === 'https:';
1020
+ const requestImpl = isHttps ? https.request : http.request;
1021
+ const ca = isHttps && fs.existsSync(defaultHttpsCaPath) ? fs.readFileSync(defaultHttpsCaPath) : undefined;
1022
+ return new Promise((resolve, reject) => {
1023
+ const req = requestImpl({
1024
+ protocol: url.protocol,
1025
+ hostname: url.hostname,
1026
+ port: url.port,
1027
+ path: `${url.pathname}${url.search}`,
1028
+ method: 'GET',
1029
+ ca,
1030
+ headers: {
1031
+ 'X-Termdock-Local-Token': token,
1032
+ },
1033
+ }, (res) => {
1034
+ let responseBody = '';
1035
+ res.setEncoding('utf8');
1036
+ res.on('data', (chunk) => { responseBody += chunk; });
1037
+ res.on('end', () => resolve({ statusCode: res.statusCode ?? 0, body: responseBody }));
1038
+ });
1039
+ req.on('error', reject);
1040
+ req.end();
1041
+ });
1042
+ }
939
1043
  async function runInjectChangeAudit(source) {
940
1044
  const raw = source === true
941
1045
  ? await readStdinText()
@@ -1476,6 +1580,225 @@ async function runInjectChangeAuditHunk(request) {
1476
1580
  };
1477
1581
  await postChangeAuditPayload(payload);
1478
1582
  }
1583
+ // ── Agent plugin CLI commands ──
1584
+ function resolveServerBaseUrl(state) {
1585
+ return state.localUrl ?? `${state.scheme ?? 'http'}://${state.host === '0.0.0.0' ? 'localhost' : state.host}:${state.port}`;
1586
+ }
1587
+ function requireRunningServer() {
1588
+ const state = getRunningState();
1589
+ if (!state) {
1590
+ console.error(`${ICON.err} ${c.red('Termdock is not running. Start it first: td')}`);
1591
+ process.exit(1);
1592
+ }
1593
+ const token = state.localApiToken;
1594
+ if (!token) {
1595
+ console.error(`${ICON.err} ${c.red('Running server has no local API token. Restart Termdock first.')}`);
1596
+ process.exit(1);
1597
+ }
1598
+ return { baseUrl: resolveServerBaseUrl(state), token };
1599
+ }
1600
+ function runPluginInit() {
1601
+ console.log(`\
1602
+ ${c.bold('Agent Plugin Reference')}
1603
+ ${c.dim('─────────────────────')}
1604
+
1605
+ A plugin is a directory under ${c.cyan('~/.termdock/agent-plugins/<slug>/')} containing:
1606
+
1607
+ ${c.cyan('manifest.json')} required – agent identity, hooks, and resume config
1608
+ ${c.cyan('icon.svg')} optional – brand icon (rendered as mask silhouette or native)
1609
+
1610
+ ${c.bold('manifest.json schema')}
1611
+ ${c.dim('───────────────────')}
1612
+
1613
+ {
1614
+ ${c.dim('// Manifest version. Must be')} ${c.green('1')}${c.dim('.')}
1615
+ "version": 1,
1616
+
1617
+ ${c.dim('// Unique slug (lowercase alphanum + hyphens, max 40 chars).')}
1618
+ ${c.dim('// Must not collide with built-in agent slugs.')}
1619
+ "slug": "trae",
1620
+
1621
+ ${c.dim('// Human-readable label shown in tabs and settings.')}
1622
+ "displayName": "Trae Agent",
1623
+
1624
+ ${c.dim('// Command names that identify this agent at runtime.')}
1625
+ ${c.dim('// First alias is the primary binary name; extras match npm/pip wrappers.')}
1626
+ "aliases": ["trae", "traecli"],
1627
+
1628
+ ${c.dim('// Brand accent color as 6-digit hex. Used for avatar background in mask mode,')}
1629
+ ${c.dim('// and as the status-dot color. Must be Flexoki-family or brand theme-safe.')}
1630
+ "accentColor": "#4385BE",
1631
+
1632
+ ${c.dim('// ── optional ──')}
1633
+ ${c.dim('// Icon rendering mode (default "mask"):')}
1634
+ ${c.dim('// "mask" - SVG is a white-on-accent silhouette (CSS mask). Mono-color.')}
1635
+ ${c.dim('// "native" - SVG rendered directly, preserving original fills/gradients.')}
1636
+ "iconMode": "native",
1637
+
1638
+ ${c.dim('// ── optional: hook installation (bring your own hooks.json surface) ──')}
1639
+ "hooks": {
1640
+ ${c.dim("// Path to the agent's hooks config file (~-abbreviated).")}
1641
+ ${c.dim('// termdock inserts marker-carrying entries; leaves user entries untouched.')}
1642
+ "target": "~/.trae/settings.json",
1643
+
1644
+ ${c.dim('// Event mappings: agent-native hook name → termdock sentinel event.')}
1645
+ ${c.dim('// Optional matcher narrows the subscription (e.g. permission type).')}
1646
+ "events": [
1647
+ { "hook": "SessionStart", "event": "session-start" },
1648
+ { "hook": "UserPromptSubmit", "event": "prompt-submit" },
1649
+ { "hook": "Notification", "event": "notification", "matcher": "elicitation_dialog" },
1650
+ { "hook": "PostToolUse", "event": "tool-complete" },
1651
+ { "hook": "Stop", "event": "stop" },
1652
+ { "hook": "SessionEnd", "event": "session-end" }
1653
+ ]
1654
+ },
1655
+
1656
+ ${c.dim('// ── optional: resume command (for session picker re-entry) ──')}
1657
+ "resume": {
1658
+ ${c.dim('// Shell command template. {sessionId} is replaced with the native session id.')}
1659
+ "command": "trae --resume {sessionId}",
1660
+ ${c.dim('// Flags to strip from the original launch argv when replaying the resume.')}
1661
+ "staleFlags": ["--resume", "-r", "--session-id", "--continue"]
1662
+ }
1663
+ }
1664
+
1665
+ ${c.bold('Sentinel event kinds')}
1666
+ ${c.dim('─────────────────────')}
1667
+
1668
+ session-start Agent session opened (binary started / first prompt)
1669
+ prompt-submit A new prompt/turn was submitted
1670
+ permission-request Agent is blocked waiting for a tool-permission grant
1671
+ question-asked Agent asked the user a question (elicitation dialog)
1672
+ tool-complete A tool execution completed (unblocks waiting state)
1673
+ notification Generic notification from the agent (heuristic gated)
1674
+ stop Agent turn finished / went idle
1675
+ session-end Agent session closed / binary exited
1676
+
1677
+ ${c.bold('Creating a plugin')}
1678
+ ${c.dim('──────────────────')}
1679
+
1680
+ # 1. Create the plugin directory with manifest + icon
1681
+ mkdir -p ~/.termdock/agent-plugins/trae
1682
+ cp manifest.json icon.svg ~/.termdock/agent-plugins/trae/
1683
+
1684
+ ${c.dim('# 2. Install the plugin via CLI (needs running server)')}
1685
+ td plugin-create ~/.termdock/agent-plugins/trae/manifest.json
1686
+
1687
+ ${c.dim('# 3. Or create directly from the manifest JSON')}
1688
+ td plugin-create ./trae-manifest.json
1689
+
1690
+ ${c.dim('# 4. List installed plugins')}
1691
+ td plugin-list
1692
+
1693
+ ${c.dim('# 5. Remove a plugin')}
1694
+ td plugin-remove trae
1695
+
1696
+ ${c.bold('Manifest validation at startup')}
1697
+ ${c.dim('───────────────────────────────')}
1698
+
1699
+ Termdock validates every plugin manifest on server start. Errors are logged
1700
+ to ${c.cyan('~/.termdock/server.log')} and visible via ${c.cyan('td plugin-list')}${c.dim('.')}
1701
+ Slugs/aliases that collide with built-in agents are skipped.
1702
+ `);
1703
+ }
1704
+ async function runPluginCreate(manifestPath) {
1705
+ const resolved = path.resolve(manifestPath);
1706
+ let raw;
1707
+ try {
1708
+ raw = fs.readFileSync(resolved, 'utf8');
1709
+ }
1710
+ catch {
1711
+ console.error(`${ICON.err} ${c.red(`Cannot read manifest file: ${resolved}`)}`);
1712
+ process.exit(1);
1713
+ }
1714
+ let manifest;
1715
+ try {
1716
+ manifest = JSON.parse(raw);
1717
+ }
1718
+ catch (error) {
1719
+ console.error(`${ICON.err} ${c.red(`Invalid JSON in ${resolved}: ${error.message}`)}`);
1720
+ process.exit(1);
1721
+ }
1722
+ const { baseUrl, token } = requireRunningServer();
1723
+ const response = await postLocalJson(baseUrl, token, '/api/terminal/agent-plugins', manifest);
1724
+ const body = JSON.parse(response.body || '{}');
1725
+ if (response.statusCode < 200 || response.statusCode >= 300) {
1726
+ console.error(`${ICON.err} ${c.red(body.error || 'Failed to create plugin')}`);
1727
+ process.exit(1);
1728
+ }
1729
+ console.log(`${ICON.ok} ${c.green(`Plugin "${body.slug}" created.`)}`);
1730
+ console.log(` ${c.dim('Directory:')} ${c.cyan(body.dir ?? resolved)}`);
1731
+ console.log(` ${c.dim('Restart Termdock to activate:')} ${c.cyan('td --stop && td')}`);
1732
+ }
1733
+ async function runPluginList() {
1734
+ const { baseUrl, token } = requireRunningServer();
1735
+ const response = await getLocalJson(baseUrl, token, '/api/terminal/agent-plugins');
1736
+ const body = JSON.parse(response.body || '{}');
1737
+ if (response.statusCode < 200 || response.statusCode >= 300) {
1738
+ console.error(`${ICON.err} ${c.red('Failed to list plugins')}`);
1739
+ process.exit(1);
1740
+ }
1741
+ const plugins = body.plugins ?? [];
1742
+ if (plugins.length === 0) {
1743
+ console.log(`${c.dim('No plugins installed.')}`);
1744
+ console.log(` ${c.dim('See:')} ${c.cyan('td plugin-init')}`);
1745
+ }
1746
+ else {
1747
+ console.log(`${c.bold('Installed plugins')} ${c.dim(`(${plugins.length})`)}`);
1748
+ console.log('');
1749
+ for (const p of plugins) {
1750
+ const flags = [];
1751
+ if (p.hasIcon)
1752
+ flags.push('icon');
1753
+ if (p.hasHooks)
1754
+ flags.push('hooks');
1755
+ if (p.hasResume)
1756
+ flags.push('resume');
1757
+ if (p.iconMode !== 'mask')
1758
+ flags.push(p.iconMode);
1759
+ console.log(` ${c.cyan(p.slug)} ${c.dim(p.displayName)} ${c.gray(p.accentColor)} ${c.dim(flags.length > 0 ? `[${flags.join(', ')}]` : '')}`);
1760
+ }
1761
+ }
1762
+ const errors = body.errors ?? [];
1763
+ if (errors.length > 0) {
1764
+ console.log('');
1765
+ console.log(`${c.yellow('Validation errors')}`);
1766
+ for (const e of errors) {
1767
+ console.log(` ${c.red('✗')} ${c.cyan(e.slug)}${c.dim(':')} ${e.errors.join('; ')}`);
1768
+ }
1769
+ }
1770
+ }
1771
+ async function runPluginRemove(slug) {
1772
+ const { baseUrl, token } = requireRunningServer();
1773
+ const response = await new Promise((resolve, reject) => {
1774
+ const url = new URL(`/api/terminal/agent-plugins/${encodeURIComponent(slug)}`, baseUrl);
1775
+ const isHttps = url.protocol === 'https:';
1776
+ const requestImpl = isHttps ? https.request : http.request;
1777
+ const ca = isHttps && fs.existsSync(defaultHttpsCaPath) ? fs.readFileSync(defaultHttpsCaPath) : undefined;
1778
+ const req = requestImpl({
1779
+ protocol: url.protocol,
1780
+ hostname: url.hostname,
1781
+ port: url.port,
1782
+ path: url.pathname,
1783
+ method: 'DELETE',
1784
+ ca,
1785
+ headers: { 'X-Termdock-Local-Token': token },
1786
+ }, (res) => {
1787
+ let responseBody = '';
1788
+ res.setEncoding('utf8');
1789
+ res.on('data', (chunk) => { responseBody += chunk; });
1790
+ res.on('end', () => resolve({ statusCode: res.statusCode ?? 0, body: responseBody }));
1791
+ });
1792
+ req.on('error', reject);
1793
+ req.end();
1794
+ });
1795
+ const body = JSON.parse(response.body || '{}');
1796
+ if (response.statusCode < 200 || response.statusCode >= 300) {
1797
+ console.error(`${ICON.err} ${c.red(body.error || `Failed to remove plugin "${slug}"`)}`);
1798
+ process.exit(1);
1799
+ }
1800
+ console.log(`${ICON.ok} ${c.green(`Plugin "${body.slug ?? slug}" removed.`)}`);
1801
+ }
1479
1802
  const options = parseArgs(process.argv.slice(2));
1480
1803
  if (options.setPassword && options.clearPassword) {
1481
1804
  console.error(`${ICON.err} ${c.red('Cannot use --set-password and --clear-password together.')}`);
@@ -2375,6 +2698,30 @@ async function main() {
2375
2698
  runClearPassword();
2376
2699
  process.exit(0);
2377
2700
  }
2701
+ if (options.pluginInit) {
2702
+ runPluginInit();
2703
+ process.exit(0);
2704
+ }
2705
+ if (options.pluginCreate !== undefined) {
2706
+ if (!options.pluginCreate) {
2707
+ console.error(`${ICON.err} ${c.red('plugin-create requires a manifest file path')}`);
2708
+ process.exit(1);
2709
+ }
2710
+ await runPluginCreate(options.pluginCreate);
2711
+ process.exit(0);
2712
+ }
2713
+ if (options.pluginList) {
2714
+ await runPluginList();
2715
+ process.exit(0);
2716
+ }
2717
+ if (options.pluginRemove !== undefined) {
2718
+ if (!options.pluginRemove) {
2719
+ console.error(`${ICON.err} ${c.red('plugin-remove requires a plugin slug')}`);
2720
+ process.exit(1);
2721
+ }
2722
+ await runPluginRemove(options.pluginRemove);
2723
+ process.exit(0);
2724
+ }
2378
2725
  const localApiToken = getOrCreateLocalApiToken();
2379
2726
  await refreshDefaultHttpsCertificateSafely();
2380
2727
  const https = resolveHttpsOptions(options);
@@ -4573,8 +4573,14 @@ router.get('/:sessionId/health', (req, res) => {
4573
4573
  const { sessionId } = req.params;
4574
4574
  const session = terminalSessions.get(sessionId);
4575
4575
  if (!session) {
4576
- console.log(`Health check: session ${sessionId} not found`);
4577
- return res.status(404).json({ healthy: false, error: 'Session not found' });
4576
+ // TD 重启后 terminalSessions 清空,但从 globalSessionState(持久化到磁盘)
4577
+ // 可恢复 mode / tmuxSessionName,避免前端把 tmux 会话误重建为 shell。
4578
+ const persistedRecord = globalSessionState.sessions.find((s) => s.backendSessionId === sessionId) ?? null;
4579
+ const recovered = persistedRecord
4580
+ ? { mode: persistedRecord.mode, tmuxSessionName: persistedRecord.tmuxSessionName }
4581
+ : {};
4582
+ console.log(`Health check: session ${sessionId} not found${persistedRecord ? `, recovered mode=${persistedRecord.mode} from persisted state` : ''}`);
4583
+ return res.status(404).json({ healthy: false, error: 'Session not found', ...recovered });
4578
4584
  }
4579
4585
  console.log(`Health check: session ${sessionId} healthy, cwd=${session.cwd}, clients=${getTotalClients(sessionId)}, lastActivity=${Date.now() - session.lastActivity}ms ago`);
4580
4586
  res.json({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "termdock",
3
- "version": "1.4.4",
3
+ "version": "1.4.6",
4
4
  "description": "A complete web-based terminal application with modern UI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +0,0 @@
1
- import{U as a,C as n}from"./mermaid.core-D4Ax3qLz.js";const t=(r,o)=>a.lang.round(n.parse(r)[o]);export{t as c};
@@ -1 +0,0 @@
1
- import{s as a,a as s,c as t,C as e}from"./chunk-V7JOEXUC-UyB55Fb4.js";import{b as i}from"./mermaid.core-D4Ax3qLz.js";import"./chunk-5VM5RSS4-D48bXYje.js";import"./chunk-XXDRQBXY-DwM4iNfy.js";import"./chunk-VR4S4FIN-C-xnKkZn.js";import"./chunk-32BRIVSS-BbMLiuF9.js";import"./purify.es-CC4Brkr_.js";import"./index-uR9bCvK3.js";import"./react-XDCjal2T.js";import"./dnd-BCFAPC0L.js";import"./terminal-C7X98Amf.js";var g={parser:t,get db(){return new e},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{g as diagram};
@@ -1 +0,0 @@
1
- import{s as a,a as s,c as t,C as e}from"./chunk-V7JOEXUC-UyB55Fb4.js";import{b as i}from"./mermaid.core-D4Ax3qLz.js";import"./chunk-5VM5RSS4-D48bXYje.js";import"./chunk-XXDRQBXY-DwM4iNfy.js";import"./chunk-VR4S4FIN-C-xnKkZn.js";import"./chunk-32BRIVSS-BbMLiuF9.js";import"./purify.es-CC4Brkr_.js";import"./index-uR9bCvK3.js";import"./react-XDCjal2T.js";import"./dnd-BCFAPC0L.js";import"./terminal-C7X98Amf.js";var g={parser:t,get db(){return new e},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{g as diagram};