tmex-cli 0.9.2 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/runtime/server.js +1988 -148
  3. package/package.json +4 -3
  4. package/resources/fe-dist/assets/DevicePage-fGI-do8S.js +26 -0
  5. package/resources/fe-dist/assets/DevicesPage-kGrzwhGj.js +2 -0
  6. package/resources/fe-dist/assets/FilePage-YWma96aj.js +16 -0
  7. package/resources/fe-dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  8. package/resources/fe-dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  9. package/resources/fe-dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  10. package/resources/fe-dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  11. package/resources/fe-dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  12. package/resources/fe-dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  13. package/resources/fe-dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  14. package/resources/fe-dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  15. package/resources/fe-dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  16. package/resources/fe-dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  17. package/resources/fe-dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  18. package/resources/fe-dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  19. package/resources/fe-dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  20. package/resources/fe-dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  21. package/resources/fe-dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  22. package/resources/fe-dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  23. package/resources/fe-dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  24. package/resources/fe-dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  25. package/resources/fe-dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  26. package/resources/fe-dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  27. package/resources/fe-dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  28. package/resources/fe-dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  29. package/resources/fe-dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  30. package/resources/fe-dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  31. package/resources/fe-dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  32. package/resources/fe-dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  33. package/resources/fe-dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  34. package/resources/fe-dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  35. package/resources/fe-dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  36. package/resources/fe-dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  37. package/resources/fe-dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  38. package/resources/fe-dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  39. package/resources/fe-dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  40. package/resources/fe-dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  41. package/resources/fe-dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  42. package/resources/fe-dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  43. package/resources/fe-dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  44. package/resources/fe-dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  45. package/resources/fe-dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  46. package/resources/fe-dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  47. package/resources/fe-dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  48. package/resources/fe-dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  49. package/resources/fe-dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  50. package/resources/fe-dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  51. package/resources/fe-dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  52. package/resources/fe-dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  53. package/resources/fe-dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  54. package/resources/fe-dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  55. package/resources/fe-dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  56. package/resources/fe-dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  57. package/resources/fe-dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  58. package/resources/fe-dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  59. package/resources/fe-dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  60. package/resources/fe-dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  61. package/resources/fe-dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  62. package/resources/fe-dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  63. package/resources/fe-dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  64. package/resources/fe-dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  65. package/resources/fe-dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  66. package/resources/fe-dist/assets/SettingsPage-LhUD03Ln.js +32 -0
  67. package/resources/fe-dist/assets/arc-DLVa298O.js +2 -0
  68. package/resources/fe-dist/assets/architectureDiagram-3BPJPVTR-EOUpuRby.js +37 -0
  69. package/resources/fe-dist/assets/blockDiagram-GPEHLZMM-BTwwkzZh.js +133 -0
  70. package/resources/fe-dist/assets/c4Diagram-AAUBKEIU-xvTqXxl9.js +11 -0
  71. package/resources/fe-dist/assets/card-cbVDy0Xp.js +2 -0
  72. package/resources/fe-dist/assets/channel-DbG6qNF6.js +2 -0
  73. package/resources/fe-dist/assets/chunk-2J33WTMH-Blr6l1Nk.js +2 -0
  74. package/resources/fe-dist/assets/chunk-4BX2VUAB-D_Y2TeLg.js +2 -0
  75. package/resources/fe-dist/assets/chunk-55IACEB6-CimNoYDH.js +2 -0
  76. package/resources/fe-dist/assets/chunk-727SXJPM-JPYDN0Cb.js +207 -0
  77. package/resources/fe-dist/assets/chunk-AQP2D5EJ-Cb3NZBib.js +232 -0
  78. package/resources/fe-dist/assets/chunk-FMBD7UC4-DyZ7Ot2F.js +16 -0
  79. package/resources/fe-dist/assets/chunk-ND2GUHAM-SU6Ea3wF.js +2 -0
  80. package/resources/fe-dist/assets/chunk-QZHKN3VN-B6qXX6sU.js +2 -0
  81. package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-rB1DgxOZ.js +2 -0
  82. package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-rB1DgxOZ.js +2 -0
  83. package/resources/fe-dist/assets/cose-bilkent-S5V4N54A-BAyMopHS.js +2 -0
  84. package/resources/fe-dist/assets/cytoscape.esm-DTSO7Bv0.js +332 -0
  85. package/resources/fe-dist/assets/dagre-BM42HDAG-Ckrx9BXZ.js +5 -0
  86. package/resources/fe-dist/assets/defaultLocale-DX6XiGOO.js +2 -0
  87. package/resources/fe-dist/assets/diagram-2AECGRRQ-DTnZtf2k.js +44 -0
  88. package/resources/fe-dist/assets/diagram-5GNKFQAL-DBpgZj4a.js +11 -0
  89. package/resources/fe-dist/assets/diagram-KO2AKTUF-CfijYCeY.js +4 -0
  90. package/resources/fe-dist/assets/diagram-LMA3HP47-D7nBmQ-E.js +25 -0
  91. package/resources/fe-dist/assets/diagram-OG6HWLK6-Bi8sHV0O.js +25 -0
  92. package/resources/fe-dist/assets/erDiagram-TEJ5UH35-DcoDesO2.js +86 -0
  93. package/resources/fe-dist/assets/flowDiagram-I6XJVG4X-Bbcl9kxS.js +163 -0
  94. package/resources/fe-dist/assets/ganttDiagram-6RSMTGT7-BM8Y7jwp.js +293 -0
  95. package/resources/fe-dist/assets/gitGraphDiagram-PVQCEYII-x3wpNQcW.js +107 -0
  96. package/resources/fe-dist/assets/graph-CAnANduQ.js +2 -0
  97. package/resources/fe-dist/assets/index-cfY8KuW7.js +466 -0
  98. package/resources/fe-dist/assets/index-vcS0VPIW.css +1 -0
  99. package/resources/fe-dist/assets/infoDiagram-5YYISTIA-Bl6cgwVb.js +3 -0
  100. package/resources/fe-dist/assets/init-Gi6I4Gst.js +2 -0
  101. package/resources/fe-dist/assets/ishikawaDiagram-YF4QCWOH-BfQ7aZxN.js +71 -0
  102. package/resources/fe-dist/assets/journeyDiagram-JHISSGLW-DSwQzmdL.js +140 -0
  103. package/resources/fe-dist/assets/kanban-definition-UN3LZRKU-B1aWDl4E.js +90 -0
  104. package/resources/fe-dist/assets/layout-DGIYPm2g.js +2 -0
  105. package/resources/fe-dist/assets/linear-CfhRC4iX.js +2 -0
  106. package/resources/fe-dist/assets/markdown-preview-Bf_6UH9R.css +1 -0
  107. package/resources/fe-dist/assets/markdown-preview-CfBRMViB.js +269 -0
  108. package/resources/fe-dist/assets/mermaid.core-M-o4g4TO.js +310 -0
  109. package/resources/fe-dist/assets/mindmap-definition-RKZ34NQL-D976sxOA.js +97 -0
  110. package/resources/fe-dist/assets/ordinal-BENe2yWM.js +2 -0
  111. package/resources/fe-dist/assets/pieDiagram-4H26LBE5-PVQ3-EB6.js +31 -0
  112. package/resources/fe-dist/assets/quadrantDiagram-W4KKPZXB-BNEFhYNq.js +8 -0
  113. package/resources/fe-dist/assets/{refresh-cw-D0LYIwxc.js → refresh-cw-cfX5JiIH.js} +3 -3
  114. package/resources/fe-dist/assets/requirementDiagram-4Y6WPE33-B7Zd2lpN.js +85 -0
  115. package/resources/fe-dist/assets/sankeyDiagram-5OEKKPKP-D_Td4j-B.js +41 -0
  116. package/resources/fe-dist/assets/sequenceDiagram-3UESZ5HK-DQSVX5mL.js +163 -0
  117. package/resources/fe-dist/assets/stateDiagram-AJRCARHV-C0PNCTfs.js +2 -0
  118. package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-CB5OePKf.js +2 -0
  119. package/resources/fe-dist/assets/timeline-definition-PNZ67QCA-BlMj8Fl1.js +121 -0
  120. package/resources/fe-dist/assets/vennDiagram-CIIHVFJN-DuGxwZiq.js +35 -0
  121. package/resources/fe-dist/assets/wardley-L42UT6IY-4VhWbg10.js +174 -0
  122. package/resources/fe-dist/assets/wardleyDiagram-YWT4CUSO-Bu4k6xIx.js +79 -0
  123. package/resources/fe-dist/assets/xychartDiagram-2RQKCTM6-Cv88IDWh.js +8 -0
  124. package/resources/fe-dist/fonts/GeistMonoNerdFontMono-Bold.woff2 +0 -0
  125. package/resources/fe-dist/fonts/GeistMonoNerdFontMono-Regular.woff2 +0 -0
  126. package/resources/fe-dist/index.html +2 -2
  127. package/resources/fe-dist/tmex-maskable.png +0 -0
  128. package/resources/fe-dist/tmex.png +0 -0
  129. package/resources/gateway-drizzle/0007_fearless_pestilence.sql +8 -0
  130. package/resources/gateway-drizzle/0008_perfect_ozymandias.sql +7 -0
  131. package/resources/gateway-drizzle/meta/_journal.json +14 -0
  132. package/resources/fe-dist/assets/DevicePage-RqH-pnZX.js +0 -26
  133. package/resources/fe-dist/assets/DevicesPage-BPLJu-5J.js +0 -2
  134. package/resources/fe-dist/assets/SettingsPage-CKP305BP.js +0 -22
  135. package/resources/fe-dist/assets/card-X35xwXzW.js +0 -2
  136. package/resources/fe-dist/assets/index-CKQEHZcV.css +0 -1
  137. package/resources/fe-dist/assets/index-O2a9149K.js +0 -400
  138. package/resources/fe-dist/fonts/GeistMono-Bold.woff2 +0 -0
  139. package/resources/fe-dist/fonts/GeistMono-Regular.woff2 +0 -0
@@ -22549,8 +22549,8 @@ function applyRepoEnv(name, env, options, log) {
22549
22549
  loadEnv();
22550
22550
 
22551
22551
  // src/runtime/server.ts
22552
- import { existsSync as existsSync5 } from "fs";
22553
- import { extname, join as join4, normalize, resolve as resolve4, sep } from "path";
22552
+ import { existsSync as existsSync8 } from "fs";
22553
+ import { extname as extname2, join as join7, normalize, resolve as resolve6, sep } from "path";
22554
22554
 
22555
22555
  // ../../apps/gateway/src/crypto/errors.ts
22556
22556
  function contextLabel(context) {
@@ -22740,11 +22740,22 @@ var I18N_RESOURCES = {
22740
22740
  language_en_US: "English",
22741
22741
  language_zh_CN: "\u7B80\u4F53\u4E2D\u6587",
22742
22742
  language_ja_JP: "\u65E5\u672C\u8A9E",
22743
- theme: "Theme",
22743
+ theme: "Dark Mode",
22744
22744
  themeLight: "Light",
22745
22745
  themeDark: "Dark",
22746
22746
  siteTab: "Site",
22747
22747
  notificationsTab: "Notifications",
22748
+ tabGroup: {
22749
+ general: "General",
22750
+ devicesAndFiles: "Devices & Files",
22751
+ notifications: "Notifications",
22752
+ ai: "AI"
22753
+ },
22754
+ deviceManagement: {
22755
+ title: "Device Management",
22756
+ description: "Add, edit, and connect to your local and SSH devices.",
22757
+ openButton: "Open Device Management"
22758
+ },
22748
22759
  refreshToApply: "Refresh to apply language change",
22749
22760
  saveSettings: "Save Settings",
22750
22761
  settingsSaved: "Settings saved",
@@ -22798,6 +22809,7 @@ var I18N_RESOURCES = {
22798
22809
  search: {
22799
22810
  title: "Search",
22800
22811
  provider: "Search Provider",
22812
+ responsesApiHint: "If your LLM provider uses the OpenAI Responses API protocol, it can use that API's built-in hosted search tool; the third-party search below is an optional supplement.",
22801
22813
  providerNone: "Disabled",
22802
22814
  tavilyApiKey: "Tavily API Key",
22803
22815
  braveApiKey: "Brave API Key",
@@ -22807,6 +22819,58 @@ var I18N_RESOURCES = {
22807
22819
  clearKeyConfirm: "Clear this API key? You will need to enter it again.",
22808
22820
  loadFailed: "Failed to load search settings",
22809
22821
  saveFailed: "Failed to save search settings"
22822
+ },
22823
+ files: {
22824
+ title: "Files",
22825
+ description: "Per-device directories browsable in the Files tab. Local devices read directly; SSH devices use rsync.",
22826
+ addRoot: "Add directory",
22827
+ roots: "Directories",
22828
+ empty: "No directories configured",
22829
+ missing: "Device missing",
22830
+ modalAddTitle: "Add directory",
22831
+ modalEditTitle: "Edit directory",
22832
+ device: "Device",
22833
+ devicePlaceholder: "Select a device",
22834
+ noDevices: "No devices yet \u2014 add one first",
22835
+ path: "Path",
22836
+ pathPlaceholder: "/absolute/path/to/directory",
22837
+ pathHint: "Absolute path on the selected device.",
22838
+ enabled: "Enabled",
22839
+ addFailed: "Failed to add directory",
22840
+ updateFailed: "Failed to update directory",
22841
+ deleteFailed: "Failed to remove directory",
22842
+ toggleFailed: "Failed to update directory",
22843
+ deleteTitle: "Remove directory?",
22844
+ deleteDesc: 'Remove "{{path}}" from the whitelist? Files under it will no longer be accessible.'
22845
+ },
22846
+ version: {
22847
+ title: "Version & Updates",
22848
+ currentVersion: "Current version",
22849
+ installMethod: "Install method",
22850
+ installMethodCli: "Installed via CLI",
22851
+ installMethodNonCli: "Not installed via CLI",
22852
+ deployment: "Deployment",
22853
+ deploymentLaunchd: "launchd (macOS)",
22854
+ deploymentSystemd: "systemd (Linux)",
22855
+ deploymentNone: "None",
22856
+ checkUpdate: "Check for updates",
22857
+ checking: "Checking\u2026",
22858
+ upToDate: "You are on the latest version.",
22859
+ updateAvailable: "Update available: {{version}}",
22860
+ changelog: "Changelog",
22861
+ changelogUnavailable: "No changelog available for this version.",
22862
+ publishedAt: "Published {{date}}",
22863
+ upgrade: "Upgrade now",
22864
+ upgradeDisabledDev: "In-app update is disabled outside the production environment.",
22865
+ upgradeDisabledNonCli: "In-app update is only available for CLI installations.",
22866
+ upgradeWarningTitle: "Confirm upgrade",
22867
+ upgradeWarningBody: "Upgrading restarts the service and will interrupt your current session. It may also affect the survival of tmux processes managed by the service. Continue?",
22868
+ upgradeStarted: "Upgrade started",
22869
+ stateDownloading: "Downloading the new version\u2026",
22870
+ stateExecuting: "Applying the upgrade, the service is restarting\u2026",
22871
+ interruptNotice: "The connection will drop while the service restarts; it will recover shortly.",
22872
+ checkFailed: "Failed to check for updates",
22873
+ terminalHint: "Or upgrade from a terminal: npx tmex-cli@<version> upgrade"
22810
22874
  }
22811
22875
  },
22812
22876
  telegram: {
@@ -22816,6 +22880,7 @@ var I18N_RESOURCES = {
22816
22880
  botToken: "Bot Token",
22817
22881
  botTokenPlaceholder: "123456:AA...",
22818
22882
  addBot: "Add Bot",
22883
+ editBot: "Edit Bot",
22819
22884
  enableBot: "Enable Bot",
22820
22885
  allowAuthRequests: "Allow Authorization Requests",
22821
22886
  pendingChats: "Pending Authorization",
@@ -22985,7 +23050,18 @@ Time: {{time}}`,
22985
23050
  watchFireModeInvalid: "Fire mode must be once or repeat",
22986
23051
  watchCooldownInvalid: "Cooldown seconds must be an integer >= 0",
22987
23052
  watchAssistDescriptionRequired: "Please describe what to match",
22988
- watchAssistModelUnavailable: "Model call failed: {{detail}}"
23053
+ watchAssistModelUnavailable: "Model call failed: {{detail}}",
23054
+ fileRootInvalid: "Path must be an absolute, existing directory",
23055
+ fileOutsideRoots: "Path is outside the allowed directories",
23056
+ fileNotADirectory: "Not a directory",
23057
+ fileTooLarge: "File is too large",
23058
+ fileBinary: "Binary files are not supported",
23059
+ fileRootDeviceInvalid: "Invalid or unknown device",
23060
+ fileRootDuplicate: "This directory is already added for the device",
23061
+ upgradeNotAllowed: "In-app update is not available for this installation.",
23062
+ upgradeInProgress: "An upgrade is already in progress.",
23063
+ upgradeVersionRequired: "Target version is required.",
23064
+ updateCheckFailed: "Failed to query the npm registry."
22989
23065
  },
22990
23066
  notification: {
22991
23067
  clickToJump: "Click to jump to corresponding pane",
@@ -23274,7 +23350,58 @@ Time: {{time}}`,
23274
23350
  },
23275
23351
  validation: {
23276
23352
  deviceNameRequired: "Device name is required",
23277
- hostRequired: "Host is required for SSH devices"
23353
+ hostRequired: "Host is required for SSH devices",
23354
+ portRequired: "A valid port is required for SSH devices",
23355
+ usernameRequired: "Username is required for SSH devices",
23356
+ sshConfigRequired: "SSH config path is required for SSH devices"
23357
+ },
23358
+ files: {
23359
+ title: "Files",
23360
+ refresh: "Refresh file list",
23361
+ noRoots: "No accessible directories. Add one in Settings \u2192 Files.",
23362
+ emptyDir: "Empty",
23363
+ truncated: "Too many items \u2014 list truncated",
23364
+ download: "Download",
23365
+ error: {
23366
+ invalid: "Invalid request",
23367
+ outside_roots: "Path is outside the allowed directory",
23368
+ not_found: "No longer exists",
23369
+ not_a_directory: "Not a directory",
23370
+ is_directory: "This is a directory",
23371
+ too_large: "File is too large to preview",
23372
+ binary: "Binary file cannot be previewed",
23373
+ permission_denied: "Permission denied",
23374
+ device_not_found: "Device not found",
23375
+ root_not_found: "Directory entry not found",
23376
+ root_disabled: "This directory is disabled",
23377
+ connection_failed: "Failed to connect to the device",
23378
+ auth_unsupported: "Device auth method unsupported for files (use key or ssh-agent)",
23379
+ rsync_missing_local: "rsync is not installed on the server",
23380
+ rsync_missing_remote: "rsync is not installed on the remote device",
23381
+ timeout: "Timed out",
23382
+ unknown: "Failed to load"
23383
+ },
23384
+ retry: "Retry (collapse then expand)",
23385
+ install: {
23386
+ button: "Install rsync",
23387
+ scopeLocal: "server",
23388
+ scopeRemote: "remote",
23389
+ connectFailed: "Failed to connect to the device",
23390
+ windowFailed: "Failed to create a window",
23391
+ prompt: "rsync is not installed on {{device}} ({{scope}}). Please install rsync using the appropriate command for this system (e.g. `brew install rsync`, `sudo apt-get install -y rsync`, or `sudo yum install -y rsync`), then reply once it is done."
23392
+ }
23393
+ },
23394
+ file: {
23395
+ invalidRef: "Invalid file reference",
23396
+ notFound: "File no longer exists",
23397
+ accessDenied: "Access denied",
23398
+ loadFailed: "Failed to load file",
23399
+ isDirectory: "This is a directory",
23400
+ notPreviewable: "Preview is not available for this file type",
23401
+ tooLarge: "File is too large to preview",
23402
+ binary: "Binary file cannot be previewed",
23403
+ download: "Download",
23404
+ openRaw: "Open raw"
23278
23405
  }
23279
23406
  }
23280
23407
  },
@@ -23441,11 +23568,22 @@ Time: {{time}}`,
23441
23568
  language_en_US: "English",
23442
23569
  language_zh_CN: "\u7B80\u4F53\u4E2D\u6587",
23443
23570
  language_ja_JP: "\u65E5\u672C\u8A9E",
23444
- theme: "\u4E3B\u9898",
23571
+ theme: "\u6DF1\u8272\u6A21\u5F0F",
23445
23572
  themeLight: "\u6D45\u8272",
23446
23573
  themeDark: "\u6DF1\u8272",
23447
23574
  siteTab: "\u7AD9\u70B9",
23448
23575
  notificationsTab: "\u901A\u77E5",
23576
+ tabGroup: {
23577
+ general: "\u901A\u7528\u8BBE\u7F6E",
23578
+ devicesAndFiles: "\u8BBE\u5907\u4E0E\u6587\u4EF6",
23579
+ notifications: "\u901A\u77E5\u8BBE\u7F6E",
23580
+ ai: "AI \u8BBE\u7F6E"
23581
+ },
23582
+ deviceManagement: {
23583
+ title: "\u8BBE\u5907\u7BA1\u7406",
23584
+ description: "\u6DFB\u52A0\u3001\u7F16\u8F91\u5E76\u8FDE\u63A5\u4F60\u7684\u672C\u5730\u4E0E SSH \u8BBE\u5907\u3002",
23585
+ openButton: "\u6253\u5F00\u8BBE\u5907\u7BA1\u7406"
23586
+ },
23449
23587
  refreshToApply: "\u5237\u65B0\u540E\u751F\u6548",
23450
23588
  saveSettings: "\u4FDD\u5B58\u8BBE\u7F6E",
23451
23589
  settingsSaved: "\u7AD9\u70B9\u8BBE\u7F6E\u5DF2\u4FDD\u5B58",
@@ -23499,6 +23637,7 @@ Time: {{time}}`,
23499
23637
  search: {
23500
23638
  title: "\u641C\u7D22",
23501
23639
  provider: "\u641C\u7D22\u63D0\u4F9B\u5546",
23640
+ responsesApiHint: "\u82E5 LLM Provider \u4F7F\u7528 OpenAI Responses API \u534F\u8BAE\uFF0C\u53EF\u76F4\u63A5\u4F7F\u7528\u5176\u5185\u7F6E\u7684 hosted \u641C\u7D22\u5DE5\u5177\uFF1B\u4E0B\u65B9\u7B2C\u4E09\u65B9\u641C\u7D22\u4E3A\u53EF\u9009\u8865\u5145\u3002",
23502
23641
  providerNone: "\u4E0D\u542F\u7528",
23503
23642
  tavilyApiKey: "Tavily API Key",
23504
23643
  braveApiKey: "Brave API Key",
@@ -23508,6 +23647,58 @@ Time: {{time}}`,
23508
23647
  clearKeyConfirm: "\u786E\u5B9A\u8981\u6E05\u9664\u6B64 API Key \u5417\uFF1F\u6E05\u9664\u540E\u9700\u8981\u91CD\u65B0\u586B\u5199\u3002",
23509
23648
  loadFailed: "\u52A0\u8F7D\u641C\u7D22\u8BBE\u7F6E\u5931\u8D25",
23510
23649
  saveFailed: "\u4FDD\u5B58\u641C\u7D22\u8BBE\u7F6E\u5931\u8D25"
23650
+ },
23651
+ files: {
23652
+ title: "\u6587\u4EF6",
23653
+ description: "\u914D\u7F6E\u5404\u8BBE\u5907\u4E0B\u53EF\u5728\u300C\u6587\u4EF6\u300D\u6807\u7B7E\u9875\u6D4F\u89C8\u7684\u76EE\u5F55\u3002\u672C\u5730\u8BBE\u5907\u76F4\u63A5\u8BFB\u53D6\uFF0CSSH \u8BBE\u5907\u8D70 rsync\u3002",
23654
+ addRoot: "\u6DFB\u52A0\u76EE\u5F55",
23655
+ roots: "\u76EE\u5F55",
23656
+ empty: "\u5C1A\u672A\u914D\u7F6E\u4EFB\u4F55\u76EE\u5F55",
23657
+ missing: "\u8BBE\u5907\u5DF2\u5931\u6548",
23658
+ modalAddTitle: "\u6DFB\u52A0\u76EE\u5F55",
23659
+ modalEditTitle: "\u7F16\u8F91\u76EE\u5F55",
23660
+ device: "\u8BBE\u5907",
23661
+ devicePlaceholder: "\u9009\u62E9\u8BBE\u5907",
23662
+ noDevices: "\u8FD8\u6CA1\u6709\u8BBE\u5907\uFF0C\u8BF7\u5148\u6DFB\u52A0\u8BBE\u5907",
23663
+ path: "\u8DEF\u5F84",
23664
+ pathPlaceholder: "/\u7EDD\u5BF9\u8DEF\u5F84/\u5230/\u76EE\u5F55",
23665
+ pathHint: "\u6240\u9009\u8BBE\u5907\u4E0A\u7684\u7EDD\u5BF9\u8DEF\u5F84\u3002",
23666
+ enabled: "\u542F\u7528",
23667
+ addFailed: "\u6DFB\u52A0\u76EE\u5F55\u5931\u8D25",
23668
+ updateFailed: "\u66F4\u65B0\u76EE\u5F55\u5931\u8D25",
23669
+ deleteFailed: "\u79FB\u9664\u76EE\u5F55\u5931\u8D25",
23670
+ toggleFailed: "\u66F4\u65B0\u76EE\u5F55\u5931\u8D25",
23671
+ deleteTitle: "\u79FB\u9664\u76EE\u5F55\uFF1F",
23672
+ deleteDesc: "\u5C06\u300C{{path}}\u300D\u79FB\u51FA\u767D\u540D\u5355\uFF1F\u8BE5\u76EE\u5F55\u4E0B\u7684\u6587\u4EF6\u5C06\u4E0D\u518D\u53EF\u8BBF\u95EE\u3002"
23673
+ },
23674
+ version: {
23675
+ title: "\u7248\u672C\u4E0E\u66F4\u65B0",
23676
+ currentVersion: "\u5F53\u524D\u7248\u672C",
23677
+ installMethod: "\u5B89\u88C5\u65B9\u5F0F",
23678
+ installMethodCli: "\u901A\u8FC7 CLI \u5B89\u88C5",
23679
+ installMethodNonCli: "\u975E CLI \u5B89\u88C5",
23680
+ deployment: "\u90E8\u7F72\u65B9\u5F0F",
23681
+ deploymentLaunchd: "launchd\uFF08macOS\uFF09",
23682
+ deploymentSystemd: "systemd\uFF08Linux\uFF09",
23683
+ deploymentNone: "\u65E0",
23684
+ checkUpdate: "\u68C0\u67E5\u66F4\u65B0",
23685
+ checking: "\u68C0\u67E5\u4E2D\u2026",
23686
+ upToDate: "\u5DF2\u662F\u6700\u65B0\u7248\u672C\u3002",
23687
+ updateAvailable: "\u6709\u53EF\u7528\u66F4\u65B0\uFF1A{{version}}",
23688
+ changelog: "\u66F4\u65B0\u65E5\u5FD7",
23689
+ changelogUnavailable: "\u8BE5\u7248\u672C\u6682\u65E0\u66F4\u65B0\u65E5\u5FD7\u3002",
23690
+ publishedAt: "\u53D1\u5E03\u4E8E {{date}}",
23691
+ upgrade: "\u7ACB\u5373\u5347\u7EA7",
23692
+ upgradeDisabledDev: "\u975E production \u73AF\u5883\u5DF2\u7981\u7528\u7A0B\u5E8F\u5185\u66F4\u65B0\u3002",
23693
+ upgradeDisabledNonCli: "\u7A0B\u5E8F\u5185\u66F4\u65B0\u4EC5\u5728\u901A\u8FC7 CLI \u5B89\u88C5\u65F6\u53EF\u7528\u3002",
23694
+ upgradeWarningTitle: "\u786E\u8BA4\u5347\u7EA7",
23695
+ upgradeWarningBody: "\u5347\u7EA7\u4F1A\u91CD\u542F\u670D\u52A1\u5E76\u4E2D\u65AD\u5F53\u524D\u8BBF\u95EE\uFF0C\u8FD8\u53EF\u80FD\u5F71\u54CD\u8BE5\u670D\u52A1\u6258\u7BA1\u7684 tmux \u8FDB\u7A0B\u7684\u5B58\u6D3B\u3002\u662F\u5426\u7EE7\u7EED\uFF1F",
23696
+ upgradeStarted: "\u5347\u7EA7\u5DF2\u5F00\u59CB",
23697
+ stateDownloading: "\u6B63\u5728\u4E0B\u8F7D\u65B0\u7248\u672C\u2026",
23698
+ stateExecuting: "\u6B63\u5728\u6267\u884C\u5347\u7EA7\uFF0C\u670D\u52A1\u5373\u5C06\u91CD\u542F\u2026",
23699
+ interruptNotice: "\u670D\u52A1\u91CD\u542F\u671F\u95F4\u8FDE\u63A5\u4F1A\u65AD\u5F00\uFF0C\u7A0D\u5019\u5373\u53EF\u6062\u590D\u3002",
23700
+ checkFailed: "\u68C0\u67E5\u66F4\u65B0\u5931\u8D25",
23701
+ terminalHint: "\u6216\u901A\u8FC7\u7EC8\u7AEF\u5347\u7EA7\uFF1Anpx tmex-cli@<version> upgrade"
23511
23702
  }
23512
23703
  },
23513
23704
  telegram: {
@@ -23517,6 +23708,7 @@ Time: {{time}}`,
23517
23708
  botToken: "Bot Token",
23518
23709
  botTokenPlaceholder: "123456:AA...",
23519
23710
  addBot: "\u65B0\u589E Bot",
23711
+ editBot: "\u7F16\u8F91 Bot",
23520
23712
  enableBot: "\u542F\u7528 Bot",
23521
23713
  allowAuthRequests: "\u5141\u8BB8\u7533\u8BF7\u6388\u6743",
23522
23714
  pendingChats: "\u5F85\u6388\u6743",
@@ -23686,7 +23878,18 @@ Bot\uFF1A{{botName}}
23686
23878
  watchFireModeInvalid: "\u89E6\u53D1\u6A21\u5F0F\u5FC5\u987B\u662F once \u6216 repeat",
23687
23879
  watchCooldownInvalid: "\u51B7\u5374\u79D2\u6570\u5FC5\u987B\u662F\u4E0D\u5C0F\u4E8E 0 \u7684\u6574\u6570",
23688
23880
  watchAssistDescriptionRequired: "\u8BF7\u63CF\u8FF0\u8981\u5339\u914D\u7684\u5185\u5BB9",
23689
- watchAssistModelUnavailable: "\u6A21\u578B\u8C03\u7528\u5931\u8D25\uFF1A{{detail}}"
23881
+ watchAssistModelUnavailable: "\u6A21\u578B\u8C03\u7528\u5931\u8D25\uFF1A{{detail}}",
23882
+ fileRootInvalid: "\u8DEF\u5F84\u5FC5\u987B\u662F\u5DF2\u5B58\u5728\u7684\u7EDD\u5BF9\u76EE\u5F55",
23883
+ fileOutsideRoots: "\u8DEF\u5F84\u4E0D\u5728\u5141\u8BB8\u7684\u76EE\u5F55\u8303\u56F4\u5185",
23884
+ fileNotADirectory: "\u4E0D\u662F\u76EE\u5F55",
23885
+ fileTooLarge: "\u6587\u4EF6\u8FC7\u5927",
23886
+ fileBinary: "\u4E0D\u652F\u6301\u4E8C\u8FDB\u5236\u6587\u4EF6",
23887
+ fileRootDeviceInvalid: "\u65E0\u6548\u6216\u672A\u77E5\u7684\u8BBE\u5907",
23888
+ fileRootDuplicate: "\u8BE5\u8BBE\u5907\u4E0B\u5DF2\u6DFB\u52A0\u8FC7\u6B64\u76EE\u5F55",
23889
+ upgradeNotAllowed: "\u5F53\u524D\u5B89\u88C5\u65B9\u5F0F\u4E0D\u652F\u6301\u7A0B\u5E8F\u5185\u66F4\u65B0\u3002",
23890
+ upgradeInProgress: "\u5DF2\u6709\u5347\u7EA7\u4EFB\u52A1\u6B63\u5728\u8FDB\u884C\u3002",
23891
+ upgradeVersionRequired: "\u7F3A\u5C11\u76EE\u6807\u7248\u672C\u53F7\u3002",
23892
+ updateCheckFailed: "\u67E5\u8BE2 npm registry \u5931\u8D25\u3002"
23690
23893
  },
23691
23894
  notification: {
23692
23895
  clickToJump: "\u70B9\u51FB\u8DF3\u8F6C\u5230\u5BF9\u5E94 Pane",
@@ -23975,7 +24178,58 @@ Bot\uFF1A{{botName}}
23975
24178
  },
23976
24179
  validation: {
23977
24180
  deviceNameRequired: "\u8BBE\u5907\u540D\u79F0\u4E3A\u5FC5\u586B\u9879",
23978
- hostRequired: "SSH \u8BBE\u5907\u9700\u8981\u586B\u5199\u4E3B\u673A\u5730\u5740"
24181
+ hostRequired: "SSH \u8BBE\u5907\u9700\u8981\u586B\u5199\u4E3B\u673A\u5730\u5740",
24182
+ portRequired: "SSH \u8BBE\u5907\u9700\u8981\u586B\u5199\u6709\u6548\u7AEF\u53E3",
24183
+ usernameRequired: "SSH \u8BBE\u5907\u9700\u8981\u586B\u5199\u7528\u6237\u540D",
24184
+ sshConfigRequired: "SSH \u8BBE\u5907\u9700\u8981\u586B\u5199 ssh config \u8DEF\u5F84"
24185
+ },
24186
+ files: {
24187
+ title: "\u6587\u4EF6",
24188
+ refresh: "\u5237\u65B0\u6587\u4EF6\u5217\u8868",
24189
+ noRoots: "\u6CA1\u6709\u53EF\u8BBF\u95EE\u7684\u76EE\u5F55\u3002\u8BF7\u5728\u300C\u8BBE\u7F6E \u2192 \u6587\u4EF6\u300D\u4E2D\u6DFB\u52A0\u3002",
24190
+ emptyDir: "\u7A7A\u76EE\u5F55",
24191
+ truncated: "\u6761\u76EE\u8FC7\u591A\uFF0C\u5217\u8868\u5DF2\u622A\u65AD",
24192
+ download: "\u4E0B\u8F7D",
24193
+ error: {
24194
+ invalid: "\u65E0\u6548\u8BF7\u6C42",
24195
+ outside_roots: "\u8DEF\u5F84\u8D85\u51FA\u5141\u8BB8\u7684\u76EE\u5F55\u8303\u56F4",
24196
+ not_found: "\u5DF2\u4E0D\u5B58\u5728",
24197
+ not_a_directory: "\u4E0D\u662F\u76EE\u5F55",
24198
+ is_directory: "\u8FD9\u662F\u4E00\u4E2A\u76EE\u5F55",
24199
+ too_large: "\u6587\u4EF6\u8FC7\u5927\uFF0C\u65E0\u6CD5\u9884\u89C8",
24200
+ binary: "\u4E8C\u8FDB\u5236\u6587\u4EF6\u65E0\u6CD5\u9884\u89C8",
24201
+ permission_denied: "\u65E0\u8BBF\u95EE\u6743\u9650",
24202
+ device_not_found: "\u8BBE\u5907\u4E0D\u5B58\u5728",
24203
+ root_not_found: "\u76EE\u5F55\u9879\u4E0D\u5B58\u5728",
24204
+ root_disabled: "\u8BE5\u76EE\u5F55\u5DF2\u7981\u7528",
24205
+ connection_failed: "\u8FDE\u63A5\u8BBE\u5907\u5931\u8D25",
24206
+ auth_unsupported: "\u8BE5\u8BBE\u5907\u7684\u8BA4\u8BC1\u65B9\u5F0F\u4E0D\u652F\u6301\u6587\u4EF6\u8BBF\u95EE\uFF08\u8BF7\u7528\u5BC6\u94A5\u6216 ssh-agent\uFF09",
24207
+ rsync_missing_local: "\u670D\u52A1\u5668\u4E0A\u672A\u5B89\u88C5 rsync",
24208
+ rsync_missing_remote: "\u8FDC\u7A0B\u8BBE\u5907\u4E0A\u672A\u5B89\u88C5 rsync",
24209
+ timeout: "\u5DF2\u8D85\u65F6",
24210
+ unknown: "\u52A0\u8F7D\u5931\u8D25"
24211
+ },
24212
+ retry: "\u91CD\u8BD5\uFF08\u6536\u8D77\u540E\u518D\u5C55\u5F00\uFF09",
24213
+ install: {
24214
+ button: "\u5B89\u88C5 rsync",
24215
+ scopeLocal: "\u670D\u52A1\u5668",
24216
+ scopeRemote: "\u8FDC\u7A0B",
24217
+ connectFailed: "\u8FDE\u63A5\u8BBE\u5907\u5931\u8D25",
24218
+ windowFailed: "\u521B\u5EFA\u7A97\u53E3\u5931\u8D25",
24219
+ prompt: "\u8BBE\u5907 {{device}}\uFF08{{scope}}\uFF09\u4E0A\u672A\u68C0\u6D4B\u5230 rsync\u3002\u8BF7\u6839\u636E\u7CFB\u7EDF\u9009\u62E9\u5408\u9002\u7684\u547D\u4EE4\u5B89\u88C5 rsync\uFF08\u4F8B\u5982 `brew install rsync`\u3001`sudo apt-get install -y rsync` \u6216 `sudo yum install -y rsync`\uFF09\uFF0C\u5B89\u88C5\u5B8C\u6210\u540E\u56DE\u590D\u6211\u3002"
24220
+ }
24221
+ },
24222
+ file: {
24223
+ invalidRef: "\u65E0\u6548\u7684\u6587\u4EF6\u5F15\u7528",
24224
+ notFound: "\u6587\u4EF6\u5DF2\u4E0D\u5B58\u5728",
24225
+ accessDenied: "\u65E0\u8BBF\u95EE\u6743\u9650",
24226
+ loadFailed: "\u52A0\u8F7D\u6587\u4EF6\u5931\u8D25",
24227
+ isDirectory: "\u8FD9\u662F\u4E00\u4E2A\u76EE\u5F55",
24228
+ notPreviewable: "\u8BE5\u6587\u4EF6\u7C7B\u578B\u4E0D\u652F\u6301\u9884\u89C8",
24229
+ tooLarge: "\u6587\u4EF6\u8FC7\u5927\uFF0C\u65E0\u6CD5\u9884\u89C8",
24230
+ binary: "\u4E8C\u8FDB\u5236\u6587\u4EF6\u65E0\u6CD5\u9884\u89C8",
24231
+ download: "\u4E0B\u8F7D",
24232
+ openRaw: "\u6253\u5F00\u539F\u59CB\u6587\u4EF6"
23979
24233
  }
23980
24234
  }
23981
24235
  },
@@ -24142,11 +24396,22 @@ Bot\uFF1A{{botName}}
24142
24396
  language_en_US: "English",
24143
24397
  language_zh_CN: "\u7B80\u4F53\u4E2D\u6587",
24144
24398
  language_ja_JP: "\u65E5\u672C\u8A9E",
24145
- theme: "\u30C6\u30FC\u30DE",
24399
+ theme: "\u30C0\u30FC\u30AF\u30E2\u30FC\u30C9",
24146
24400
  themeLight: "\u30E9\u30A4\u30C8",
24147
24401
  themeDark: "\u30C0\u30FC\u30AF",
24148
24402
  siteTab: "\u30B5\u30A4\u30C8",
24149
24403
  notificationsTab: "\u901A\u77E5",
24404
+ tabGroup: {
24405
+ general: "\u4E00\u822C\u8A2D\u5B9A",
24406
+ devicesAndFiles: "\u30C7\u30D0\u30A4\u30B9\u3068\u30D5\u30A1\u30A4\u30EB",
24407
+ notifications: "\u901A\u77E5\u8A2D\u5B9A",
24408
+ ai: "AI \u8A2D\u5B9A"
24409
+ },
24410
+ deviceManagement: {
24411
+ title: "\u30C7\u30D0\u30A4\u30B9\u7BA1\u7406",
24412
+ description: "\u30ED\u30FC\u30AB\u30EB\u304A\u3088\u3073 SSH \u30C7\u30D0\u30A4\u30B9\u306E\u8FFD\u52A0\u30FB\u7DE8\u96C6\u30FB\u63A5\u7D9A\u3092\u884C\u3044\u307E\u3059\u3002",
24413
+ openButton: "\u30C7\u30D0\u30A4\u30B9\u7BA1\u7406\u3092\u958B\u304F"
24414
+ },
24150
24415
  refreshToApply: "\u8A00\u8A9E\u5909\u66F4\u3092\u9069\u7528\u3059\u308B\u306B\u306F\u66F4\u65B0\u3057\u3066\u304F\u3060\u3055\u3044",
24151
24416
  saveSettings: "\u8A2D\u5B9A\u3092\u4FDD\u5B58",
24152
24417
  settingsSaved: "\u8A2D\u5B9A\u3092\u4FDD\u5B58\u3057\u307E\u3057\u305F",
@@ -24200,6 +24465,7 @@ Bot\uFF1A{{botName}}
24200
24465
  search: {
24201
24466
  title: "\u691C\u7D22",
24202
24467
  provider: "\u691C\u7D22\u30D7\u30ED\u30D0\u30A4\u30C0\u30FC",
24468
+ responsesApiHint: "LLM \u30D7\u30ED\u30D0\u30A4\u30C0\u30FC\u304C OpenAI Responses API \u30D7\u30ED\u30C8\u30B3\u30EB\u3092\u4F7F\u7528\u3057\u3066\u3044\u308B\u5834\u5408\u3001\u305D\u306E\u5185\u8535 hosted \u691C\u7D22\u30C4\u30FC\u30EB\u3092\u5229\u7528\u3067\u304D\u307E\u3059\u3002\u4E0B\u8A18\u306E\u30B5\u30FC\u30C9\u30D1\u30FC\u30C6\u30A3\u691C\u7D22\u306F\u4EFB\u610F\u306E\u88DC\u5B8C\u3067\u3059\u3002",
24203
24469
  providerNone: "\u7121\u52B9",
24204
24470
  tavilyApiKey: "Tavily API \u30AD\u30FC",
24205
24471
  braveApiKey: "Brave API \u30AD\u30FC",
@@ -24209,6 +24475,58 @@ Bot\uFF1A{{botName}}
24209
24475
  clearKeyConfirm: "\u3053\u306E API \u30AD\u30FC\u3092\u30AF\u30EA\u30A2\u3057\u307E\u3059\u304B\uFF1F\u518D\u5EA6\u5165\u529B\u304C\u5FC5\u8981\u306B\u306A\u308A\u307E\u3059\u3002",
24210
24476
  loadFailed: "\u691C\u7D22\u8A2D\u5B9A\u306E\u8AAD\u307F\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
24211
24477
  saveFailed: "\u691C\u7D22\u8A2D\u5B9A\u306E\u4FDD\u5B58\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
24478
+ },
24479
+ files: {
24480
+ title: "\u30D5\u30A1\u30A4\u30EB",
24481
+ description: "\u300C\u30D5\u30A1\u30A4\u30EB\u300D\u30BF\u30D6\u3067\u95B2\u89A7\u3067\u304D\u308B\u30C7\u30D0\u30A4\u30B9\u3054\u3068\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u8A2D\u5B9A\u3057\u307E\u3059\u3002\u30ED\u30FC\u30AB\u30EB\u306F\u76F4\u63A5\u8AAD\u307F\u53D6\u308A\u3001SSH \u306F rsync \u3092\u4F7F\u7528\u3002",
24482
+ addRoot: "\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u8FFD\u52A0",
24483
+ roots: "\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA",
24484
+ empty: "\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u8A2D\u5B9A\u3055\u308C\u3066\u3044\u307E\u305B\u3093",
24485
+ missing: "\u30C7\u30D0\u30A4\u30B9\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093",
24486
+ modalAddTitle: "\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u8FFD\u52A0",
24487
+ modalEditTitle: "\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u7DE8\u96C6",
24488
+ device: "\u30C7\u30D0\u30A4\u30B9",
24489
+ devicePlaceholder: "\u30C7\u30D0\u30A4\u30B9\u3092\u9078\u629E",
24490
+ noDevices: "\u30C7\u30D0\u30A4\u30B9\u304C\u3042\u308A\u307E\u305B\u3093\u3002\u5148\u306B\u8FFD\u52A0\u3057\u3066\u304F\u3060\u3055\u3044",
24491
+ path: "\u30D1\u30B9",
24492
+ pathPlaceholder: "/\u7D76\u5BFE\u30D1\u30B9/\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA",
24493
+ pathHint: "\u9078\u629E\u3057\u305F\u30C7\u30D0\u30A4\u30B9\u4E0A\u306E\u7D76\u5BFE\u30D1\u30B9\u3002",
24494
+ enabled: "\u6709\u52B9",
24495
+ addFailed: "\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306E\u8FFD\u52A0\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
24496
+ updateFailed: "\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306E\u66F4\u65B0\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
24497
+ deleteFailed: "\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306E\u524A\u9664\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
24498
+ toggleFailed: "\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306E\u66F4\u65B0\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
24499
+ deleteTitle: "\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u524A\u9664\u3057\u307E\u3059\u304B\uFF1F",
24500
+ deleteDesc: "\u300C{{path}}\u300D\u3092\u30DB\u30EF\u30A4\u30C8\u30EA\u30B9\u30C8\u304B\u3089\u524A\u9664\u3057\u307E\u3059\u304B\uFF1F\u914D\u4E0B\u306E\u30D5\u30A1\u30A4\u30EB\u306B\u30A2\u30AF\u30BB\u30B9\u3067\u304D\u306A\u304F\u306A\u308A\u307E\u3059\u3002"
24501
+ },
24502
+ version: {
24503
+ title: "\u30D0\u30FC\u30B8\u30E7\u30F3\u3068\u66F4\u65B0",
24504
+ currentVersion: "\u73FE\u5728\u306E\u30D0\u30FC\u30B8\u30E7\u30F3",
24505
+ installMethod: "\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u65B9\u6CD5",
24506
+ installMethodCli: "CLI \u3067\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB",
24507
+ installMethodNonCli: "CLI \u4EE5\u5916\u3067\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB",
24508
+ deployment: "\u30C7\u30D7\u30ED\u30A4\u65B9\u5F0F",
24509
+ deploymentLaunchd: "launchd\uFF08macOS\uFF09",
24510
+ deploymentSystemd: "systemd\uFF08Linux\uFF09",
24511
+ deploymentNone: "\u306A\u3057",
24512
+ checkUpdate: "\u66F4\u65B0\u3092\u78BA\u8A8D",
24513
+ checking: "\u78BA\u8A8D\u4E2D\u2026",
24514
+ upToDate: "\u6700\u65B0\u30D0\u30FC\u30B8\u30E7\u30F3\u3067\u3059\u3002",
24515
+ updateAvailable: "\u66F4\u65B0\u304C\u3042\u308A\u307E\u3059\uFF1A{{version}}",
24516
+ changelog: "\u5909\u66F4\u5C65\u6B74",
24517
+ changelogUnavailable: "\u3053\u306E\u30D0\u30FC\u30B8\u30E7\u30F3\u306E\u5909\u66F4\u5C65\u6B74\u306F\u3042\u308A\u307E\u305B\u3093\u3002",
24518
+ publishedAt: "\u516C\u958B\u65E5 {{date}}",
24519
+ upgrade: "\u4ECA\u3059\u3050\u66F4\u65B0",
24520
+ upgradeDisabledDev: "production \u4EE5\u5916\u306E\u74B0\u5883\u3067\u306F\u30A2\u30D7\u30EA\u5185\u66F4\u65B0\u306F\u7121\u52B9\u3067\u3059\u3002",
24521
+ upgradeDisabledNonCli: "\u30A2\u30D7\u30EA\u5185\u66F4\u65B0\u306F CLI \u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u6642\u306E\u307F\u5229\u7528\u3067\u304D\u307E\u3059\u3002",
24522
+ upgradeWarningTitle: "\u66F4\u65B0\u306E\u78BA\u8A8D",
24523
+ upgradeWarningBody: "\u66F4\u65B0\u3059\u308B\u3068\u30B5\u30FC\u30D3\u30B9\u304C\u518D\u8D77\u52D5\u3057\u3001\u73FE\u5728\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u304C\u4E2D\u65AD\u3055\u308C\u307E\u3059\u3002\u30B5\u30FC\u30D3\u30B9\u304C\u7BA1\u7406\u3059\u308B tmux \u30D7\u30ED\u30BB\u30B9\u306E\u5B58\u7D9A\u306B\u3082\u5F71\u97FF\u3059\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002\u7D9A\u884C\u3057\u307E\u3059\u304B\uFF1F",
24524
+ upgradeStarted: "\u66F4\u65B0\u3092\u958B\u59CB\u3057\u307E\u3057\u305F",
24525
+ stateDownloading: "\u65B0\u3057\u3044\u30D0\u30FC\u30B8\u30E7\u30F3\u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u4E2D\u2026",
24526
+ stateExecuting: "\u66F4\u65B0\u3092\u9069\u7528\u4E2D\u3001\u30B5\u30FC\u30D3\u30B9\u3092\u518D\u8D77\u52D5\u3057\u3066\u3044\u307E\u3059\u2026",
24527
+ interruptNotice: "\u30B5\u30FC\u30D3\u30B9\u518D\u8D77\u52D5\u4E2D\u306F\u63A5\u7D9A\u304C\u5207\u65AD\u3055\u308C\u307E\u3059\u304C\u3001\u307E\u3082\u306A\u304F\u5FA9\u65E7\u3057\u307E\u3059\u3002",
24528
+ checkFailed: "\u66F4\u65B0\u306E\u78BA\u8A8D\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
24529
+ terminalHint: "\u307E\u305F\u306F\u30BF\u30FC\u30DF\u30CA\u30EB\u304B\u3089\u66F4\u65B0\uFF1Anpx tmex-cli@<version> upgrade"
24212
24530
  }
24213
24531
  },
24214
24532
  telegram: {
@@ -24218,6 +24536,7 @@ Bot\uFF1A{{botName}}
24218
24536
  botToken: "Bot Token",
24219
24537
  botTokenPlaceholder: "123456:AA...",
24220
24538
  addBot: "Bot \u3092\u8FFD\u52A0",
24539
+ editBot: "Bot \u3092\u7DE8\u96C6",
24221
24540
  enableBot: "Bot \u3092\u6709\u52B9\u306B\u3059\u308B",
24222
24541
  allowAuthRequests: "\u8A8D\u8A3C\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u8A31\u53EF",
24223
24542
  pendingChats: "\u627F\u8A8D\u5F85\u3061",
@@ -24387,7 +24706,18 @@ Bot\uFF1A{{botName}}
24387
24706
  watchFireModeInvalid: "\u767A\u706B\u30E2\u30FC\u30C9\u306F once \u307E\u305F\u306F repeat \u3067\u3059",
24388
24707
  watchCooldownInvalid: "\u30AF\u30FC\u30EB\u30C0\u30A6\u30F3\u79D2\u6570\u306F 0 \u4EE5\u4E0A\u306E\u6574\u6570\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044",
24389
24708
  watchAssistDescriptionRequired: "\u30DE\u30C3\u30C1\u3055\u305B\u305F\u3044\u5185\u5BB9\u3092\u8A18\u8FF0\u3057\u3066\u304F\u3060\u3055\u3044",
24390
- watchAssistModelUnavailable: "\u30E2\u30C7\u30EB\u547C\u3073\u51FA\u3057\u306B\u5931\u6557\u3057\u307E\u3057\u305F\uFF1A{{detail}}"
24709
+ watchAssistModelUnavailable: "\u30E2\u30C7\u30EB\u547C\u3073\u51FA\u3057\u306B\u5931\u6557\u3057\u307E\u3057\u305F\uFF1A{{detail}}",
24710
+ fileRootInvalid: "\u7D76\u5BFE\u30D1\u30B9\u304B\u3064\u65E2\u5B58\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044",
24711
+ fileOutsideRoots: "\u8A31\u53EF\u3055\u308C\u305F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306E\u7BC4\u56F2\u5916\u3067\u3059",
24712
+ fileNotADirectory: "\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3067\u306F\u3042\u308A\u307E\u305B\u3093",
24713
+ fileTooLarge: "\u30D5\u30A1\u30A4\u30EB\u304C\u5927\u304D\u3059\u304E\u307E\u3059",
24714
+ fileBinary: "\u30D0\u30A4\u30CA\u30EA\u30D5\u30A1\u30A4\u30EB\u306F\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u307E\u305B\u3093",
24715
+ fileRootDeviceInvalid: "\u7121\u52B9\u307E\u305F\u306F\u4E0D\u660E\u306A\u30C7\u30D0\u30A4\u30B9",
24716
+ fileRootDuplicate: "\u3053\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306F\u3053\u306E\u30C7\u30D0\u30A4\u30B9\u306B\u65E2\u306B\u8FFD\u52A0\u3055\u308C\u3066\u3044\u307E\u3059",
24717
+ upgradeNotAllowed: "\u3053\u306E\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3067\u306F\u30A2\u30D7\u30EA\u5185\u66F4\u65B0\u3092\u5229\u7528\u3067\u304D\u307E\u305B\u3093\u3002",
24718
+ upgradeInProgress: "\u3059\u3067\u306B\u66F4\u65B0\u304C\u9032\u884C\u4E2D\u3067\u3059\u3002",
24719
+ upgradeVersionRequired: "\u5BFE\u8C61\u30D0\u30FC\u30B8\u30E7\u30F3\u304C\u5FC5\u8981\u3067\u3059\u3002",
24720
+ updateCheckFailed: "npm registry \u306E\u7167\u4F1A\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002"
24391
24721
  },
24392
24722
  notification: {
24393
24723
  clickToJump: "\u5BFE\u5FDC\u3059\u308B\u30DA\u30A4\u30F3\u306B\u30B8\u30E3\u30F3\u30D7",
@@ -24676,7 +25006,58 @@ Bot\uFF1A{{botName}}
24676
25006
  },
24677
25007
  validation: {
24678
25008
  deviceNameRequired: "\u30C7\u30D0\u30A4\u30B9\u540D\u306F\u5FC5\u9808\u3067\u3059",
24679
- hostRequired: "SSH \u30C7\u30D0\u30A4\u30B9\u306B\u306F\u30DB\u30B9\u30C8\u30A2\u30C9\u30EC\u30B9\u304C\u5FC5\u8981\u3067\u3059"
25009
+ hostRequired: "SSH \u30C7\u30D0\u30A4\u30B9\u306B\u306F\u30DB\u30B9\u30C8\u30A2\u30C9\u30EC\u30B9\u304C\u5FC5\u8981\u3067\u3059",
25010
+ portRequired: "SSH \u30C7\u30D0\u30A4\u30B9\u306B\u306F\u6709\u52B9\u306A\u30DD\u30FC\u30C8\u304C\u5FC5\u8981\u3067\u3059",
25011
+ usernameRequired: "SSH \u30C7\u30D0\u30A4\u30B9\u306B\u306F\u30E6\u30FC\u30B6\u30FC\u540D\u304C\u5FC5\u8981\u3067\u3059",
25012
+ sshConfigRequired: "SSH \u30C7\u30D0\u30A4\u30B9\u306B\u306F ssh config \u306E\u30D1\u30B9\u304C\u5FC5\u8981\u3067\u3059"
25013
+ },
25014
+ files: {
25015
+ title: "\u30D5\u30A1\u30A4\u30EB",
25016
+ refresh: "\u30D5\u30A1\u30A4\u30EB\u4E00\u89A7\u3092\u66F4\u65B0",
25017
+ noRoots: "\u30A2\u30AF\u30BB\u30B9\u53EF\u80FD\u306A\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u304C\u3042\u308A\u307E\u305B\u3093\u3002\u300C\u8A2D\u5B9A \u2192 \u30D5\u30A1\u30A4\u30EB\u300D\u3067\u8FFD\u52A0\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
25018
+ emptyDir: "\u7A7A\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA",
25019
+ truncated: "\u9805\u76EE\u304C\u591A\u3059\u304E\u308B\u305F\u3081\u4E00\u89A7\u3092\u7701\u7565\u3057\u307E\u3057\u305F",
25020
+ download: "\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9",
25021
+ error: {
25022
+ invalid: "\u4E0D\u6B63\u306A\u30EA\u30AF\u30A8\u30B9\u30C8",
25023
+ outside_roots: "\u8A31\u53EF\u3055\u308C\u305F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306E\u7BC4\u56F2\u5916\u3067\u3059",
25024
+ not_found: "\u3059\u3067\u306B\u5B58\u5728\u3057\u307E\u305B\u3093",
25025
+ not_a_directory: "\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3067\u306F\u3042\u308A\u307E\u305B\u3093",
25026
+ is_directory: "\u3053\u308C\u306F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3067\u3059",
25027
+ too_large: "\u30D5\u30A1\u30A4\u30EB\u304C\u5927\u304D\u3059\u304E\u3066\u30D7\u30EC\u30D3\u30E5\u30FC\u3067\u304D\u307E\u305B\u3093",
25028
+ binary: "\u30D0\u30A4\u30CA\u30EA\u30D5\u30A1\u30A4\u30EB\u306F\u30D7\u30EC\u30D3\u30E5\u30FC\u3067\u304D\u307E\u305B\u3093",
25029
+ permission_denied: "\u30A2\u30AF\u30BB\u30B9\u304C\u62D2\u5426\u3055\u308C\u307E\u3057\u305F",
25030
+ device_not_found: "\u30C7\u30D0\u30A4\u30B9\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093",
25031
+ root_not_found: "\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u9805\u76EE\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093",
25032
+ root_disabled: "\u3053\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306F\u7121\u52B9\u3067\u3059",
25033
+ connection_failed: "\u30C7\u30D0\u30A4\u30B9\u3078\u306E\u63A5\u7D9A\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
25034
+ auth_unsupported: "\u3053\u306E\u30C7\u30D0\u30A4\u30B9\u306E\u8A8D\u8A3C\u65B9\u5F0F\u306F\u30D5\u30A1\u30A4\u30EB\u30A2\u30AF\u30BB\u30B9\u306B\u975E\u5BFE\u5FDC\u3067\u3059\uFF08\u9375\u304B ssh-agent \u3092\u4F7F\u7528\uFF09",
25035
+ rsync_missing_local: "\u30B5\u30FC\u30D0\u30FC\u306B rsync \u304C\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3055\u308C\u3066\u3044\u307E\u305B\u3093",
25036
+ rsync_missing_remote: "\u30EA\u30E2\u30FC\u30C8\u30C7\u30D0\u30A4\u30B9\u306B rsync \u304C\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3055\u308C\u3066\u3044\u307E\u305B\u3093",
25037
+ timeout: "\u30BF\u30A4\u30E0\u30A2\u30A6\u30C8\u3057\u307E\u3057\u305F",
25038
+ unknown: "\u8AAD\u307F\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
25039
+ },
25040
+ retry: "\u518D\u8A66\u884C\uFF08\u6298\u308A\u305F\u305F\u3093\u3067\u518D\u5C55\u958B\uFF09",
25041
+ install: {
25042
+ button: "rsync \u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB",
25043
+ scopeLocal: "\u30B5\u30FC\u30D0\u30FC",
25044
+ scopeRemote: "\u30EA\u30E2\u30FC\u30C8",
25045
+ connectFailed: "\u30C7\u30D0\u30A4\u30B9\u3078\u306E\u63A5\u7D9A\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
25046
+ windowFailed: "\u30A6\u30A3\u30F3\u30C9\u30A6\u306E\u4F5C\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
25047
+ prompt: "{{device}}\uFF08{{scope}}\uFF09\u306B rsync \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u30B7\u30B9\u30C6\u30E0\u306B\u5408\u308F\u305B\u305F\u65B9\u6CD5\u3067 rsync \u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\uFF08\u4F8B: `brew install rsync`\u3001`sudo apt-get install -y rsync`\u3001`sudo yum install -y rsync`\uFF09\u3001\u5B8C\u4E86\u3057\u305F\u3089\u8FD4\u4FE1\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
25048
+ }
25049
+ },
25050
+ file: {
25051
+ invalidRef: "\u7121\u52B9\u306A\u30D5\u30A1\u30A4\u30EB\u53C2\u7167\u3067\u3059",
25052
+ notFound: "\u30D5\u30A1\u30A4\u30EB\u306F\u5B58\u5728\u3057\u307E\u305B\u3093",
25053
+ accessDenied: "\u30A2\u30AF\u30BB\u30B9\u304C\u62D2\u5426\u3055\u308C\u307E\u3057\u305F",
25054
+ loadFailed: "\u30D5\u30A1\u30A4\u30EB\u306E\u8AAD\u307F\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
25055
+ isDirectory: "\u3053\u308C\u306F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3067\u3059",
25056
+ notPreviewable: "\u3053\u306E\u30D5\u30A1\u30A4\u30EB\u5F62\u5F0F\u306F\u30D7\u30EC\u30D3\u30E5\u30FC\u3067\u304D\u307E\u305B\u3093",
25057
+ tooLarge: "\u30D5\u30A1\u30A4\u30EB\u304C\u5927\u304D\u3059\u304E\u3066\u30D7\u30EC\u30D3\u30E5\u30FC\u3067\u304D\u307E\u305B\u3093",
25058
+ binary: "\u30D0\u30A4\u30CA\u30EA\u30D5\u30A1\u30A4\u30EB\u306F\u30D7\u30EC\u30D3\u30E5\u30FC\u3067\u304D\u307E\u305B\u3093",
25059
+ download: "\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9",
25060
+ openRaw: "\u5143\u30D5\u30A1\u30A4\u30EB\u3092\u958B\u304F"
24680
25061
  }
24681
25062
  }
24682
25063
  }
@@ -24686,6 +25067,11 @@ function toBCP47(locale) {
24686
25067
  return locale.replace("_", "-");
24687
25068
  }
24688
25069
  var AVAILABLE_LOCALES = ["en_US", "zh_CN", "ja_JP"];
25070
+ // ../shared/src/version.ts
25071
+ function formatDisplayVersion(baseVersion, isProd) {
25072
+ const base = baseVersion?.trim() || "unknown";
25073
+ return isProd ? base : `${base}_dev`;
25074
+ }
24689
25075
  // ../shared/src/ws-borsh/index.ts
24690
25076
  var exports_ws_borsh = {};
24691
25077
  __export(exports_ws_borsh, {
@@ -27202,6 +27588,7 @@ var config = {
27202
27588
  tmuxAllowPassthrough: getBooleanEnv("TMEX_TMUX_ALLOW_PASSTHROUGH", false),
27203
27589
  tmuxTermProgram: getEnv("TMEX_TMUX_TERM_PROGRAM", "ghostty"),
27204
27590
  tmuxWindowStyle: getEnv("TMEX_TMUX_WINDOW_STYLE", "fg=#d0d0d0,bg=#262626"),
27591
+ tmuxSocket: getEnv("TMEX_TMUX_SOCKET", ""),
27205
27592
  sshReconnectMaxRetriesDefault: Number.parseInt(getEnv("TMEX_SSH_RECONNECT_MAX_RETRIES", "2"), 10),
27206
27593
  sshReconnectDelaySecondsDefault: Number.parseInt(getEnv("TMEX_SSH_RECONNECT_DELAY_SECONDS", "10"), 10),
27207
27594
  languageDefault: getEnv("TMEX_DEFAULT_LANGUAGE", "en_US"),
@@ -32196,6 +32583,7 @@ __export(exports_schema2, {
32196
32583
  telegramBotChats: () => telegramBotChats,
32197
32584
  siteSettings: () => siteSettings,
32198
32585
  llmProviders: () => llmProviders,
32586
+ fileRoots: () => fileRoots,
32199
32587
  devices: () => devices,
32200
32588
  deviceTreeOrder: () => deviceTreeOrder,
32201
32589
  deviceRuntimeStatus: () => deviceRuntimeStatus,
@@ -32394,6 +32782,14 @@ var deviceTreeOrder = sqliteTable("device_tree_order", {
32394
32782
  panes: text("panes", { mode: "json" }).$type().notNull().default({}),
32395
32783
  updatedAt: text("updated_at").notNull()
32396
32784
  });
32785
+ var fileRoots = sqliteTable("file_roots", {
32786
+ id: text("id").primaryKey(),
32787
+ deviceId: text("device_id").notNull().references(() => devices.id, { onDelete: "cascade" }),
32788
+ path: text("path").notNull(),
32789
+ enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
32790
+ sortOrder: integer("sort_order").notNull().default(0),
32791
+ createdAt: text("created_at").notNull()
32792
+ }, (table) => [unique("file_roots_device_path_unique").on(table.deviceId, table.path)]);
32397
32793
  var telegramBotChats = sqliteTable("telegram_bot_chats", {
32398
32794
  id: text("id").primaryKey(),
32399
32795
  botId: text("bot_id").notNull().references(() => telegramBots.id, { onDelete: "cascade" }),
@@ -33154,7 +33550,7 @@ function decideAgentConfirmation(id, decision) {
33154
33550
  return updated ?? null;
33155
33551
  }
33156
33552
 
33157
- // ../../node_modules/.bun/gramio@0.4.12+cb432ad7c9c526f6/node_modules/gramio/dist/index.js
33553
+ // ../../node_modules/.bun/gramio@0.4.12+7be50f8974dcb86c/node_modules/gramio/dist/index.js
33158
33554
  var exports_dist5 = {};
33159
33555
  __export(exports_dist5, {
33160
33556
  webhookHandler: () => webhookHandler,
@@ -53088,7 +53484,7 @@ class MediaUpload {
53088
53484
  return new File([text2], filename);
53089
53485
  }
53090
53486
  }
53091
- // ../../node_modules/.bun/@gramio+format@0.3.4/node_modules/@gramio/format/dist/index.js
53487
+ // ../../node_modules/.bun/@gramio+format@0.3.4+32af5fe46ea567aa/node_modules/@gramio/format/dist/index.js
53092
53488
  var exports_dist3 = {};
53093
53489
  __export(exports_dist3, {
53094
53490
  underline: () => underline,
@@ -53553,10 +53949,10 @@ function formatSaveIndents(stringParts, ...strings) {
53553
53949
  return processRawFormat([...stringParts], strings);
53554
53950
  }
53555
53951
 
53556
- // ../../node_modules/.bun/gramio@0.4.12+cb432ad7c9c526f6/node_modules/gramio/dist/index.js
53952
+ // ../../node_modules/.bun/gramio@0.4.12+7be50f8974dcb86c/node_modules/gramio/dist/index.js
53557
53953
  var import_debug2 = __toESM(require_src2(), 1);
53558
53954
 
53559
- // ../../node_modules/.bun/gramio@0.4.12+cb432ad7c9c526f6/node_modules/gramio/dist/utils-CJfJNxc_.js
53955
+ // ../../node_modules/.bun/gramio@0.4.12+7be50f8974dcb86c/node_modules/gramio/dist/utils-CJfJNxc_.js
53560
53956
  var exports_utils_CJfJNxc_ = {};
53561
53957
  __export(exports_utils_CJfJNxc_, {
53562
53958
  w: () => withRetries,
@@ -54515,7 +54911,7 @@ class InlineQueryResult {
54515
54911
  }
54516
54912
  Symbol.metadata ??= Symbol("Symbol.metadata");
54517
54913
 
54518
- // ../../node_modules/.bun/gramio@0.4.12+cb432ad7c9c526f6/node_modules/gramio/dist/index.js
54914
+ // ../../node_modules/.bun/gramio@0.4.12+7be50f8974dcb86c/node_modules/gramio/dist/index.js
54519
54915
  class Composer2 {
54520
54916
  composer = Composer.builder();
54521
54917
  length = 0;
@@ -96770,6 +97166,19 @@ function shouldIgnoreReaderAbortError(error51) {
96770
97166
  const maybeError = error51;
96771
97167
  return maybeError.name === "AbortError" && maybeError.code === "ERR_STREAM_RELEASE_LOCK" && typeof maybeError.message === "string" && maybeError.message.includes("releaseLock");
96772
97168
  }
97169
+ var TRANSIENT_SPAWN_ERROR_CODES = new Set(["EAGAIN", "EMFILE", "ENFILE", "ENOMEM"]);
97170
+ var TMUX_SPAWN_UNAVAILABLE_EXIT = -2;
97171
+ function isTransientSpawnError(error51) {
97172
+ if (!error51 || typeof error51 !== "object") {
97173
+ return false;
97174
+ }
97175
+ const code2 = error51.code;
97176
+ if (typeof code2 === "string" && TRANSIENT_SPAWN_ERROR_CODES.has(code2)) {
97177
+ return true;
97178
+ }
97179
+ const message = error51.message;
97180
+ return typeof message === "string" && (message.includes("EAGAIN") || message.includes("posix_spawn") || message.includes("resource temporarily unavailable") || message.includes("Too many open files"));
97181
+ }
96773
97182
  function defaultRun(argv) {
96774
97183
  return new Promise((resolve3, reject) => {
96775
97184
  const subprocess = Bun.spawn(argv, {
@@ -96829,6 +97238,7 @@ class LocalExternalTmuxConnection {
96829
97238
  controlStartedAt = 0;
96830
97239
  controlRestartCount = 0;
96831
97240
  controlStderrTail = "";
97241
+ spawnUnavailableNotified = false;
96832
97242
  constructor(options, inputDeps = {}) {
96833
97243
  this.deviceId = options.deviceId;
96834
97244
  this.callbacks = options;
@@ -96880,7 +97290,13 @@ class LocalExternalTmuxConnection {
96880
97290
  this.stopControlClient();
96881
97291
  }
96882
97292
  requestSnapshot() {
96883
- this.requestSnapshotInternal();
97293
+ this.requestSnapshotInternal().catch((error51) => {
97294
+ if (isTransientSpawnError(error51)) {
97295
+ this.handleSpawnUnavailable(error51 instanceof Error ? error51.message : String(error51));
97296
+ return;
97297
+ }
97298
+ this.callbacks.onError(error51 instanceof Error ? error51 : new Error(String(error51)));
97299
+ });
96884
97300
  }
96885
97301
  sendInput(paneId, data) {
96886
97302
  if (!this.connected) {
@@ -97180,6 +97596,7 @@ class LocalExternalTmuxConnection {
97180
97596
  });
97181
97597
  const proc = this.deps.spawnControlClient([
97182
97598
  "tmux",
97599
+ ...config.tmuxSocket ? ["-L", config.tmuxSocket] : [],
97183
97600
  "-C",
97184
97601
  "attach-session",
97185
97602
  "-t",
@@ -97268,6 +97685,16 @@ class LocalExternalTmuxConnection {
97268
97685
  return;
97269
97686
  }
97270
97687
  const probe = await this.runTmuxAllowFailure(["has-session", "-t", this.sessionName]);
97688
+ if (probe.exitCode === TMUX_SPAWN_UNAVAILABLE_EXIT) {
97689
+ this.handleSpawnUnavailable(probe.stderr);
97690
+ this.controlRestartCount = Math.max(0, this.controlRestartCount - 1);
97691
+ if (this.connected && !this.manualDisconnect) {
97692
+ setTimeout(() => {
97693
+ this.reconnectControlClient(exitCode);
97694
+ }, CONTROL_RESTART_DELAY_MS * 4);
97695
+ }
97696
+ return;
97697
+ }
97271
97698
  if (probe.exitCode !== 0) {
97272
97699
  const message = probe.stderr.trim() || probe.stdout.trim() || "tmux session gone";
97273
97700
  console.warn(`[local] tmux session gone on ${this.deviceId}: ${message}`);
@@ -97379,6 +97806,11 @@ class LocalExternalTmuxConnection {
97379
97806
  ].join(SNAPSHOT_FIELD_SEPARATOR)
97380
97807
  ])
97381
97808
  ]);
97809
+ const transientResult = [sessionRes, windowsRes, panesRes].find((res) => res.exitCode === TMUX_SPAWN_UNAVAILABLE_EXIT);
97810
+ if (transientResult) {
97811
+ this.handleSpawnUnavailable(transientResult.stderr);
97812
+ return;
97813
+ }
97382
97814
  if (sessionRes.exitCode !== 0 || windowsRes.exitCode !== 0 || panesRes.exitCode !== 0) {
97383
97815
  const stderrBlob = `${sessionRes.stderr}
97384
97816
  ${windowsRes.stderr}
@@ -97402,6 +97834,7 @@ ${panesRes.stderr}`;
97402
97834
  this.parseSnapshotPanes(panesRes.stdout.split(/\r?\n/));
97403
97835
  this.discardInvalidSnapshot();
97404
97836
  this.controlSubscription?.prunePanes(new Set(this.getExpectedPaneIds()));
97837
+ this.markSpawnRecovered();
97405
97838
  this.emitSnapshot();
97406
97839
  }
97407
97840
  parseSnapshotSession(lines) {
@@ -97587,8 +98020,29 @@ ${panesRes.stderr}`;
97587
98020
  silentTelegram: true
97588
98021
  });
97589
98022
  }
98023
+ handleSpawnUnavailable(message) {
98024
+ if (this.spawnUnavailableNotified) {
98025
+ return;
98026
+ }
98027
+ this.spawnUnavailableNotified = true;
98028
+ const detail = (message || "tmux spawn unavailable (process table exhausted)").trim();
98029
+ console.warn(`[local] tmux spawn unavailable on ${this.deviceId} (process pressure), degrading without shutdown: ${detail}`);
98030
+ this.notifyRuntimeError(detail);
98031
+ }
98032
+ markSpawnRecovered() {
98033
+ this.spawnUnavailableNotified = false;
98034
+ }
97590
98035
  async runTmuxAllowFailure(argv) {
97591
- return this.deps.run(["tmux", ...argv]);
98036
+ const socketArgs = config.tmuxSocket ? ["-L", config.tmuxSocket] : [];
98037
+ try {
98038
+ return await this.deps.run(["tmux", ...socketArgs, ...argv]);
98039
+ } catch (error51) {
98040
+ if (isTransientSpawnError(error51)) {
98041
+ const message = error51 instanceof Error ? error51.message : String(error51);
98042
+ return { exitCode: TMUX_SPAWN_UNAVAILABLE_EXIT, stdout: "", stderr: message };
98043
+ }
98044
+ throw error51;
98045
+ }
97592
98046
  }
97593
98047
  isTmuxServerGoneMessage(message) {
97594
98048
  const normalized = message.toLowerCase();
@@ -102370,6 +102824,1037 @@ function json3(data, status = 200) {
102370
102824
  });
102371
102825
  }
102372
102826
 
102827
+ // ../../apps/gateway/src/db/file-roots.ts
102828
+ function getFileRoots() {
102829
+ const orm = getDb();
102830
+ return orm.select().from(fileRoots).orderBy(asc(fileRoots.sortOrder), asc(fileRoots.path)).all();
102831
+ }
102832
+ function getFileRootById(id) {
102833
+ const orm = getDb();
102834
+ return orm.select().from(fileRoots).where(eq(fileRoots.id, id)).get() ?? null;
102835
+ }
102836
+ function createFileRoot(input) {
102837
+ const orm = getDb();
102838
+ const maxRow = orm.select({ value: max(fileRoots.sortOrder) }).from(fileRoots).get();
102839
+ const record2 = {
102840
+ id: v4_default(),
102841
+ deviceId: input.deviceId,
102842
+ path: input.path,
102843
+ enabled: input.enabled ?? true,
102844
+ sortOrder: (maxRow?.value ?? -1) + 1,
102845
+ createdAt: new Date().toISOString()
102846
+ };
102847
+ orm.insert(fileRoots).values(record2).run();
102848
+ return record2;
102849
+ }
102850
+ function updateFileRoot(id, updates) {
102851
+ const current = getFileRootById(id);
102852
+ if (!current)
102853
+ return null;
102854
+ const next = {
102855
+ ...current,
102856
+ path: updates.path ?? current.path,
102857
+ enabled: updates.enabled ?? current.enabled,
102858
+ sortOrder: updates.sortOrder ?? current.sortOrder
102859
+ };
102860
+ const orm = getDb();
102861
+ orm.update(fileRoots).set({ path: next.path, enabled: next.enabled, sortOrder: next.sortOrder }).where(eq(fileRoots.id, id)).run();
102862
+ return next;
102863
+ }
102864
+ function deleteFileRoot(id) {
102865
+ const orm = getDb();
102866
+ const existing = orm.select({ id: fileRoots.id }).from(fileRoots).where(eq(fileRoots.id, id)).get();
102867
+ if (!existing)
102868
+ return false;
102869
+ orm.delete(fileRoots).where(eq(fileRoots.id, id)).run();
102870
+ return true;
102871
+ }
102872
+
102873
+ // ../../apps/gateway/src/files/device-storage.ts
102874
+ import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync3, realpathSync, rmSync as rmSync2 } from "fs";
102875
+ import { tmpdir as tmpdir2 } from "os";
102876
+ import { join as join5 } from "path";
102877
+
102878
+ // ../../apps/gateway/src/files/categorize.ts
102879
+ import { extname } from "path";
102880
+ var MAX_ENTRIES = 2000;
102881
+ var MAX_TEXT_BYTES = 2 * 1024 * 1024;
102882
+ var MARKDOWN_EXTS = new Set(["md", "markdown", "mdx"]);
102883
+ var IMAGE_EXTS = new Set(["png", "jpg", "jpeg", "gif", "webp", "bmp", "ico", "svg", "avif"]);
102884
+ var PDF_EXTS = new Set(["pdf"]);
102885
+ var ARCHIVE_EXTS = new Set(["zip", "tar", "gz", "tgz", "bz2", "xz", "7z", "rar", "zst"]);
102886
+ var AUDIO_EXTS = new Set(["mp3", "wav", "ogg", "flac", "m4a", "aac"]);
102887
+ var VIDEO_EXTS = new Set(["mp4", "webm", "mkv", "mov", "avi", "m4v"]);
102888
+ var TEXT_EXTS = new Set(["txt", "text", "log", "csv", "tsv", "rtf"]);
102889
+ var CODE_EXTS = new Set([
102890
+ "ts",
102891
+ "tsx",
102892
+ "js",
102893
+ "jsx",
102894
+ "mjs",
102895
+ "cjs",
102896
+ "json",
102897
+ "jsonc",
102898
+ "json5",
102899
+ "css",
102900
+ "scss",
102901
+ "sass",
102902
+ "less",
102903
+ "html",
102904
+ "htm",
102905
+ "xml",
102906
+ "vue",
102907
+ "svelte",
102908
+ "astro",
102909
+ "py",
102910
+ "pyi",
102911
+ "rb",
102912
+ "go",
102913
+ "rs",
102914
+ "java",
102915
+ "kt",
102916
+ "kts",
102917
+ "c",
102918
+ "h",
102919
+ "cpp",
102920
+ "cc",
102921
+ "cxx",
102922
+ "hpp",
102923
+ "hh",
102924
+ "cs",
102925
+ "php",
102926
+ "swift",
102927
+ "m",
102928
+ "mm",
102929
+ "sh",
102930
+ "bash",
102931
+ "zsh",
102932
+ "fish",
102933
+ "ps1",
102934
+ "bat",
102935
+ "cmd",
102936
+ "sql",
102937
+ "toml",
102938
+ "yaml",
102939
+ "yml",
102940
+ "ini",
102941
+ "cfg",
102942
+ "conf",
102943
+ "env",
102944
+ "properties",
102945
+ "gradle",
102946
+ "lua",
102947
+ "r",
102948
+ "dart",
102949
+ "scala",
102950
+ "clj",
102951
+ "cljs",
102952
+ "ex",
102953
+ "exs",
102954
+ "erl",
102955
+ "hs",
102956
+ "ml",
102957
+ "mli",
102958
+ "pl",
102959
+ "pm",
102960
+ "vim",
102961
+ "lock",
102962
+ "tf",
102963
+ "tfvars",
102964
+ "proto",
102965
+ "graphql",
102966
+ "gql",
102967
+ "prisma",
102968
+ "dockerfile",
102969
+ "makefile",
102970
+ "cmake",
102971
+ "nix",
102972
+ "zig",
102973
+ "d",
102974
+ "jl",
102975
+ "groovy",
102976
+ "patch",
102977
+ "diff"
102978
+ ]);
102979
+ var KNOWN_NOEXT = new Set([
102980
+ "makefile",
102981
+ "dockerfile",
102982
+ "license",
102983
+ "readme",
102984
+ "changelog",
102985
+ "authors",
102986
+ "copying",
102987
+ "notice",
102988
+ "procfile",
102989
+ "gemfile",
102990
+ "rakefile",
102991
+ "brewfile",
102992
+ "caddyfile",
102993
+ ".gitignore",
102994
+ ".gitattributes",
102995
+ ".gitmodules",
102996
+ ".env",
102997
+ ".editorconfig",
102998
+ ".npmrc",
102999
+ ".nvmrc",
103000
+ ".prettierrc",
103001
+ ".eslintrc",
103002
+ ".babelrc",
103003
+ ".dockerignore",
103004
+ ".zshrc",
103005
+ ".bashrc",
103006
+ ".profile"
103007
+ ]);
103008
+ var MIME_MAP = {
103009
+ png: "image/png",
103010
+ jpg: "image/jpeg",
103011
+ jpeg: "image/jpeg",
103012
+ gif: "image/gif",
103013
+ webp: "image/webp",
103014
+ bmp: "image/bmp",
103015
+ ico: "image/x-icon",
103016
+ svg: "image/svg+xml",
103017
+ avif: "image/avif",
103018
+ pdf: "application/pdf",
103019
+ mp3: "audio/mpeg",
103020
+ wav: "audio/wav",
103021
+ ogg: "audio/ogg",
103022
+ flac: "audio/flac",
103023
+ m4a: "audio/mp4",
103024
+ aac: "audio/aac",
103025
+ mp4: "video/mp4",
103026
+ webm: "video/webm",
103027
+ mkv: "video/x-matroska",
103028
+ mov: "video/quicktime",
103029
+ zip: "application/zip",
103030
+ gz: "application/gzip",
103031
+ tar: "application/x-tar",
103032
+ json: "application/json; charset=utf-8",
103033
+ txt: "text/plain; charset=utf-8"
103034
+ };
103035
+ function extOf(name24) {
103036
+ return extname(name24).slice(1).toLowerCase();
103037
+ }
103038
+ function categorize(name24) {
103039
+ const lower = name24.toLowerCase();
103040
+ const ext = extOf(lower);
103041
+ if (MARKDOWN_EXTS.has(ext))
103042
+ return "markdown";
103043
+ if (IMAGE_EXTS.has(ext))
103044
+ return "image";
103045
+ if (PDF_EXTS.has(ext))
103046
+ return "pdf";
103047
+ if (ARCHIVE_EXTS.has(ext))
103048
+ return "archive";
103049
+ if (AUDIO_EXTS.has(ext))
103050
+ return "audio";
103051
+ if (VIDEO_EXTS.has(ext))
103052
+ return "video";
103053
+ if (CODE_EXTS.has(ext))
103054
+ return "code";
103055
+ if (TEXT_EXTS.has(ext))
103056
+ return "text";
103057
+ if (!ext && KNOWN_NOEXT.has(lower))
103058
+ return "code";
103059
+ return "other";
103060
+ }
103061
+ function mimeOf(name24) {
103062
+ return MIME_MAP[extOf(name24)] ?? null;
103063
+ }
103064
+
103065
+ // ../../apps/gateway/src/files/queue.ts
103066
+ var GLOBAL_MAX_CONCURRENT = 4;
103067
+ var active = 0;
103068
+ var globalWaiters = [];
103069
+ var deviceChains = new Map;
103070
+ function acquireGlobalSlot() {
103071
+ if (active < GLOBAL_MAX_CONCURRENT) {
103072
+ active += 1;
103073
+ return Promise.resolve();
103074
+ }
103075
+ return new Promise((resolve3) => {
103076
+ globalWaiters.push(() => {
103077
+ active += 1;
103078
+ resolve3();
103079
+ });
103080
+ });
103081
+ }
103082
+ function releaseGlobalSlot() {
103083
+ active -= 1;
103084
+ const next = globalWaiters.shift();
103085
+ if (next)
103086
+ next();
103087
+ }
103088
+ function enqueueDeviceJob(deviceId, job) {
103089
+ const prev = deviceChains.get(deviceId) ?? Promise.resolve();
103090
+ const run = prev.catch(() => {
103091
+ return;
103092
+ }).then(async () => {
103093
+ await acquireGlobalSlot();
103094
+ try {
103095
+ return await job();
103096
+ } finally {
103097
+ releaseGlobalSlot();
103098
+ }
103099
+ });
103100
+ deviceChains.set(deviceId, run.then(() => {
103101
+ return;
103102
+ }, () => {
103103
+ return;
103104
+ }));
103105
+ return run;
103106
+ }
103107
+
103108
+ // ../../apps/gateway/src/files/rsync.ts
103109
+ class RsyncMissingLocalError extends Error {
103110
+ constructor(message) {
103111
+ super(message);
103112
+ this.name = "RsyncMissingLocalError";
103113
+ }
103114
+ }
103115
+ var SENSITIVE_ENV_KEYS = new Set(["DATABASE_URL", "NODE_ENV", "GATEWAY_PORT", "FE_PORT"]);
103116
+ function baseSubprocessEnv() {
103117
+ const out = {};
103118
+ for (const [k, v] of Object.entries(process.env)) {
103119
+ if (v === undefined)
103120
+ continue;
103121
+ if (k.startsWith("TMEX_"))
103122
+ continue;
103123
+ if (SENSITIVE_ENV_KEYS.has(k))
103124
+ continue;
103125
+ out[k] = v;
103126
+ }
103127
+ out.LC_ALL = "C";
103128
+ return out;
103129
+ }
103130
+ async function runRsync(argv, opts = {}) {
103131
+ const timeoutMs = opts.timeoutMs ?? 20000;
103132
+ let proc;
103133
+ try {
103134
+ proc = Bun.spawn(["rsync", ...argv], {
103135
+ env: { ...baseSubprocessEnv(), ...opts.env },
103136
+ stdout: "pipe",
103137
+ stderr: "pipe",
103138
+ stdin: "ignore"
103139
+ });
103140
+ } catch (error51) {
103141
+ throw new RsyncMissingLocalError(error51 instanceof Error ? error51.message : String(error51));
103142
+ }
103143
+ let timedOut = false;
103144
+ const killTimer = setTimeout(() => {
103145
+ timedOut = true;
103146
+ try {
103147
+ proc.kill();
103148
+ } catch {}
103149
+ }, timeoutMs);
103150
+ const onAbort = () => {
103151
+ try {
103152
+ proc.kill();
103153
+ } catch {}
103154
+ };
103155
+ opts.signal?.addEventListener("abort", onAbort, { once: true });
103156
+ try {
103157
+ const [stdout, stderr, exitCode] = await Promise.all([
103158
+ new Response(proc.stdout).text(),
103159
+ new Response(proc.stderr).text(),
103160
+ proc.exited
103161
+ ]);
103162
+ if (timedOut) {
103163
+ return { stdout, stderr: `${stderr}
103164
+ [tmex] rsync timed out`, exitCode: 124 };
103165
+ }
103166
+ return { stdout, stderr, exitCode };
103167
+ } finally {
103168
+ clearTimeout(killTimer);
103169
+ opts.signal?.removeEventListener("abort", onAbort);
103170
+ }
103171
+ }
103172
+ var LIST_RE = /^([dlspbc-][rwxsStT-]{9}[.+@]?)\s+([\d,]+)\s+(\d{4})\/(\d{2})\/(\d{2})\s+(\d{2}):(\d{2}):(\d{2})\s+(.*)$/;
103173
+ function typeFromPerms(perms) {
103174
+ switch (perms[0]) {
103175
+ case "d":
103176
+ return "dir";
103177
+ case "l":
103178
+ return "symlink";
103179
+ case "-":
103180
+ return "file";
103181
+ default:
103182
+ return "other";
103183
+ }
103184
+ }
103185
+ function parseListOnly(stdout) {
103186
+ const entries = [];
103187
+ for (const rawLine of stdout.split(`
103188
+ `)) {
103189
+ const line = rawLine.replace(/\r$/, "");
103190
+ const m = LIST_RE.exec(line);
103191
+ if (!m)
103192
+ continue;
103193
+ const type = typeFromPerms(m[1]);
103194
+ let name24 = m[9];
103195
+ if (type === "symlink") {
103196
+ const arrow = name24.indexOf(" -> ");
103197
+ if (arrow >= 0)
103198
+ name24 = name24.slice(0, arrow);
103199
+ }
103200
+ if (name24 === "" || name24 === "." || name24 === "..")
103201
+ continue;
103202
+ const sizeNum = Number.parseInt(m[2].replace(/,/g, ""), 10);
103203
+ const size = type === "dir" ? null : Number.isNaN(sizeNum) ? null : sizeNum;
103204
+ const modifiedAt = `${m[3]}-${m[4]}-${m[5]}T${m[6]}:${m[7]}:${m[8]}`;
103205
+ entries.push({ name: name24, type, size, modifiedAt });
103206
+ }
103207
+ return entries;
103208
+ }
103209
+ function classifyRsyncFailure(exitCode, stderr) {
103210
+ const s = stderr.toLowerCase();
103211
+ if (exitCode === 124)
103212
+ return "timeout";
103213
+ if (/command not found|rsync: not found|rsync error: error in rsync protocol.*\(code 127\)/.test(s)) {
103214
+ return "rsync_missing_remote";
103215
+ }
103216
+ if (/rsync: not found|exec: rsync: not found|bash: rsync/.test(s))
103217
+ return "rsync_missing_remote";
103218
+ if (/host key verification failed|could not resolve hostname|connection refused|connection timed out|no route to host|operation timed out|ssh: connect to host/.test(s)) {
103219
+ return "connection_failed";
103220
+ }
103221
+ if (/permission denied \((publickey|password|keyboard-interactive|gssapi|hostbased)/.test(s) || /too many authentication failures|authentication failed/.test(s)) {
103222
+ return "connection_failed";
103223
+ }
103224
+ if (/permission denied/.test(s))
103225
+ return "permission_denied";
103226
+ if (/no such file or directory|change_dir.*failed|link_stat.*failed|failed to stat/.test(s)) {
103227
+ return "not_found";
103228
+ }
103229
+ return "unknown";
103230
+ }
103231
+
103232
+ // ../../apps/gateway/src/files/ssh-command.ts
103233
+ import { chmodSync, mkdtempSync, rmSync, writeFileSync } from "fs";
103234
+ import { tmpdir } from "os";
103235
+ import { join as join4 } from "path";
103236
+ class RsyncAuthError extends Error {
103237
+ code;
103238
+ constructor(code2, message) {
103239
+ super(message);
103240
+ this.name = "RsyncAuthError";
103241
+ this.code = code2;
103242
+ }
103243
+ }
103244
+ var SSH_BASE_OPTS = ["-o", "StrictHostKeyChecking=accept-new", "-o", "ConnectTimeout=10"];
103245
+ function setupAskpass(secret) {
103246
+ const dir2 = mkdtempSync(join4(tmpdir(), "tmex-rsync-ap-"));
103247
+ const scriptPath = join4(dir2, "askpass.sh");
103248
+ writeFileSync(scriptPath, `#!/bin/sh
103249
+ printf '%s\\n' "$TMEX_RSYNC_SECRET"
103250
+ `, { mode: 448 });
103251
+ chmodSync(scriptPath, 448);
103252
+ return {
103253
+ env: {
103254
+ SSH_ASKPASS: scriptPath,
103255
+ SSH_ASKPASS_REQUIRE: "force",
103256
+ TMEX_RSYNC_SECRET: secret,
103257
+ DISPLAY: process.env.DISPLAY || ":0"
103258
+ },
103259
+ cleanup: () => {
103260
+ try {
103261
+ rmSync(dir2, { recursive: true, force: true });
103262
+ } catch {}
103263
+ }
103264
+ };
103265
+ }
103266
+ function writeTempKey(privateKey) {
103267
+ const dir2 = mkdtempSync(join4(tmpdir(), "tmex-rsync-key-"));
103268
+ const keyPath = join4(dir2, "id");
103269
+ writeFileSync(keyPath, privateKey, { mode: 384 });
103270
+ chmodSync(keyPath, 384);
103271
+ return {
103272
+ keyPath,
103273
+ cleanup: () => {
103274
+ try {
103275
+ rmSync(dir2, { recursive: true, force: true });
103276
+ } catch {}
103277
+ }
103278
+ };
103279
+ }
103280
+ async function buildRsyncDeviceSpec(device, decrypt2 = decryptWithContext, resolveConfig = resolveSshConnectConfig) {
103281
+ if (device.type === "local") {
103282
+ return { targetPrefix: "", rsh: undefined, env: {}, cleanup: () => {} };
103283
+ }
103284
+ if (device.authMode === "configRef" && device.sshConfigRef?.trim()) {
103285
+ const alias = device.sshConfigRef.trim();
103286
+ return {
103287
+ targetPrefix: `${alias}:`,
103288
+ rsh: ["ssh", ...SSH_BASE_OPTS, "-o", "BatchMode=yes"].join(" "),
103289
+ env: {},
103290
+ cleanup: () => {}
103291
+ };
103292
+ }
103293
+ const cfg = await resolveConfig(device, decrypt2);
103294
+ if (!cfg.host) {
103295
+ throw new RsyncAuthError("connection_failed", "SSH \u8BBE\u5907\u7F3A\u5C11 host");
103296
+ }
103297
+ const port = cfg.port ?? 22;
103298
+ const target = cfg.username ? `${cfg.username}@${cfg.host}` : cfg.host;
103299
+ const sshArgs = ["ssh", "-p", String(port), ...SSH_BASE_OPTS];
103300
+ const env = {};
103301
+ const cleanups = [];
103302
+ if (cfg.privateKey) {
103303
+ const { keyPath, cleanup } = writeTempKey(String(cfg.privateKey));
103304
+ sshArgs.push("-i", keyPath, "-o", "IdentitiesOnly=yes");
103305
+ cleanups.push(cleanup);
103306
+ if (cfg.agent) {
103307
+ env.SSH_AUTH_SOCK = String(cfg.agent);
103308
+ sshArgs.push("-o", "BatchMode=yes");
103309
+ } else if (cfg.passphrase) {
103310
+ const ap = setupAskpass(String(cfg.passphrase));
103311
+ Object.assign(env, ap.env);
103312
+ cleanups.push(ap.cleanup);
103313
+ } else {
103314
+ sshArgs.push("-o", "BatchMode=yes");
103315
+ }
103316
+ } else if (cfg.agent) {
103317
+ env.SSH_AUTH_SOCK = String(cfg.agent);
103318
+ sshArgs.push("-o", "BatchMode=yes");
103319
+ } else if (cfg.password) {
103320
+ const ap = setupAskpass(String(cfg.password));
103321
+ Object.assign(env, ap.env);
103322
+ cleanups.push(ap.cleanup);
103323
+ sshArgs.push("-o", "PreferredAuthentications=password,keyboard-interactive", "-o", "NumberOfPasswordPrompts=1");
103324
+ } else {
103325
+ throw new RsyncAuthError("auth_unsupported", "\u672A\u627E\u5230\u53EF\u7528\u4E8E rsync \u7684\u8BA4\u8BC1\u65B9\u5F0F\uFF08\u5BC6\u94A5 / ssh-agent / \u5BC6\u7801\uFF09");
103326
+ }
103327
+ return {
103328
+ targetPrefix: `${target}:`,
103329
+ rsh: sshArgs.join(" "),
103330
+ env,
103331
+ cleanup: () => {
103332
+ for (const c of cleanups)
103333
+ c();
103334
+ }
103335
+ };
103336
+ }
103337
+ function rsyncTargetArg(spec, remotePath) {
103338
+ if (!spec.targetPrefix)
103339
+ return remotePath;
103340
+ return `${spec.targetPrefix}${quoteShellArg(remotePath)}`;
103341
+ }
103342
+ function rsyncListArgs(spec, remotePath) {
103343
+ const args = ["--list-only"];
103344
+ if (spec.rsh)
103345
+ args.push("-e", spec.rsh);
103346
+ args.push(rsyncTargetArg(spec, remotePath));
103347
+ return args;
103348
+ }
103349
+ function rsyncCopyArgs(spec, remotePath, dest) {
103350
+ const args = ["-L"];
103351
+ if (spec.rsh)
103352
+ args.push("-e", spec.rsh);
103353
+ args.push(rsyncTargetArg(spec, remotePath), dest);
103354
+ return args;
103355
+ }
103356
+
103357
+ // ../../apps/gateway/src/files/device-storage.ts
103358
+ var RAW_MAX_BYTES = 50 * 1024 * 1024;
103359
+ var LIST_TIMEOUT_MS = 20000;
103360
+ var COPY_TIMEOUT_MS = 60000;
103361
+ function ok(data) {
103362
+ return { ok: true, data };
103363
+ }
103364
+ function fail(code2, detail) {
103365
+ return { ok: false, code: code2, detail };
103366
+ }
103367
+ function posixNormalize(p) {
103368
+ const isAbs = p.startsWith("/");
103369
+ const out = [];
103370
+ for (const seg of p.split("/")) {
103371
+ if (seg === "" || seg === ".")
103372
+ continue;
103373
+ if (seg === "..") {
103374
+ if (out.length && out[out.length - 1] !== "..")
103375
+ out.pop();
103376
+ else if (!isAbs)
103377
+ out.push("..");
103378
+ continue;
103379
+ }
103380
+ out.push(seg);
103381
+ }
103382
+ const joined = out.join("/");
103383
+ return isAbs ? `/${joined}` : joined;
103384
+ }
103385
+ function posixJoin(dir2, name24) {
103386
+ return dir2 === "/" ? `/${name24}` : `${dir2}/${name24}`;
103387
+ }
103388
+ function posixBasename(p) {
103389
+ const i = p.lastIndexOf("/");
103390
+ const base = i >= 0 ? p.slice(i + 1) : p;
103391
+ return base || p;
103392
+ }
103393
+ function checkAndNormalize(device, rootPath, inputPath) {
103394
+ if (!inputPath || !inputPath.startsWith("/"))
103395
+ return { ok: false, code: "invalid" };
103396
+ const normRoot = posixNormalize(rootPath);
103397
+ const normPath = posixNormalize(inputPath);
103398
+ const prefix = normRoot === "/" ? "/" : `${normRoot}/`;
103399
+ if (!(normPath === normRoot || normPath.startsWith(prefix))) {
103400
+ return { ok: false, code: "outside_roots" };
103401
+ }
103402
+ if (device.type === "local") {
103403
+ let realRoot;
103404
+ let realTarget;
103405
+ try {
103406
+ realRoot = realpathSync(normRoot);
103407
+ } catch {
103408
+ return { ok: false, code: "root_not_found" };
103409
+ }
103410
+ try {
103411
+ realTarget = realpathSync(normPath);
103412
+ } catch {
103413
+ return { ok: false, code: "not_found" };
103414
+ }
103415
+ const rPrefix = realRoot === "/" ? "/" : `${realRoot}/`;
103416
+ if (!(realTarget === realRoot || realTarget.startsWith(rPrefix))) {
103417
+ return { ok: false, code: "outside_roots" };
103418
+ }
103419
+ }
103420
+ return { ok: true, path: normPath };
103421
+ }
103422
+ function resolveContext(rootId) {
103423
+ const root = getFileRootById(rootId);
103424
+ if (!root)
103425
+ return { ok: false, code: "root_not_found" };
103426
+ if (!root.enabled)
103427
+ return { ok: false, code: "root_disabled" };
103428
+ const device = getDeviceById(root.deviceId);
103429
+ if (!device)
103430
+ return { ok: false, code: "device_not_found" };
103431
+ return { ok: true, ctx: { root, device } };
103432
+ }
103433
+ function entryToDto(entry, parentPath) {
103434
+ return {
103435
+ name: entry.name,
103436
+ path: posixJoin(parentPath, entry.name),
103437
+ type: entry.type,
103438
+ category: entry.type === "dir" ? "directory" : categorize(entry.name),
103439
+ size: entry.size,
103440
+ modifiedAt: entry.modifiedAt,
103441
+ isSymlink: entry.type === "symlink"
103442
+ };
103443
+ }
103444
+ function sortEntries(entries) {
103445
+ entries.sort((a, b3) => {
103446
+ const ad = a.type === "dir" ? 0 : 1;
103447
+ const bd = b3.type === "dir" ? 0 : 1;
103448
+ if (ad !== bd)
103449
+ return ad - bd;
103450
+ return a.name.localeCompare(b3.name, undefined, { numeric: true, sensitivity: "base" });
103451
+ });
103452
+ }
103453
+ function looksBinary(buf) {
103454
+ const len = Math.min(buf.length, 8192);
103455
+ for (let i = 0;i < len; i++) {
103456
+ if (buf[i] === 0)
103457
+ return true;
103458
+ }
103459
+ return false;
103460
+ }
103461
+ async function listDirectory(rootId, inputPath) {
103462
+ const r = resolveContext(rootId);
103463
+ if (!r.ok)
103464
+ return fail(r.code);
103465
+ const { root, device } = r.ctx;
103466
+ const norm = checkAndNormalize(device, root.path, inputPath ?? root.path);
103467
+ if (!norm.ok)
103468
+ return fail(norm.code);
103469
+ return enqueueDeviceJob(device.id, async () => {
103470
+ let spec;
103471
+ try {
103472
+ spec = await buildRsyncDeviceSpec(device);
103473
+ } catch (error51) {
103474
+ if (error51 instanceof RsyncAuthError)
103475
+ return fail(error51.code, error51.message);
103476
+ throw error51;
103477
+ }
103478
+ try {
103479
+ const listPath = norm.path.endsWith("/") ? norm.path : `${norm.path}/`;
103480
+ let res;
103481
+ try {
103482
+ res = await runRsync(rsyncListArgs(spec, listPath), {
103483
+ env: spec.env,
103484
+ timeoutMs: LIST_TIMEOUT_MS
103485
+ });
103486
+ } catch (error51) {
103487
+ if (error51 instanceof RsyncMissingLocalError)
103488
+ return fail("rsync_missing_local");
103489
+ throw error51;
103490
+ }
103491
+ if (res.exitCode !== 0)
103492
+ return fail(classifyRsyncFailure(res.exitCode, res.stderr), res.stderr);
103493
+ const parsed = parseListOnly(res.stdout);
103494
+ const truncated = parsed.length > MAX_ENTRIES;
103495
+ const slice = truncated ? parsed.slice(0, MAX_ENTRIES) : parsed;
103496
+ const entries = slice.map((e) => entryToDto(e, norm.path));
103497
+ sortEntries(entries);
103498
+ return ok({ path: norm.path, entries, truncated });
103499
+ } finally {
103500
+ spec.cleanup();
103501
+ }
103502
+ });
103503
+ }
103504
+ async function statViaRsync(spec, normPath) {
103505
+ let res;
103506
+ try {
103507
+ res = await runRsync(rsyncListArgs(spec, normPath), {
103508
+ env: spec.env,
103509
+ timeoutMs: LIST_TIMEOUT_MS
103510
+ });
103511
+ } catch (error51) {
103512
+ if (error51 instanceof RsyncMissingLocalError)
103513
+ return fail("rsync_missing_local");
103514
+ throw error51;
103515
+ }
103516
+ if (res.exitCode !== 0)
103517
+ return fail(classifyRsyncFailure(res.exitCode, res.stderr), res.stderr);
103518
+ const entry = parseListOnly(res.stdout)[0];
103519
+ if (!entry)
103520
+ return fail("not_found");
103521
+ return ok(entry);
103522
+ }
103523
+ async function statFile(rootId, inputPath) {
103524
+ const r = resolveContext(rootId);
103525
+ if (!r.ok)
103526
+ return fail(r.code);
103527
+ const { root, device } = r.ctx;
103528
+ const norm = checkAndNormalize(device, root.path, inputPath);
103529
+ if (!norm.ok)
103530
+ return fail(norm.code);
103531
+ return enqueueDeviceJob(device.id, async () => {
103532
+ let spec;
103533
+ try {
103534
+ spec = await buildRsyncDeviceSpec(device);
103535
+ } catch (error51) {
103536
+ if (error51 instanceof RsyncAuthError)
103537
+ return fail(error51.code, error51.message);
103538
+ throw error51;
103539
+ }
103540
+ try {
103541
+ const st = await statViaRsync(spec, norm.path);
103542
+ if (!st.ok)
103543
+ return st;
103544
+ const name24 = posixBasename(norm.path);
103545
+ const isDir = st.data.type === "dir";
103546
+ const type = isDir ? "dir" : st.data.type === "symlink" ? "symlink" : "file";
103547
+ return ok({
103548
+ path: norm.path,
103549
+ name: name24,
103550
+ type,
103551
+ category: isDir ? "directory" : categorize(name24),
103552
+ size: isDir ? 0 : st.data.size ?? 0,
103553
+ modifiedAt: st.data.modifiedAt,
103554
+ mime: isDir ? null : mimeOf(name24),
103555
+ isSymlink: st.data.type === "symlink"
103556
+ });
103557
+ } finally {
103558
+ spec.cleanup();
103559
+ }
103560
+ });
103561
+ }
103562
+ async function copyToBuffer(spec, normPath) {
103563
+ const dir2 = mkdtempSync2(join5(tmpdir2(), "tmex-rfile-"));
103564
+ const dest = join5(dir2, "f");
103565
+ try {
103566
+ let res;
103567
+ try {
103568
+ res = await runRsync(rsyncCopyArgs(spec, normPath, dest), {
103569
+ env: spec.env,
103570
+ timeoutMs: COPY_TIMEOUT_MS
103571
+ });
103572
+ } catch (error51) {
103573
+ if (error51 instanceof RsyncMissingLocalError)
103574
+ return fail("rsync_missing_local");
103575
+ throw error51;
103576
+ }
103577
+ if (res.exitCode !== 0)
103578
+ return fail(classifyRsyncFailure(res.exitCode, res.stderr), res.stderr);
103579
+ return ok(readFileSync3(dest));
103580
+ } finally {
103581
+ try {
103582
+ rmSync2(dir2, { recursive: true, force: true });
103583
+ } catch {}
103584
+ }
103585
+ }
103586
+ async function readTextFile(rootId, inputPath) {
103587
+ const r = resolveContext(rootId);
103588
+ if (!r.ok)
103589
+ return fail(r.code);
103590
+ const { root, device } = r.ctx;
103591
+ const norm = checkAndNormalize(device, root.path, inputPath);
103592
+ if (!norm.ok)
103593
+ return fail(norm.code);
103594
+ return enqueueDeviceJob(device.id, async () => {
103595
+ let spec;
103596
+ try {
103597
+ spec = await buildRsyncDeviceSpec(device);
103598
+ } catch (error51) {
103599
+ if (error51 instanceof RsyncAuthError)
103600
+ return fail(error51.code, error51.message);
103601
+ throw error51;
103602
+ }
103603
+ try {
103604
+ const st = await statViaRsync(spec, norm.path);
103605
+ if (!st.ok)
103606
+ return st;
103607
+ if (st.data.type === "dir")
103608
+ return fail("is_directory");
103609
+ if (st.data.size != null && st.data.size > MAX_TEXT_BYTES)
103610
+ return fail("too_large");
103611
+ const buf = await copyToBuffer(spec, norm.path);
103612
+ if (!buf.ok)
103613
+ return buf;
103614
+ if (buf.data.length > MAX_TEXT_BYTES)
103615
+ return fail("too_large");
103616
+ if (looksBinary(buf.data))
103617
+ return fail("binary");
103618
+ const name24 = posixBasename(norm.path);
103619
+ return ok({
103620
+ path: norm.path,
103621
+ name: name24,
103622
+ category: categorize(name24),
103623
+ encoding: "utf-8",
103624
+ content: buf.data.toString("utf-8"),
103625
+ size: st.data.size ?? buf.data.length,
103626
+ truncated: false
103627
+ });
103628
+ } finally {
103629
+ spec.cleanup();
103630
+ }
103631
+ });
103632
+ }
103633
+ async function readRawFile(rootId, inputPath) {
103634
+ const r = resolveContext(rootId);
103635
+ if (!r.ok)
103636
+ return fail(r.code);
103637
+ const { root, device } = r.ctx;
103638
+ const norm = checkAndNormalize(device, root.path, inputPath);
103639
+ if (!norm.ok)
103640
+ return fail(norm.code);
103641
+ return enqueueDeviceJob(device.id, async () => {
103642
+ let spec;
103643
+ try {
103644
+ spec = await buildRsyncDeviceSpec(device);
103645
+ } catch (error51) {
103646
+ if (error51 instanceof RsyncAuthError)
103647
+ return fail(error51.code, error51.message);
103648
+ throw error51;
103649
+ }
103650
+ try {
103651
+ const st = await statViaRsync(spec, norm.path);
103652
+ if (!st.ok)
103653
+ return st;
103654
+ if (st.data.type === "dir")
103655
+ return fail("is_directory");
103656
+ if (st.data.size != null && st.data.size > RAW_MAX_BYTES)
103657
+ return fail("too_large");
103658
+ const buf = await copyToBuffer(spec, norm.path);
103659
+ if (!buf.ok)
103660
+ return buf;
103661
+ if (buf.data.length > RAW_MAX_BYTES)
103662
+ return fail("too_large");
103663
+ const name24 = posixBasename(norm.path);
103664
+ return ok({ data: new Uint8Array(buf.data), name: name24, mime: mimeOf(name24) });
103665
+ } finally {
103666
+ spec.cleanup();
103667
+ }
103668
+ });
103669
+ }
103670
+
103671
+ // ../../apps/gateway/src/api/files.ts
103672
+ function json4(data, status = 200, headers = {}) {
103673
+ return new Response(JSON.stringify(data), {
103674
+ status,
103675
+ headers: { "Content-Type": "application/json", ...headers }
103676
+ });
103677
+ }
103678
+ var CODE_STATUS = {
103679
+ invalid: 400,
103680
+ outside_roots: 403,
103681
+ not_found: 404,
103682
+ not_a_directory: 400,
103683
+ is_directory: 400,
103684
+ too_large: 413,
103685
+ binary: 415,
103686
+ permission_denied: 403,
103687
+ device_not_found: 404,
103688
+ root_not_found: 404,
103689
+ root_disabled: 403,
103690
+ connection_failed: 502,
103691
+ auth_unsupported: 400,
103692
+ rsync_missing_local: 502,
103693
+ rsync_missing_remote: 502,
103694
+ timeout: 504,
103695
+ unknown: 500
103696
+ };
103697
+ function codeError(code2, detail) {
103698
+ return json4({ error: code2, code: code2, detail }, CODE_STATUS[code2]);
103699
+ }
103700
+ function rootDisplayName(p) {
103701
+ if (p === "/")
103702
+ return "/";
103703
+ const i = p.replace(/\/$/, "").lastIndexOf("/");
103704
+ const base = i >= 0 ? p.replace(/\/$/, "").slice(i + 1) : p;
103705
+ return base || p;
103706
+ }
103707
+ function toRootDto(root) {
103708
+ const device = getDeviceById(root.deviceId);
103709
+ return {
103710
+ id: root.id,
103711
+ deviceId: root.deviceId,
103712
+ deviceName: device?.name ?? null,
103713
+ deviceType: device?.type ?? null,
103714
+ path: root.path,
103715
+ name: rootDisplayName(root.path),
103716
+ enabled: root.enabled,
103717
+ sortOrder: root.sortOrder
103718
+ };
103719
+ }
103720
+ function handleListRoots() {
103721
+ return json4({ roots: getFileRoots().map(toRootDto) });
103722
+ }
103723
+ async function handleCreateRoot(req) {
103724
+ let body;
103725
+ try {
103726
+ body = await req.json();
103727
+ } catch {
103728
+ return json4({ error: t2("apiError.invalidRequest") }, 400);
103729
+ }
103730
+ const deviceId = typeof body.deviceId === "string" ? body.deviceId : "";
103731
+ const path = typeof body.path === "string" ? body.path.trim() : "";
103732
+ if (!deviceId || !getDeviceById(deviceId)) {
103733
+ return json4({ error: t2("apiError.fileRootDeviceInvalid") }, 400);
103734
+ }
103735
+ if (!path || !path.startsWith("/")) {
103736
+ return json4({ error: t2("apiError.fileRootInvalid") }, 400);
103737
+ }
103738
+ const existing = getFileRoots().find((r) => r.deviceId === deviceId && r.path === path);
103739
+ if (existing) {
103740
+ return json4({ error: t2("apiError.fileRootDuplicate") }, 400);
103741
+ }
103742
+ const record2 = createFileRoot({ deviceId, path, enabled: body.enabled ?? true });
103743
+ return json4({ root: toRootDto(record2) }, 201);
103744
+ }
103745
+ async function handleUpdateRoot(req, id) {
103746
+ const existing = getFileRootById(id);
103747
+ if (!existing)
103748
+ return json4({ error: t2("apiError.notFound") }, 404);
103749
+ let body;
103750
+ try {
103751
+ body = await req.json();
103752
+ } catch {
103753
+ return json4({ error: t2("apiError.invalidRequest") }, 400);
103754
+ }
103755
+ const updates = {};
103756
+ if (body.path !== undefined) {
103757
+ const path = typeof body.path === "string" ? body.path.trim() : "";
103758
+ if (!path || !path.startsWith("/"))
103759
+ return json4({ error: t2("apiError.fileRootInvalid") }, 400);
103760
+ const dup = getFileRoots().find((r) => r.id !== id && r.deviceId === existing.deviceId && r.path === path);
103761
+ if (dup)
103762
+ return json4({ error: t2("apiError.fileRootDuplicate") }, 400);
103763
+ updates.path = path;
103764
+ }
103765
+ if (body.enabled !== undefined) {
103766
+ if (typeof body.enabled !== "boolean")
103767
+ return json4({ error: t2("apiError.invalidRequest") }, 400);
103768
+ updates.enabled = body.enabled;
103769
+ }
103770
+ if (body.sortOrder !== undefined) {
103771
+ if (typeof body.sortOrder !== "number")
103772
+ return json4({ error: t2("apiError.invalidRequest") }, 400);
103773
+ updates.sortOrder = body.sortOrder;
103774
+ }
103775
+ const updated = updateFileRoot(id, updates);
103776
+ if (!updated)
103777
+ return json4({ error: t2("apiError.notFound") }, 404);
103778
+ return json4({ root: toRootDto(updated) });
103779
+ }
103780
+ function handleDeleteRoot(id) {
103781
+ const okDelete = deleteFileRoot(id);
103782
+ if (!okDelete)
103783
+ return json4({ error: t2("apiError.notFound") }, 404);
103784
+ return json4({ success: true });
103785
+ }
103786
+ async function handleList(url2) {
103787
+ const rootId = url2.searchParams.get("rootId");
103788
+ if (!rootId)
103789
+ return json4({ error: t2("apiError.invalidRequest") }, 400);
103790
+ const path = url2.searchParams.get("path");
103791
+ const result = await listDirectory(rootId, path);
103792
+ if (!result.ok)
103793
+ return codeError(result.code, result.detail);
103794
+ return json4(result.data);
103795
+ }
103796
+ async function handleContent(url2) {
103797
+ const rootId = url2.searchParams.get("rootId");
103798
+ const path = url2.searchParams.get("path");
103799
+ if (!rootId || !path)
103800
+ return json4({ error: t2("apiError.invalidRequest") }, 400);
103801
+ const result = await readTextFile(rootId, path);
103802
+ if (!result.ok)
103803
+ return codeError(result.code, result.detail);
103804
+ return json4(result.data);
103805
+ }
103806
+ async function handleStat(url2) {
103807
+ const rootId = url2.searchParams.get("rootId");
103808
+ const path = url2.searchParams.get("path");
103809
+ if (!rootId || !path)
103810
+ return json4({ error: t2("apiError.invalidRequest") }, 400);
103811
+ const result = await statFile(rootId, path);
103812
+ if (!result.ok)
103813
+ return codeError(result.code, result.detail);
103814
+ return json4(result.data);
103815
+ }
103816
+ async function handleRaw(url2) {
103817
+ const rootId = url2.searchParams.get("rootId");
103818
+ const path = url2.searchParams.get("path");
103819
+ if (!rootId || !path)
103820
+ return json4({ error: t2("apiError.invalidRequest") }, 400);
103821
+ const result = await readRawFile(rootId, path);
103822
+ if (!result.ok)
103823
+ return codeError(result.code, result.detail);
103824
+ const headers = {
103825
+ "Content-Type": result.data.mime ?? "application/octet-stream"
103826
+ };
103827
+ const download2 = url2.searchParams.get("download");
103828
+ if (download2 === "1" || download2 === "true") {
103829
+ const encoded = encodeURIComponent(result.data.name);
103830
+ const ascii = result.data.name.replace(/["\\\r\n]/g, "_");
103831
+ headers["Content-Disposition"] = `attachment; filename="${ascii}"; filename*=UTF-8''${encoded}`;
103832
+ }
103833
+ return new Response(result.data.data, { status: 200, headers });
103834
+ }
103835
+ function handleFilesApiRequest(req, path) {
103836
+ const url2 = new URL(req.url);
103837
+ if (path === "/api/files/roots" && req.method === "GET")
103838
+ return handleListRoots();
103839
+ if (path === "/api/files/roots" && req.method === "POST")
103840
+ return handleCreateRoot(req);
103841
+ if (path.match(/^\/api\/files\/roots\/[^/]+$/) && req.method === "PATCH") {
103842
+ return handleUpdateRoot(req, decodeURIComponent(path.split("/")[4]));
103843
+ }
103844
+ if (path.match(/^\/api\/files\/roots\/[^/]+$/) && req.method === "DELETE") {
103845
+ return handleDeleteRoot(decodeURIComponent(path.split("/")[4]));
103846
+ }
103847
+ if (path === "/api/files/list" && req.method === "GET")
103848
+ return handleList(url2);
103849
+ if (path === "/api/files/content" && req.method === "GET")
103850
+ return handleContent(url2);
103851
+ if (path === "/api/files/stat" && req.method === "GET")
103852
+ return handleStat(url2);
103853
+ if (path === "/api/files/raw" && req.method === "GET")
103854
+ return handleRaw(url2);
103855
+ return;
103856
+ }
103857
+
102373
103858
  // ../../apps/gateway/src/api/llm.ts
102374
103859
  var PROTOCOLS = ["openai-chat", "openai-responses"];
102375
103860
  var SEARCH_PROVIDERS = ["none", "tavily", "brave"];
@@ -102463,31 +103948,31 @@ async function refreshModelsCache(provider) {
102463
103948
  }
102464
103949
  async function handleListProviders() {
102465
103950
  const providers = getAllLlmProviders().map(toProviderDto);
102466
- return json4({ providers });
103951
+ return json5({ providers });
102467
103952
  }
102468
103953
  async function handleCreateProvider(req) {
102469
103954
  const raw = await readJsonObjectBody2(req);
102470
103955
  if (!raw) {
102471
- return json4({ error: t2("apiError.invalidRequest") }, 400);
103956
+ return json5({ error: t2("apiError.invalidRequest") }, 400);
102472
103957
  }
102473
103958
  const body = raw;
102474
103959
  const name24 = typeof body.name === "string" ? body.name.trim() : "";
102475
103960
  if (!name24) {
102476
- return json4({ error: t2("apiError.llmProviderNameRequired") }, 400);
103961
+ return json5({ error: t2("apiError.llmProviderNameRequired") }, 400);
102477
103962
  }
102478
103963
  if (!PROTOCOLS.includes(body.protocol)) {
102479
- return json4({ error: t2("apiError.llmProviderProtocolInvalid") }, 400);
103964
+ return json5({ error: t2("apiError.llmProviderProtocolInvalid") }, 400);
102480
103965
  }
102481
103966
  const baseUrl = typeof body.baseUrl === "string" ? body.baseUrl.trim() : "";
102482
103967
  if (!baseUrl || !isValidBaseUrl(baseUrl)) {
102483
- return json4({ error: t2("apiError.llmProviderBaseUrlInvalid") }, 400);
103968
+ return json5({ error: t2("apiError.llmProviderBaseUrlInvalid") }, 400);
102484
103969
  }
102485
103970
  const apiKey = typeof body.apiKey === "string" ? body.apiKey.trim() : "";
102486
103971
  if (!apiKey) {
102487
- return json4({ error: t2("apiError.llmProviderApiKeyRequired") }, 400);
103972
+ return json5({ error: t2("apiError.llmProviderApiKeyRequired") }, 400);
102488
103973
  }
102489
103974
  if (body.enabled !== undefined && typeof body.enabled !== "boolean") {
102490
- return json4({ error: t2("apiError.invalidRequest") }, 400);
103975
+ return json5({ error: t2("apiError.invalidRequest") }, 400);
102491
103976
  }
102492
103977
  const created = createLlmProvider({
102493
103978
  name: name24,
@@ -102497,41 +103982,41 @@ async function handleCreateProvider(req) {
102497
103982
  enabled: body.enabled ?? true
102498
103983
  });
102499
103984
  const { provider, modelsError } = await refreshModelsCache(created);
102500
- return json4({ provider: toProviderDto(provider), ...modelsError ? { modelsError } : {} }, 201);
103985
+ return json5({ provider: toProviderDto(provider), ...modelsError ? { modelsError } : {} }, 201);
102501
103986
  }
102502
103987
  async function handleUpdateProvider(req, id) {
102503
103988
  const existing = getLlmProviderById(id);
102504
103989
  if (!existing) {
102505
- return json4({ error: t2("apiError.llmProviderNotFound") }, 404);
103990
+ return json5({ error: t2("apiError.llmProviderNotFound") }, 404);
102506
103991
  }
102507
103992
  const raw = await readJsonObjectBody2(req);
102508
103993
  if (!raw) {
102509
- return json4({ error: t2("apiError.invalidRequest") }, 400);
103994
+ return json5({ error: t2("apiError.invalidRequest") }, 400);
102510
103995
  }
102511
103996
  const body = raw;
102512
103997
  const updates = {};
102513
103998
  if (body.name !== undefined) {
102514
103999
  const name24 = typeof body.name === "string" ? body.name.trim() : "";
102515
104000
  if (!name24) {
102516
- return json4({ error: t2("apiError.llmProviderNameRequired") }, 400);
104001
+ return json5({ error: t2("apiError.llmProviderNameRequired") }, 400);
102517
104002
  }
102518
104003
  updates.name = name24;
102519
104004
  }
102520
104005
  if (body.protocol !== undefined) {
102521
104006
  if (!PROTOCOLS.includes(body.protocol)) {
102522
- return json4({ error: t2("apiError.llmProviderProtocolInvalid") }, 400);
104007
+ return json5({ error: t2("apiError.llmProviderProtocolInvalid") }, 400);
102523
104008
  }
102524
104009
  updates.protocol = body.protocol;
102525
104010
  }
102526
104011
  if (body.baseUrl !== undefined) {
102527
104012
  const baseUrl = typeof body.baseUrl === "string" ? body.baseUrl.trim() : "";
102528
104013
  if (!isValidBaseUrl(baseUrl)) {
102529
- return json4({ error: t2("apiError.llmProviderBaseUrlInvalid") }, 400);
104014
+ return json5({ error: t2("apiError.llmProviderBaseUrlInvalid") }, 400);
102530
104015
  }
102531
104016
  updates.baseUrl = baseUrl;
102532
104017
  }
102533
104018
  if (body.apiKey !== undefined && typeof body.apiKey !== "string") {
102534
- return json4({ error: t2("apiError.invalidRequest") }, 400);
104019
+ return json5({ error: t2("apiError.invalidRequest") }, 400);
102535
104020
  }
102536
104021
  const apiKey = body.apiKey?.trim();
102537
104022
  if (apiKey) {
@@ -102539,26 +104024,26 @@ async function handleUpdateProvider(req, id) {
102539
104024
  }
102540
104025
  if (body.enabled !== undefined) {
102541
104026
  if (typeof body.enabled !== "boolean") {
102542
- return json4({ error: t2("apiError.invalidRequest") }, 400);
104027
+ return json5({ error: t2("apiError.invalidRequest") }, 400);
102543
104028
  }
102544
104029
  updates.enabled = body.enabled;
102545
104030
  }
102546
104031
  const isStringArray = (value) => Array.isArray(value) && value.every((item) => typeof item === "string");
102547
104032
  if (body.manualModels !== undefined) {
102548
104033
  if (!isStringArray(body.manualModels)) {
102549
- return json4({ error: t2("apiError.invalidRequest") }, 400);
104034
+ return json5({ error: t2("apiError.invalidRequest") }, 400);
102550
104035
  }
102551
104036
  updates.manualModels = [...new Set(body.manualModels.map((m) => m.trim()).filter(Boolean))];
102552
104037
  }
102553
104038
  if (body.disabledModels !== undefined) {
102554
104039
  if (!isStringArray(body.disabledModels)) {
102555
- return json4({ error: t2("apiError.invalidRequest") }, 400);
104040
+ return json5({ error: t2("apiError.invalidRequest") }, 400);
102556
104041
  }
102557
104042
  updates.disabledModels = [...new Set(body.disabledModels.map((m) => m.trim()).filter(Boolean))];
102558
104043
  }
102559
104044
  let provider = updateLlmProvider(id, updates);
102560
104045
  if (!provider) {
102561
- return json4({ error: t2("apiError.llmProviderNotFound") }, 404);
104046
+ return json5({ error: t2("apiError.llmProviderNotFound") }, 404);
102562
104047
  }
102563
104048
  const credentialsChanged = updates.baseUrl !== undefined && updates.baseUrl !== existing.baseUrl || updates.apiKeyEnc !== undefined;
102564
104049
  let modelsError;
@@ -102567,76 +104052,76 @@ async function handleUpdateProvider(req, id) {
102567
104052
  provider = refreshed.provider;
102568
104053
  modelsError = refreshed.modelsError;
102569
104054
  }
102570
- return json4({ provider: toProviderDto(provider), ...modelsError ? { modelsError } : {} });
104055
+ return json5({ provider: toProviderDto(provider), ...modelsError ? { modelsError } : {} });
102571
104056
  }
102572
104057
  async function handleDeleteProvider(id) {
102573
104058
  const existing = getLlmProviderById(id);
102574
104059
  if (!existing) {
102575
- return json4({ error: t2("apiError.llmProviderNotFound") }, 404);
104060
+ return json5({ error: t2("apiError.llmProviderNotFound") }, 404);
102576
104061
  }
102577
104062
  deleteLlmProvider(id);
102578
- return json4({ success: true });
104063
+ return json5({ success: true });
102579
104064
  }
102580
104065
  async function handleRefreshProviderModels(id) {
102581
104066
  const existing = getLlmProviderById(id);
102582
104067
  if (!existing) {
102583
- return json4({ error: t2("apiError.llmProviderNotFound") }, 404);
104068
+ return json5({ error: t2("apiError.llmProviderNotFound") }, 404);
102584
104069
  }
102585
104070
  const { models, modelsError } = await refreshModelsCache(existing);
102586
104071
  if (modelsError !== undefined || models === undefined) {
102587
- return json4({ error: modelsError ?? t2("apiError.invalidRequest") }, 502);
104072
+ return json5({ error: modelsError ?? t2("apiError.invalidRequest") }, 502);
102588
104073
  }
102589
- return json4({ models });
104074
+ return json5({ models });
102590
104075
  }
102591
104076
  async function handleGetSettings() {
102592
- return json4({ settings: toSettingsDto(getAgentSettings()) });
104077
+ return json5({ settings: toSettingsDto(getAgentSettings()) });
102593
104078
  }
102594
104079
  async function handleUpdateSettings(req) {
102595
104080
  const raw = await readJsonObjectBody2(req);
102596
104081
  if (!raw) {
102597
- return json4({ error: t2("apiError.invalidRequest") }, 400);
104082
+ return json5({ error: t2("apiError.invalidRequest") }, 400);
102598
104083
  }
102599
104084
  const body = raw;
102600
104085
  const updates = {};
102601
104086
  if (body.searchProvider !== undefined) {
102602
104087
  if (!SEARCH_PROVIDERS.includes(body.searchProvider)) {
102603
- return json4({ error: t2("apiError.llmSearchProviderInvalid") }, 400);
104088
+ return json5({ error: t2("apiError.llmSearchProviderInvalid") }, 400);
102604
104089
  }
102605
104090
  updates.searchProvider = body.searchProvider;
102606
104091
  }
102607
104092
  if (body.defaultProviderId !== undefined) {
102608
104093
  if (body.defaultProviderId !== null && typeof body.defaultProviderId !== "string") {
102609
- return json4({ error: t2("apiError.invalidRequest") }, 400);
104094
+ return json5({ error: t2("apiError.invalidRequest") }, 400);
102610
104095
  }
102611
104096
  if (body.defaultProviderId !== null && !getLlmProviderById(body.defaultProviderId)) {
102612
- return json4({ error: t2("apiError.llmDefaultProviderNotFound") }, 400);
104097
+ return json5({ error: t2("apiError.llmDefaultProviderNotFound") }, 400);
102613
104098
  }
102614
104099
  updates.defaultProviderId = body.defaultProviderId;
102615
104100
  }
102616
104101
  if (body.defaultModelId !== undefined) {
102617
104102
  if (body.defaultModelId !== null && typeof body.defaultModelId !== "string") {
102618
- return json4({ error: t2("apiError.invalidRequest") }, 400);
104103
+ return json5({ error: t2("apiError.invalidRequest") }, 400);
102619
104104
  }
102620
104105
  updates.defaultModelId = body.defaultModelId;
102621
104106
  }
102622
104107
  if (body.tavilyApiKey !== undefined) {
102623
104108
  if (typeof body.tavilyApiKey !== "string") {
102624
- return json4({ error: t2("apiError.invalidRequest") }, 400);
104109
+ return json5({ error: t2("apiError.invalidRequest") }, 400);
102625
104110
  }
102626
104111
  const value = body.tavilyApiKey.trim();
102627
104112
  updates.tavilyApiKeyEnc = value ? await encrypt(value) : null;
102628
104113
  }
102629
104114
  if (body.braveApiKey !== undefined) {
102630
104115
  if (typeof body.braveApiKey !== "string") {
102631
- return json4({ error: t2("apiError.invalidRequest") }, 400);
104116
+ return json5({ error: t2("apiError.invalidRequest") }, 400);
102632
104117
  }
102633
104118
  const value = body.braveApiKey.trim();
102634
104119
  updates.braveApiKeyEnc = value ? await encrypt(value) : null;
102635
104120
  }
102636
104121
  const settings = updateAgentSettings(updates);
102637
- return json4({ settings: toSettingsDto(settings) });
104122
+ return json5({ settings: toSettingsDto(settings) });
102638
104123
  }
102639
- function json4(data, status = 200) {
104124
+ function json5(data, status = 200) {
102640
104125
  return new Response(JSON.stringify(data), {
102641
104126
  status,
102642
104127
  headers: {
@@ -102645,6 +104130,344 @@ function json4(data, status = 200) {
102645
104130
  });
102646
104131
  }
102647
104132
 
104133
+ // ../../apps/gateway/src/system/install-info.ts
104134
+ import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
104135
+ import { resolve as resolve3 } from "path";
104136
+ function resolveInstallDir() {
104137
+ const feDist = process.env.TMEX_FE_DIST_DIR;
104138
+ if (feDist) {
104139
+ return resolve3(feDist, "..", "..");
104140
+ }
104141
+ return process.cwd();
104142
+ }
104143
+ function readInstallMeta() {
104144
+ const metaPath = resolve3(resolveInstallDir(), "install-meta.json");
104145
+ try {
104146
+ if (!existsSync4(metaPath))
104147
+ return null;
104148
+ return JSON.parse(readFileSync4(metaPath, "utf8"));
104149
+ } catch {
104150
+ return null;
104151
+ }
104152
+ }
104153
+ function deploymentFromPlatform(platform) {
104154
+ if (platform === "darwin")
104155
+ return "launchd";
104156
+ if (platform === "linux")
104157
+ return "systemd";
104158
+ return "none";
104159
+ }
104160
+ function getInstallInfo() {
104161
+ if (!config.isProd) {
104162
+ return {
104163
+ installedViaCli: false,
104164
+ deployment: "none",
104165
+ installDir: null,
104166
+ serviceName: null,
104167
+ cliVersion: null
104168
+ };
104169
+ }
104170
+ const meta3 = readInstallMeta();
104171
+ if (!meta3) {
104172
+ return {
104173
+ installedViaCli: false,
104174
+ deployment: "none",
104175
+ installDir: resolveInstallDir(),
104176
+ serviceName: null,
104177
+ cliVersion: null
104178
+ };
104179
+ }
104180
+ return {
104181
+ installedViaCli: true,
104182
+ deployment: deploymentFromPlatform(meta3.platform ?? process.platform),
104183
+ installDir: meta3.installDir ?? resolveInstallDir(),
104184
+ serviceName: meta3.serviceName ?? null,
104185
+ cliVersion: meta3.cliVersion ?? null
104186
+ };
104187
+ }
104188
+
104189
+ // ../../apps/gateway/src/system/version.ts
104190
+ import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
104191
+ import { resolve as resolve4 } from "path";
104192
+ var cachedBase;
104193
+ function readRepoPackageVersion() {
104194
+ const dir2 = import.meta.dir;
104195
+ if (!dir2)
104196
+ return null;
104197
+ const path = resolve4(dir2, "../../../../packages/app/package.json");
104198
+ try {
104199
+ if (!existsSync5(path))
104200
+ return null;
104201
+ const pkg = JSON.parse(readFileSync5(path, "utf8"));
104202
+ return pkg.version ?? null;
104203
+ } catch {
104204
+ return null;
104205
+ }
104206
+ }
104207
+ function getBaseVersion() {
104208
+ if (cachedBase !== undefined)
104209
+ return cachedBase;
104210
+ let base = null;
104211
+ if ("0.11.0") {
104212
+ base = "0.11.0";
104213
+ }
104214
+ if (!base && config.isProd) {
104215
+ base = readInstallMeta()?.cliVersion ?? null;
104216
+ }
104217
+ if (!base) {
104218
+ base = readRepoPackageVersion();
104219
+ }
104220
+ cachedBase = base ?? "unknown";
104221
+ return cachedBase;
104222
+ }
104223
+ function getDisplayVersion() {
104224
+ return formatDisplayVersion(getBaseVersion(), config.isProd);
104225
+ }
104226
+ // ../../apps/gateway/src/system/semver.ts
104227
+ function parse5(input) {
104228
+ const match = input.trim().match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
104229
+ if (!match)
104230
+ return null;
104231
+ return {
104232
+ major: Number(match[1]),
104233
+ minor: Number(match[2]),
104234
+ patch: Number(match[3]),
104235
+ prerelease: match[4] ?? null
104236
+ };
104237
+ }
104238
+ function compareVersions(a, b3) {
104239
+ const pa = parse5(a);
104240
+ const pb = parse5(b3);
104241
+ if (!pa || !pb)
104242
+ return 0;
104243
+ if (pa.major !== pb.major)
104244
+ return pa.major > pb.major ? 1 : -1;
104245
+ if (pa.minor !== pb.minor)
104246
+ return pa.minor > pb.minor ? 1 : -1;
104247
+ if (pa.patch !== pb.patch)
104248
+ return pa.patch > pb.patch ? 1 : -1;
104249
+ if (pa.prerelease === pb.prerelease)
104250
+ return 0;
104251
+ if (pa.prerelease === null)
104252
+ return 1;
104253
+ if (pb.prerelease === null)
104254
+ return -1;
104255
+ return pa.prerelease > pb.prerelease ? 1 : pa.prerelease < pb.prerelease ? -1 : 0;
104256
+ }
104257
+
104258
+ // ../../apps/gateway/src/system/update-check.ts
104259
+ var REGISTRY_URL = "https://registry.npmjs.org/tmex-cli";
104260
+ var changelogCdnUrl = (version3) => `https://cdn.jsdelivr.net/npm/tmex-cli@${version3}/CHANGELOG.md`;
104261
+ var FETCH_TIMEOUT_MS = 1e4;
104262
+ async function checkForUpdate() {
104263
+ const current = getBaseVersion();
104264
+ const res = await fetch(REGISTRY_URL, {
104265
+ cache: "no-store",
104266
+ headers: { accept: "application/json", "cache-control": "no-cache" },
104267
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
104268
+ });
104269
+ if (!res.ok) {
104270
+ throw new Error(`npm registry HTTP ${res.status}`);
104271
+ }
104272
+ const packument = await res.json();
104273
+ const latest = packument["dist-tags"]?.latest ?? null;
104274
+ const publishedAt = latest ? packument.time?.[latest] ?? null : null;
104275
+ const hasUpdate = latest !== null && current !== "unknown" && compareVersions(latest, current) > 0;
104276
+ const changelog = latest ? await fetchChangelog(latest) : null;
104277
+ return {
104278
+ currentVersion: current,
104279
+ latestVersion: latest,
104280
+ hasUpdate,
104281
+ changelog,
104282
+ publishedAt
104283
+ };
104284
+ }
104285
+ async function fetchChangelog(version3) {
104286
+ try {
104287
+ const res = await fetch(changelogCdnUrl(version3), {
104288
+ cache: "no-store",
104289
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
104290
+ });
104291
+ if (!res.ok)
104292
+ return null;
104293
+ const text3 = await res.text();
104294
+ return text3.trim() ? text3 : null;
104295
+ } catch {
104296
+ return null;
104297
+ }
104298
+ }
104299
+ // ../../apps/gateway/src/system/upgrade.ts
104300
+ import { spawn } from "child_process";
104301
+ import { closeSync, existsSync as existsSync6, openSync } from "fs";
104302
+ import { mkdtemp, rm, writeFile } from "fs/promises";
104303
+ import { tmpdir as tmpdir3 } from "os";
104304
+ import { join as join6 } from "path";
104305
+ class UpgradeController {
104306
+ state = "idle";
104307
+ targetVersion = null;
104308
+ error = null;
104309
+ startedAt = null;
104310
+ status() {
104311
+ return {
104312
+ state: this.state,
104313
+ targetVersion: this.targetVersion,
104314
+ error: this.error,
104315
+ startedAt: this.startedAt
104316
+ };
104317
+ }
104318
+ isBusy() {
104319
+ return this.state !== "idle";
104320
+ }
104321
+ start(version3) {
104322
+ if (this.isBusy())
104323
+ return false;
104324
+ this.state = "downloading";
104325
+ this.targetVersion = version3;
104326
+ this.error = null;
104327
+ this.startedAt = new Date().toISOString();
104328
+ this.run(version3);
104329
+ return true;
104330
+ }
104331
+ async run(version3) {
104332
+ const install = getInstallInfo();
104333
+ const installDir = install.installDir;
104334
+ let stageDir = null;
104335
+ let cacheDir = null;
104336
+ try {
104337
+ if (!installDir) {
104338
+ throw new Error("install directory could not be resolved");
104339
+ }
104340
+ stageDir = await mkdtemp(join6(tmpdir3(), "tmex-upg-"));
104341
+ cacheDir = await mkdtemp(join6(tmpdir3(), "tmex-upg-cache-"));
104342
+ await writeFile(join6(stageDir, "package.json"), `${JSON.stringify({ name: "tmex-upgrade-stage", private: true })}
104343
+ `);
104344
+ await this.runBunAdd(stageDir, cacheDir, version3);
104345
+ const binPath = join6(stageDir, "node_modules", "tmex-cli", "bin", "tmex.js");
104346
+ if (!existsSync6(binPath)) {
104347
+ throw new Error(`downloaded tmex-cli binary not found at ${binPath}`);
104348
+ }
104349
+ this.state = "executing";
104350
+ this.spawnUpgrade(binPath, installDir, version3);
104351
+ } catch (err) {
104352
+ this.error = err instanceof Error ? err.message : String(err);
104353
+ this.state = "idle";
104354
+ this.targetVersion = null;
104355
+ if (stageDir)
104356
+ await rm(stageDir, { recursive: true, force: true }).catch(() => {});
104357
+ if (cacheDir)
104358
+ await rm(cacheDir, { recursive: true, force: true }).catch(() => {});
104359
+ }
104360
+ }
104361
+ runBunAdd(stageDir, cacheDir, version3) {
104362
+ return new Promise((resolve5, reject) => {
104363
+ const child = spawn(process.execPath, ["add", `tmex-cli@${version3}`], {
104364
+ cwd: stageDir,
104365
+ env: { ...process.env, BUN_INSTALL_CACHE_DIR: cacheDir },
104366
+ stdio: "ignore"
104367
+ });
104368
+ child.on("error", reject);
104369
+ child.on("close", (code2) => {
104370
+ if (code2 === 0)
104371
+ resolve5();
104372
+ else
104373
+ reject(new Error(`bun add tmex-cli@${version3} exited with code ${code2 ?? "null"}`));
104374
+ });
104375
+ });
104376
+ }
104377
+ spawnUpgrade(binPath, installDir, version3) {
104378
+ let logFd = null;
104379
+ try {
104380
+ logFd = openSync(join6(installDir, "upgrade.log"), "a");
104381
+ } catch {
104382
+ logFd = null;
104383
+ }
104384
+ const child = spawn(process.execPath, [
104385
+ binPath,
104386
+ "upgrade",
104387
+ "--apply-current-package",
104388
+ "--install-dir",
104389
+ installDir,
104390
+ "--version",
104391
+ version3
104392
+ ], {
104393
+ cwd: installDir,
104394
+ env: process.env,
104395
+ detached: true,
104396
+ stdio: logFd !== null ? ["ignore", logFd, logFd] : "ignore"
104397
+ });
104398
+ child.unref();
104399
+ if (logFd !== null)
104400
+ closeSync(logFd);
104401
+ }
104402
+ }
104403
+ var upgradeController = new UpgradeController;
104404
+
104405
+ // ../../apps/gateway/src/system/index.ts
104406
+ function getSystemInfo() {
104407
+ const install = getInstallInfo();
104408
+ const canSelfUpdate = config.isProd && install.installedViaCli && install.deployment !== "none";
104409
+ return {
104410
+ version: getDisplayVersion(),
104411
+ baseVersion: getBaseVersion(),
104412
+ isProd: config.isProd,
104413
+ installedViaCli: install.installedViaCli,
104414
+ deployment: install.deployment,
104415
+ canSelfUpdate,
104416
+ serviceName: install.serviceName
104417
+ };
104418
+ }
104419
+
104420
+ // ../../apps/gateway/src/api/system.ts
104421
+ function json6(data, status = 200) {
104422
+ return new Response(JSON.stringify(data), {
104423
+ status,
104424
+ headers: { "Content-Type": "application/json" }
104425
+ });
104426
+ }
104427
+ function handleSystemApiRequest(req, path) {
104428
+ if (path === "/api/system/info" && req.method === "GET") {
104429
+ return json6(getSystemInfo());
104430
+ }
104431
+ if (path === "/api/system/update-check" && req.method === "GET") {
104432
+ return handleUpdateCheck();
104433
+ }
104434
+ if (path === "/api/system/upgrade" && req.method === "GET") {
104435
+ return json6(upgradeController.status());
104436
+ }
104437
+ if (path === "/api/system/upgrade" && req.method === "POST") {
104438
+ return handleStartUpgrade(req);
104439
+ }
104440
+ return;
104441
+ }
104442
+ async function handleUpdateCheck() {
104443
+ try {
104444
+ return json6(await checkForUpdate());
104445
+ } catch {
104446
+ return json6({ error: t2("apiError.updateCheckFailed") }, 502);
104447
+ }
104448
+ }
104449
+ async function handleStartUpgrade(req) {
104450
+ const info = getSystemInfo();
104451
+ if (!info.canSelfUpdate) {
104452
+ return json6({ error: t2("apiError.upgradeNotAllowed") }, 403);
104453
+ }
104454
+ let version3 = "";
104455
+ try {
104456
+ const body = await req.json();
104457
+ version3 = (body?.version ?? "").trim();
104458
+ } catch {
104459
+ version3 = "";
104460
+ }
104461
+ if (!version3) {
104462
+ return json6({ error: t2("apiError.upgradeVersionRequired") }, 400);
104463
+ }
104464
+ const started = upgradeController.start(version3);
104465
+ if (!started) {
104466
+ return json6({ ...upgradeController.status(), error: t2("apiError.upgradeInProgress") }, 409);
104467
+ }
104468
+ return json6(upgradeController.status());
104469
+ }
104470
+
102648
104471
  // ../../apps/gateway/src/api/test-connection.ts
102649
104472
  function inferFailurePhase(errorType) {
102650
104473
  if (errorType === "tmux_unavailable") {
@@ -102652,7 +104475,7 @@ function inferFailurePhase(errorType) {
102652
104475
  }
102653
104476
  return "connect";
102654
104477
  }
102655
- function json5(data, status = 200) {
104478
+ function json7(data, status = 200) {
102656
104479
  return new Response(JSON.stringify(data), {
102657
104480
  status,
102658
104481
  headers: {
@@ -102671,7 +104494,7 @@ async function handleDeviceTestConnection(deviceId, inputDeps = {}) {
102671
104494
  };
102672
104495
  const device = deps.getDevice(deviceId);
102673
104496
  if (!device) {
102674
- return json5({ error: deps.translate("apiError.deviceNotFound") }, 404);
104497
+ return json7({ error: deps.translate("apiError.deviceNotFound") }, 404);
102675
104498
  }
102676
104499
  const classifyErrorResponse = (error51) => {
102677
104500
  const rawMessage = error51 instanceof Error ? error51.message : String(error51);
@@ -102684,7 +104507,7 @@ async function handleDeviceTestConnection(deviceId, inputDeps = {}) {
102684
104507
  message: deps.translate(classified.messageKey, classified.messageParams),
102685
104508
  rawMessage
102686
104509
  };
102687
- return json5(payload);
104510
+ return json7(payload);
102688
104511
  };
102689
104512
  let runtime = null;
102690
104513
  try {
@@ -102697,7 +104520,7 @@ async function handleDeviceTestConnection(deviceId, inputDeps = {}) {
102697
104520
  phase: "ready",
102698
104521
  message: deps.translate("common.success")
102699
104522
  };
102700
- return json5(payload);
104523
+ return json7(payload);
102701
104524
  } catch (error51) {
102702
104525
  return classifyErrorResponse(error51);
102703
104526
  } finally {
@@ -103804,35 +105627,35 @@ async function handleListRules(req) {
103804
105627
  if (paneId) {
103805
105628
  rules = rules.filter((rule) => rule.paneId === paneId);
103806
105629
  }
103807
- return json6({ rules: rules.map(toRuleDto) });
105630
+ return json8({ rules: rules.map(toRuleDto) });
103808
105631
  }
103809
105632
  async function handleCreateRule(req, deps) {
103810
105633
  const raw = await readJsonObjectBody3(req);
103811
105634
  if (!raw) {
103812
- return json6({ error: t2("apiError.invalidRequest") }, 400);
105635
+ return json8({ error: t2("apiError.invalidRequest") }, 400);
103813
105636
  }
103814
105637
  const body = raw;
103815
105638
  const name24 = typeof body.name === "string" ? body.name.trim() : "";
103816
105639
  if (!name24) {
103817
- return json6({ error: t2("apiError.watchNameRequired") }, 400);
105640
+ return json8({ error: t2("apiError.watchNameRequired") }, 400);
103818
105641
  }
103819
105642
  const deviceId = typeof body.deviceId === "string" ? body.deviceId.trim() : "";
103820
105643
  if (!deviceId) {
103821
- return json6({ error: t2("apiError.agentDeviceRequired") }, 400);
105644
+ return json8({ error: t2("apiError.agentDeviceRequired") }, 400);
103822
105645
  }
103823
105646
  if (!getDeviceById(deviceId)) {
103824
- return json6({ error: t2("apiError.deviceNotFound") }, 404);
105647
+ return json8({ error: t2("apiError.deviceNotFound") }, 404);
103825
105648
  }
103826
105649
  const paneId = typeof body.paneId === "string" ? body.paneId.trim() : "";
103827
105650
  if (!paneId) {
103828
- return json6({ error: t2("apiError.agentPaneRequired") }, 400);
105651
+ return json8({ error: t2("apiError.agentPaneRequired") }, 400);
103829
105652
  }
103830
105653
  if (!TRIGGER_TYPES.includes(body.triggerType)) {
103831
- return json6({ error: t2("apiError.watchTriggerTypeInvalid") }, 400);
105654
+ return json8({ error: t2("apiError.watchTriggerTypeInvalid") }, 400);
103832
105655
  }
103833
105656
  const parsed = parseRuleFields(raw);
103834
105657
  if (!parsed.ok) {
103835
- return json6({ error: parsed.error }, 400);
105658
+ return json8({ error: parsed.error }, 400);
103836
105659
  }
103837
105660
  const fields = parsed.fields;
103838
105661
  const effective = {
@@ -103845,7 +105668,7 @@ async function handleCreateRule(req, deps) {
103845
105668
  };
103846
105669
  const semanticError = validateRuleSemantics(effective);
103847
105670
  if (semanticError) {
103848
- return json6({ error: semanticError }, 400);
105671
+ return json8({ error: semanticError }, 400);
103849
105672
  }
103850
105673
  const rule = createWatchRule({
103851
105674
  name: name24,
@@ -103868,50 +105691,50 @@ async function handleCreateRule(req, deps) {
103868
105691
  cooldownSeconds: fields.cooldownSeconds
103869
105692
  });
103870
105693
  await deps.service.refreshRule(rule.id);
103871
- return json6({ rule: toRuleDto(rule), state: null }, 201);
105694
+ return json8({ rule: toRuleDto(rule), state: null }, 201);
103872
105695
  }
103873
105696
  async function handleGetRule(id) {
103874
105697
  const rule = getWatchRuleById(id);
103875
105698
  if (!rule) {
103876
- return json6({ error: t2("apiError.watchRuleNotFound") }, 404);
105699
+ return json8({ error: t2("apiError.watchRuleNotFound") }, 404);
103877
105700
  }
103878
105701
  const state = getWatchRuleState(id);
103879
- return json6({ rule: toRuleDto(rule), state: state ? toStateDto(state) : null });
105702
+ return json8({ rule: toRuleDto(rule), state: state ? toStateDto(state) : null });
103880
105703
  }
103881
105704
  async function handleUpdateRule(req, id, deps) {
103882
105705
  const existing = getWatchRuleById(id);
103883
105706
  if (!existing) {
103884
- return json6({ error: t2("apiError.watchRuleNotFound") }, 404);
105707
+ return json8({ error: t2("apiError.watchRuleNotFound") }, 404);
103885
105708
  }
103886
105709
  const raw = await readJsonObjectBody3(req);
103887
105710
  if (!raw) {
103888
- return json6({ error: t2("apiError.invalidRequest") }, 400);
105711
+ return json8({ error: t2("apiError.invalidRequest") }, 400);
103889
105712
  }
103890
105713
  const body = raw;
103891
105714
  const updates = {};
103892
105715
  if (body.name !== undefined) {
103893
105716
  const name24 = typeof body.name === "string" ? body.name.trim() : "";
103894
105717
  if (!name24) {
103895
- return json6({ error: t2("apiError.watchNameRequired") }, 400);
105718
+ return json8({ error: t2("apiError.watchNameRequired") }, 400);
103896
105719
  }
103897
105720
  updates.name = name24;
103898
105721
  }
103899
105722
  if (body.paneId !== undefined) {
103900
105723
  const paneId = typeof body.paneId === "string" ? body.paneId.trim() : "";
103901
105724
  if (!paneId) {
103902
- return json6({ error: t2("apiError.agentPaneRequired") }, 400);
105725
+ return json8({ error: t2("apiError.agentPaneRequired") }, 400);
103903
105726
  }
103904
105727
  updates.paneId = paneId;
103905
105728
  }
103906
105729
  if (body.triggerType !== undefined) {
103907
105730
  if (!TRIGGER_TYPES.includes(body.triggerType)) {
103908
- return json6({ error: t2("apiError.watchTriggerTypeInvalid") }, 400);
105731
+ return json8({ error: t2("apiError.watchTriggerTypeInvalid") }, 400);
103909
105732
  }
103910
105733
  updates.triggerType = body.triggerType;
103911
105734
  }
103912
105735
  const parsed = parseRuleFields(raw);
103913
105736
  if (!parsed.ok) {
103914
- return json6({ error: parsed.error }, 400);
105737
+ return json8({ error: parsed.error }, 400);
103915
105738
  }
103916
105739
  const fields = parsed.fields;
103917
105740
  Object.assign(updates, fields);
@@ -103925,32 +105748,32 @@ async function handleUpdateRule(req, id, deps) {
103925
105748
  };
103926
105749
  const semanticError = validateRuleSemantics(effective);
103927
105750
  if (semanticError) {
103928
- return json6({ error: semanticError }, 400);
105751
+ return json8({ error: semanticError }, 400);
103929
105752
  }
103930
105753
  const rule = updateWatchRule(id, updates);
103931
105754
  if (!rule) {
103932
- return json6({ error: t2("apiError.watchRuleNotFound") }, 404);
105755
+ return json8({ error: t2("apiError.watchRuleNotFound") }, 404);
103933
105756
  }
103934
105757
  await deps.service.refreshRule(id);
103935
105758
  const state = getWatchRuleState(id);
103936
- return json6({ rule: toRuleDto(rule), state: state ? toStateDto(state) : null });
105759
+ return json8({ rule: toRuleDto(rule), state: state ? toStateDto(state) : null });
103937
105760
  }
103938
105761
  async function handleDeleteRule(id, deps) {
103939
105762
  const existing = getWatchRuleById(id);
103940
105763
  if (!existing) {
103941
- return json6({ error: t2("apiError.watchRuleNotFound") }, 404);
105764
+ return json8({ error: t2("apiError.watchRuleNotFound") }, 404);
103942
105765
  }
103943
105766
  deleteWatchRule(id);
103944
105767
  await deps.service.removeRule(id);
103945
- return json6({ success: true });
105768
+ return json8({ success: true });
103946
105769
  }
103947
105770
  async function handleGetRuleState(id, deps) {
103948
105771
  const rule = getWatchRuleById(id);
103949
105772
  if (!rule) {
103950
- return json6({ error: t2("apiError.watchRuleNotFound") }, 404);
105773
+ return json8({ error: t2("apiError.watchRuleNotFound") }, 404);
103951
105774
  }
103952
105775
  const state = getWatchRuleState(id);
103953
- return json6({
105776
+ return json8({
103954
105777
  state: state ? toStateDto(state) : null,
103955
105778
  samples: deps.service.getSamples(id)
103956
105779
  });
@@ -103974,17 +105797,17 @@ function buildAssistPrompt(description, screen) {
103974
105797
  async function handleAssistRegex(req, deps) {
103975
105798
  const raw = await readJsonObjectBody3(req);
103976
105799
  if (!raw) {
103977
- return json6({ error: t2("apiError.invalidRequest") }, 400);
105800
+ return json8({ error: t2("apiError.invalidRequest") }, 400);
103978
105801
  }
103979
105802
  const body = raw;
103980
105803
  const description = typeof body.description === "string" ? body.description.trim() : "";
103981
105804
  if (!description) {
103982
- return json6({ error: t2("apiError.watchAssistDescriptionRequired") }, 400);
105805
+ return json8({ error: t2("apiError.watchAssistDescriptionRequired") }, 400);
103983
105806
  }
103984
105807
  let providerId = null;
103985
105808
  if (body.providerId !== undefined && body.providerId !== null) {
103986
105809
  if (typeof body.providerId !== "string" || !getLlmProviderById(body.providerId)) {
103987
- return json6({ error: t2("apiError.llmProviderNotFound") }, 400);
105810
+ return json8({ error: t2("apiError.llmProviderNotFound") }, 400);
103988
105811
  }
103989
105812
  providerId = body.providerId;
103990
105813
  }
@@ -103994,7 +105817,7 @@ async function handleAssistRegex(req, deps) {
103994
105817
  const paneId = typeof body.paneId === "string" ? body.paneId.trim() : "";
103995
105818
  if (deviceId && paneId) {
103996
105819
  if (!getDeviceById(deviceId)) {
103997
- return json6({ error: t2("apiError.deviceNotFound") }, 404);
105820
+ return json8({ error: t2("apiError.deviceNotFound") }, 404);
103998
105821
  }
103999
105822
  try {
104000
105823
  screen = await deps.captureScreen(deviceId, paneId);
@@ -104015,14 +105838,14 @@ async function handleAssistRegex(req, deps) {
104015
105838
  object3 = result.object;
104016
105839
  } catch (error51) {
104017
105840
  const detail = error51 instanceof Error ? error51.message : String(error51);
104018
- return json6({ error: t2("apiError.watchAssistModelUnavailable", { detail }) }, 502);
105841
+ return json8({ error: t2("apiError.watchAssistModelUnavailable", { detail }) }, 502);
104019
105842
  }
104020
105843
  let regex;
104021
105844
  try {
104022
105845
  regex = compileWatchPattern(object3.pattern, object3.flags);
104023
105846
  } catch (error51) {
104024
105847
  const detail = error51 instanceof Error ? error51.message : String(error51);
104025
- return json6({ error: t2("apiError.watchPatternInvalid", { detail }) }, 502);
105848
+ return json8({ error: t2("apiError.watchPatternInvalid", { detail }) }, 502);
104026
105849
  }
104027
105850
  const preview = [];
104028
105851
  if (screen) {
@@ -104036,7 +105859,7 @@ async function handleAssistRegex(req, deps) {
104036
105859
  match = regex.exec(screen);
104037
105860
  }
104038
105861
  }
104039
- return json6({
105862
+ return json8({
104040
105863
  pattern: object3.pattern,
104041
105864
  flags: object3.flags,
104042
105865
  extractGroup: object3.extractGroup >= 0 ? object3.extractGroup : 0,
@@ -104044,7 +105867,7 @@ async function handleAssistRegex(req, deps) {
104044
105867
  preview
104045
105868
  });
104046
105869
  }
104047
- function json6(data, status = 200) {
105870
+ function json8(data, status = 200) {
104048
105871
  return new Response(JSON.stringify(data), {
104049
105872
  status,
104050
105873
  headers: {
@@ -104228,6 +106051,18 @@ function handleApiRequest(req, _server) {
104228
106051
  return watchResponse;
104229
106052
  }
104230
106053
  }
106054
+ if (path.startsWith("/api/files")) {
106055
+ const filesResponse = handleFilesApiRequest(req, path);
106056
+ if (filesResponse) {
106057
+ return filesResponse;
106058
+ }
106059
+ }
106060
+ if (path.startsWith("/api/system/")) {
106061
+ const systemResponse = handleSystemApiRequest(req, path);
106062
+ if (systemResponse) {
106063
+ return systemResponse;
106064
+ }
106065
+ }
104231
106066
  if (path === "/api/webhooks" && req.method === "GET") {
104232
106067
  return handleGetWebhooks();
104233
106068
  }
@@ -104241,9 +106076,13 @@ function handleApiRequest(req, _server) {
104241
106076
  return handleGetManifest(req.method);
104242
106077
  }
104243
106078
  if (path === "/healthz" && req.method === "GET") {
104244
- return json7({ status: "ok", restarting: runtimeController.isRestarting() });
106079
+ return json9({
106080
+ status: "ok",
106081
+ restarting: runtimeController.isRestarting(),
106082
+ env: "development"
106083
+ });
104245
106084
  }
104246
- return json7({ error: t2("apiError.notFound") }, 404);
106085
+ return json9({ error: t2("apiError.notFound") }, 404);
104247
106086
  }
104248
106087
  function enrichDeviceWithRuntime(device) {
104249
106088
  const status = getDeviceRuntimeStatus(device.id);
@@ -104257,22 +106096,22 @@ function enrichDeviceWithRuntime(device) {
104257
106096
  }
104258
106097
  async function handleGetDevices() {
104259
106098
  const devices2 = getAllDevices().map(enrichDeviceWithRuntime);
104260
- return json7({ devices: devices2 });
106099
+ return json9({ devices: devices2 });
104261
106100
  }
104262
106101
  async function handleGetDevice(id) {
104263
106102
  const device = getDeviceById(id);
104264
106103
  if (!device) {
104265
- return json7({ error: t2("apiError.deviceNotFound") }, 404);
106104
+ return json9({ error: t2("apiError.deviceNotFound") }, 404);
104266
106105
  }
104267
- return json7({ device: enrichDeviceWithRuntime(device) });
106106
+ return json9({ device: enrichDeviceWithRuntime(device) });
104268
106107
  }
104269
106108
  async function handleCreateDevice(req) {
104270
106109
  const body = await req.json();
104271
106110
  if (!body.name || !body.type || !body.authMode) {
104272
- return json7({ error: t2("apiError.missingFields") }, 400);
106111
+ return json9({ error: t2("apiError.missingFields") }, 400);
104273
106112
  }
104274
106113
  if (body.type === "ssh" && !body.host && !body.sshConfigRef) {
104275
- return json7({ error: t2("apiError.sshRequiresHost") }, 400);
106114
+ return json9({ error: t2("apiError.sshRequiresHost") }, 400);
104276
106115
  }
104277
106116
  const now2 = new Date().toISOString();
104278
106117
  const device = {
@@ -104294,12 +106133,12 @@ async function handleCreateDevice(req) {
104294
106133
  };
104295
106134
  createDevice(device);
104296
106135
  await pushSupervisor.upsert(device.id);
104297
- return json7({ device: getDeviceById(device.id) ?? device }, 201);
106136
+ return json9({ device: getDeviceById(device.id) ?? device }, 201);
104298
106137
  }
104299
106138
  async function handleUpdateDevice(req, id) {
104300
106139
  const existing = getDeviceById(id);
104301
106140
  if (!existing) {
104302
- return json7({ error: t2("apiError.deviceNotFound") }, 404);
106141
+ return json9({ error: t2("apiError.deviceNotFound") }, 404);
104303
106142
  }
104304
106143
  const body = await req.json();
104305
106144
  const updates = {};
@@ -104329,61 +106168,61 @@ async function handleUpdateDevice(req, id) {
104329
106168
  await pushSupervisor.reconnect(id);
104330
106169
  }
104331
106170
  const device = getDeviceById(id);
104332
- return json7({ device });
106171
+ return json9({ device });
104333
106172
  }
104334
106173
  async function handleReorderDevices(req) {
104335
106174
  const body = await req.json();
104336
106175
  if (!Array.isArray(body.deviceIds) || body.deviceIds.some((id) => typeof id !== "string")) {
104337
- return json7({ error: t2("apiError.invalidRequest") }, 400);
106176
+ return json9({ error: t2("apiError.invalidRequest") }, 400);
104338
106177
  }
104339
106178
  reorderDevices(body.deviceIds);
104340
- return json7({ devices: getAllDevices().map(enrichDeviceWithRuntime) });
106179
+ return json9({ devices: getAllDevices().map(enrichDeviceWithRuntime) });
104341
106180
  }
104342
106181
  async function handleDeleteDevice(id) {
104343
106182
  const existing = getDeviceById(id);
104344
106183
  if (!existing) {
104345
- return json7({ error: t2("apiError.deviceNotFound") }, 404);
106184
+ return json9({ error: t2("apiError.deviceNotFound") }, 404);
104346
106185
  }
104347
106186
  deleteDevice(id);
104348
106187
  pushSupervisor.remove(id);
104349
- return json7({ success: true });
106188
+ return json9({ success: true });
104350
106189
  }
104351
106190
  async function handleTestConnection(id) {
104352
106191
  return handleDeviceTestConnection(id);
104353
106192
  }
104354
106193
  async function handleGetSiteSettings() {
104355
- return json7({ settings: getSiteSettings() });
106194
+ return json9({ settings: getSiteSettings() });
104356
106195
  }
104357
106196
  async function handleUpdateSiteSettings(req) {
104358
106197
  try {
104359
106198
  const body = await req.json();
104360
106199
  const updates = normalizeSiteSettingsInput(body);
104361
106200
  const settings = updateSiteSettings(updates);
104362
- return json7({ settings });
106201
+ return json9({ settings });
104363
106202
  } catch (err) {
104364
- return json7({ error: err instanceof Error ? err.message : t2("apiError.invalidRequest") }, 400);
106203
+ return json9({ error: err instanceof Error ? err.message : t2("apiError.invalidRequest") }, 400);
104365
106204
  }
104366
106205
  }
104367
106206
  async function handleRestartGateway() {
104368
106207
  setTimeout(() => {
104369
106208
  runtimeController.requestRestart();
104370
106209
  }, 50);
104371
- return json7({
106210
+ return json9({
104372
106211
  success: true,
104373
106212
  message: t2("settings.restartScheduled")
104374
106213
  });
104375
106214
  }
104376
106215
  async function handleGetTelegramBots() {
104377
106216
  const bots = getTelegramBotsWithStats();
104378
- return json7({ bots });
106217
+ return json9({ bots });
104379
106218
  }
104380
106219
  async function handleCreateTelegramBot(req) {
104381
106220
  const body = await req.json();
104382
106221
  if (!body.name?.trim()) {
104383
- return json7({ error: t2("apiError.botNameRequired") }, 400);
106222
+ return json9({ error: t2("apiError.botNameRequired") }, 400);
104384
106223
  }
104385
106224
  if (!body.token?.trim()) {
104386
- return json7({ error: t2("apiError.botTokenRequired") }, 400);
106225
+ return json9({ error: t2("apiError.botTokenRequired") }, 400);
104387
106226
  }
104388
106227
  const now2 = new Date().toISOString();
104389
106228
  createTelegramBot({
@@ -104397,26 +106236,26 @@ async function handleCreateTelegramBot(req) {
104397
106236
  updatedAt: now2
104398
106237
  });
104399
106238
  await telegramService.refresh();
104400
- return json7({ success: true }, 201);
106239
+ return json9({ success: true }, 201);
104401
106240
  }
104402
106241
  async function handleUpdateTelegramBot(req, botId) {
104403
106242
  const existing = getTelegramBotById(botId);
104404
106243
  if (!existing) {
104405
- return json7({ error: t2("apiError.botNotFound") }, 404);
106244
+ return json9({ error: t2("apiError.botNotFound") }, 404);
104406
106245
  }
104407
106246
  const body = await req.json();
104408
106247
  const updates = {};
104409
106248
  if (body.name !== undefined) {
104410
106249
  const value = body.name.trim();
104411
106250
  if (!value) {
104412
- return json7({ error: t2("apiError.botNameRequired") }, 400);
106251
+ return json9({ error: t2("apiError.botNameRequired") }, 400);
104413
106252
  }
104414
106253
  updates.name = value;
104415
106254
  }
104416
106255
  if (body.token !== undefined) {
104417
106256
  const token = body.token.trim();
104418
106257
  if (!token) {
104419
- return json7({ error: t2("apiError.botTokenRequired") }, 400);
106258
+ return json9({ error: t2("apiError.botTokenRequired") }, 400);
104420
106259
  }
104421
106260
  updates.tokenEnc = await encrypt(token);
104422
106261
  }
@@ -104428,69 +106267,69 @@ async function handleUpdateTelegramBot(req, botId) {
104428
106267
  }
104429
106268
  updateTelegramBot(botId, updates);
104430
106269
  await telegramService.refresh();
104431
- return json7({ success: true });
106270
+ return json9({ success: true });
104432
106271
  }
104433
106272
  async function handleDeleteTelegramBot(botId) {
104434
106273
  const existing = getTelegramBotById(botId);
104435
106274
  if (!existing) {
104436
- return json7({ error: t2("apiError.botNotFound") }, 404);
106275
+ return json9({ error: t2("apiError.botNotFound") }, 404);
104437
106276
  }
104438
106277
  deleteTelegramBot(botId);
104439
106278
  await telegramService.refresh();
104440
- return json7({ success: true });
106279
+ return json9({ success: true });
104441
106280
  }
104442
106281
  async function handleListTelegramChats(botId) {
104443
106282
  const existing = getTelegramBotById(botId);
104444
106283
  if (!existing) {
104445
- return json7({ error: t2("apiError.botNotFound") }, 404);
106284
+ return json9({ error: t2("apiError.botNotFound") }, 404);
104446
106285
  }
104447
106286
  const chats = listTelegramChatsByBot(botId);
104448
- return json7({ chats });
106287
+ return json9({ chats });
104449
106288
  }
104450
106289
  async function handleApproveTelegramChat(botId, chatId) {
104451
106290
  const existing = getTelegramBotById(botId);
104452
106291
  if (!existing) {
104453
- return json7({ error: t2("apiError.botNotFound") }, 404);
106292
+ return json9({ error: t2("apiError.botNotFound") }, 404);
104454
106293
  }
104455
106294
  const chat = approveTelegramChat(botId, chatId);
104456
106295
  if (!chat) {
104457
- return json7({ error: t2("apiError.chatNotFound") }, 404);
106296
+ return json9({ error: t2("apiError.chatNotFound") }, 404);
104458
106297
  }
104459
106298
  const settings = getSiteSettings();
104460
106299
  await telegramService.sendTestMessage(botId, chatId, t2("telegram.approveMessageTemplate", {
104461
106300
  botName: existing.name,
104462
106301
  time: new Date().toLocaleString(toBCP47(settings.language))
104463
106302
  }));
104464
- return json7({ chat });
106303
+ return json9({ chat });
104465
106304
  }
104466
106305
  async function handleDeleteTelegramChat(botId, chatId) {
104467
106306
  const existing = getTelegramBotById(botId);
104468
106307
  if (!existing) {
104469
- return json7({ error: t2("apiError.botNotFound") }, 404);
106308
+ return json9({ error: t2("apiError.botNotFound") }, 404);
104470
106309
  }
104471
106310
  deleteTelegramChat(botId, chatId);
104472
- return json7({ success: true });
106311
+ return json9({ success: true });
104473
106312
  }
104474
106313
  async function handleTestTelegramChat(botId, chatId) {
104475
106314
  const bot = getTelegramBotById(botId);
104476
106315
  if (!bot) {
104477
- return json7({ error: t2("apiError.botNotFound") }, 404);
106316
+ return json9({ error: t2("apiError.botNotFound") }, 404);
104478
106317
  }
104479
106318
  const settings = getSiteSettings();
104480
106319
  await telegramService.sendTestMessage(botId, chatId, t2("telegram.testMessageTemplate", {
104481
106320
  siteName: settings.siteName,
104482
106321
  time: new Date().toLocaleString(toBCP47(settings.language))
104483
106322
  }));
104484
- return json7({ success: true });
106323
+ return json9({ success: true });
104485
106324
  }
104486
106325
  async function handleGetWebhooks() {
104487
106326
  const webhooks = getAllWebhookEndpoints();
104488
- return json7({ webhooks });
106327
+ return json9({ webhooks });
104489
106328
  }
104490
106329
  async function handleCreateWebhook(req) {
104491
106330
  const body = await req.json();
104492
106331
  if (!body.url || !body.secret) {
104493
- return json7({ error: t2("apiError.urlAndSecretRequired") }, 400);
106332
+ return json9({ error: t2("apiError.urlAndSecretRequired") }, 400);
104494
106333
  }
104495
106334
  const now2 = new Date().toISOString();
104496
106335
  const endpoint = {
@@ -104503,11 +106342,11 @@ async function handleCreateWebhook(req) {
104503
106342
  updatedAt: now2
104504
106343
  };
104505
106344
  createWebhookEndpoint(endpoint);
104506
- return json7({ webhook: endpoint }, 201);
106345
+ return json9({ webhook: endpoint }, 201);
104507
106346
  }
104508
106347
  async function handleDeleteWebhook(id) {
104509
106348
  deleteWebhookEndpoint(id);
104510
- return json7({ success: true });
106349
+ return json9({ success: true });
104511
106350
  }
104512
106351
  async function handleGetManifest(method) {
104513
106352
  const settings = getSiteSettings();
@@ -104528,7 +106367,7 @@ async function handleGetManifest(method) {
104528
106367
  purpose: "any"
104529
106368
  },
104530
106369
  {
104531
- src: "/tmex.png",
106370
+ src: "/tmex-maskable.png",
104532
106371
  sizes: "1024x1024",
104533
106372
  type: "image/png",
104534
106373
  purpose: "maskable"
@@ -104546,7 +106385,7 @@ function manifestJson(data, method) {
104546
106385
  }
104547
106386
  });
104548
106387
  }
104549
- function json7(data, status = 200, headers = {}) {
106388
+ function json9(data, status = 200, headers = {}) {
104550
106389
  return new Response(JSON.stringify(data), {
104551
106390
  status,
104552
106391
  headers: {
@@ -104557,8 +106396,8 @@ function json7(data, status = 200, headers = {}) {
104557
106396
  }
104558
106397
 
104559
106398
  // ../../apps/gateway/src/db/migrate.ts
104560
- import { existsSync as existsSync4 } from "fs";
104561
- import { resolve as resolve3 } from "path";
106399
+ import { existsSync as existsSync7 } from "fs";
106400
+ import { resolve as resolve5 } from "path";
104562
106401
 
104563
106402
  // ../../node_modules/.bun/drizzle-orm@0.45.1+d3720f6f296df04b/node_modules/drizzle-orm/migrator.js
104564
106403
  import crypto2 from "crypto";
@@ -104603,10 +106442,10 @@ function resolveMigrationsFolder() {
104603
106442
  const fromEnv = process.env.TMEX_MIGRATIONS_DIR;
104604
106443
  if (fromEnv)
104605
106444
  return fromEnv;
104606
- const byCwd = resolve3(process.cwd(), "drizzle");
104607
- if (existsSync4(byCwd))
106445
+ const byCwd = resolve5(process.cwd(), "drizzle");
106446
+ if (existsSync7(byCwd))
104608
106447
  return byCwd;
104609
- return resolve3(import.meta.dir, "../../drizzle");
106448
+ return resolve5(import.meta.dir, "../../drizzle");
104610
106449
  }
104611
106450
  var migrationsFolder = resolveMigrationsFolder();
104612
106451
  function runMigrations() {
@@ -106387,7 +108226,7 @@ function t3(key, vars) {
106387
108226
  }
106388
108227
 
106389
108228
  // src/runtime/server.ts
106390
- var MIME_MAP = {
108229
+ var MIME_MAP2 = {
106391
108230
  ".html": "text/html; charset=utf-8",
106392
108231
  ".js": "text/javascript; charset=utf-8",
106393
108232
  ".mjs": "text/javascript; charset=utf-8",
@@ -106403,21 +108242,21 @@ var MIME_MAP = {
106403
108242
  ".map": "application/json; charset=utf-8"
106404
108243
  };
106405
108244
  function contentTypeByPath(path) {
106406
- const ext = extname(path).toLowerCase();
106407
- return MIME_MAP[ext];
108245
+ const ext = extname2(path).toLowerCase();
108246
+ return MIME_MAP2[ext];
106408
108247
  }
106409
108248
  function resolveStaticRoot() {
106410
108249
  if (process.env.TMEX_FE_DIST_DIR) {
106411
- return resolve4(process.env.TMEX_FE_DIST_DIR);
108250
+ return resolve6(process.env.TMEX_FE_DIST_DIR);
106412
108251
  }
106413
- return resolve4(import.meta.dir, "../../resources/fe-dist");
108252
+ return resolve6(import.meta.dir, "../../resources/fe-dist");
106414
108253
  }
106415
108254
  function resolveRequestedFile(staticRoot, pathname) {
106416
- const root = resolve4(staticRoot);
108255
+ const root = resolve6(staticRoot);
106417
108256
  const decoded = decodeURIComponent(pathname);
106418
108257
  const normalized = normalize(decoded).replace(/^\.\.(\/|\\|$)/, "");
106419
108258
  const requested = normalized === "/" ? "/index.html" : normalized;
106420
- const absolutePath = resolve4(root, `.${requested}`);
108259
+ const absolutePath = resolve6(root, `.${requested}`);
106421
108260
  if (absolutePath !== root && !absolutePath.startsWith(`${root}${sep}`)) {
106422
108261
  return null;
106423
108262
  }
@@ -106432,12 +108271,12 @@ async function serveFrontend(req, staticRoot) {
106432
108271
  if (!requestedPath) {
106433
108272
  return new Response(t3("runtime.forbidden"), { status: 403 });
106434
108273
  }
106435
- if (!existsSync5(requestedPath) && extname(url2.pathname) !== "") {
108274
+ if (!existsSync8(requestedPath) && extname2(url2.pathname) !== "") {
106436
108275
  return new Response(t3("runtime.notFound"), { status: 404 });
106437
108276
  }
106438
- const indexPath = join4(staticRoot, "index.html");
106439
- const targetPath = existsSync5(requestedPath) ? requestedPath : indexPath;
106440
- if (!existsSync5(targetPath)) {
108277
+ const indexPath = join7(staticRoot, "index.html");
108278
+ const targetPath = existsSync8(requestedPath) ? requestedPath : indexPath;
108279
+ if (!existsSync8(targetPath)) {
106441
108280
  return new Response(t3("runtime.frontendMissing"), { status: 500 });
106442
108281
  }
106443
108282
  const headers = new Headers;
@@ -106448,6 +108287,7 @@ async function serveFrontend(req, staticRoot) {
106448
108287
  return new Response(Bun.file(targetPath), { headers });
106449
108288
  }
106450
108289
  async function main() {
108290
+ console.log(`[tmex] version ${getDisplayVersion()}`);
106451
108291
  const host = process.env.TMEX_BIND_HOST || "127.0.0.1";
106452
108292
  const port = Number(process.env.GATEWAY_PORT || "9883");
106453
108293
  const staticRoot = resolveStaticRoot();