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.
- package/CHANGELOG.md +19 -0
- package/dist/runtime/server.js +1988 -148
- package/package.json +4 -3
- package/resources/fe-dist/assets/DevicePage-fGI-do8S.js +26 -0
- package/resources/fe-dist/assets/DevicesPage-kGrzwhGj.js +2 -0
- package/resources/fe-dist/assets/FilePage-YWma96aj.js +16 -0
- package/resources/fe-dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/resources/fe-dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/resources/fe-dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/resources/fe-dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/resources/fe-dist/assets/SettingsPage-LhUD03Ln.js +32 -0
- package/resources/fe-dist/assets/arc-DLVa298O.js +2 -0
- package/resources/fe-dist/assets/architectureDiagram-3BPJPVTR-EOUpuRby.js +37 -0
- package/resources/fe-dist/assets/blockDiagram-GPEHLZMM-BTwwkzZh.js +133 -0
- package/resources/fe-dist/assets/c4Diagram-AAUBKEIU-xvTqXxl9.js +11 -0
- package/resources/fe-dist/assets/card-cbVDy0Xp.js +2 -0
- package/resources/fe-dist/assets/channel-DbG6qNF6.js +2 -0
- package/resources/fe-dist/assets/chunk-2J33WTMH-Blr6l1Nk.js +2 -0
- package/resources/fe-dist/assets/chunk-4BX2VUAB-D_Y2TeLg.js +2 -0
- package/resources/fe-dist/assets/chunk-55IACEB6-CimNoYDH.js +2 -0
- package/resources/fe-dist/assets/chunk-727SXJPM-JPYDN0Cb.js +207 -0
- package/resources/fe-dist/assets/chunk-AQP2D5EJ-Cb3NZBib.js +232 -0
- package/resources/fe-dist/assets/chunk-FMBD7UC4-DyZ7Ot2F.js +16 -0
- package/resources/fe-dist/assets/chunk-ND2GUHAM-SU6Ea3wF.js +2 -0
- package/resources/fe-dist/assets/chunk-QZHKN3VN-B6qXX6sU.js +2 -0
- package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-rB1DgxOZ.js +2 -0
- package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-rB1DgxOZ.js +2 -0
- package/resources/fe-dist/assets/cose-bilkent-S5V4N54A-BAyMopHS.js +2 -0
- package/resources/fe-dist/assets/cytoscape.esm-DTSO7Bv0.js +332 -0
- package/resources/fe-dist/assets/dagre-BM42HDAG-Ckrx9BXZ.js +5 -0
- package/resources/fe-dist/assets/defaultLocale-DX6XiGOO.js +2 -0
- package/resources/fe-dist/assets/diagram-2AECGRRQ-DTnZtf2k.js +44 -0
- package/resources/fe-dist/assets/diagram-5GNKFQAL-DBpgZj4a.js +11 -0
- package/resources/fe-dist/assets/diagram-KO2AKTUF-CfijYCeY.js +4 -0
- package/resources/fe-dist/assets/diagram-LMA3HP47-D7nBmQ-E.js +25 -0
- package/resources/fe-dist/assets/diagram-OG6HWLK6-Bi8sHV0O.js +25 -0
- package/resources/fe-dist/assets/erDiagram-TEJ5UH35-DcoDesO2.js +86 -0
- package/resources/fe-dist/assets/flowDiagram-I6XJVG4X-Bbcl9kxS.js +163 -0
- package/resources/fe-dist/assets/ganttDiagram-6RSMTGT7-BM8Y7jwp.js +293 -0
- package/resources/fe-dist/assets/gitGraphDiagram-PVQCEYII-x3wpNQcW.js +107 -0
- package/resources/fe-dist/assets/graph-CAnANduQ.js +2 -0
- package/resources/fe-dist/assets/index-cfY8KuW7.js +466 -0
- package/resources/fe-dist/assets/index-vcS0VPIW.css +1 -0
- package/resources/fe-dist/assets/infoDiagram-5YYISTIA-Bl6cgwVb.js +3 -0
- package/resources/fe-dist/assets/init-Gi6I4Gst.js +2 -0
- package/resources/fe-dist/assets/ishikawaDiagram-YF4QCWOH-BfQ7aZxN.js +71 -0
- package/resources/fe-dist/assets/journeyDiagram-JHISSGLW-DSwQzmdL.js +140 -0
- package/resources/fe-dist/assets/kanban-definition-UN3LZRKU-B1aWDl4E.js +90 -0
- package/resources/fe-dist/assets/layout-DGIYPm2g.js +2 -0
- package/resources/fe-dist/assets/linear-CfhRC4iX.js +2 -0
- package/resources/fe-dist/assets/markdown-preview-Bf_6UH9R.css +1 -0
- package/resources/fe-dist/assets/markdown-preview-CfBRMViB.js +269 -0
- package/resources/fe-dist/assets/mermaid.core-M-o4g4TO.js +310 -0
- package/resources/fe-dist/assets/mindmap-definition-RKZ34NQL-D976sxOA.js +97 -0
- package/resources/fe-dist/assets/ordinal-BENe2yWM.js +2 -0
- package/resources/fe-dist/assets/pieDiagram-4H26LBE5-PVQ3-EB6.js +31 -0
- package/resources/fe-dist/assets/quadrantDiagram-W4KKPZXB-BNEFhYNq.js +8 -0
- package/resources/fe-dist/assets/{refresh-cw-D0LYIwxc.js → refresh-cw-cfX5JiIH.js} +3 -3
- package/resources/fe-dist/assets/requirementDiagram-4Y6WPE33-B7Zd2lpN.js +85 -0
- package/resources/fe-dist/assets/sankeyDiagram-5OEKKPKP-D_Td4j-B.js +41 -0
- package/resources/fe-dist/assets/sequenceDiagram-3UESZ5HK-DQSVX5mL.js +163 -0
- package/resources/fe-dist/assets/stateDiagram-AJRCARHV-C0PNCTfs.js +2 -0
- package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-CB5OePKf.js +2 -0
- package/resources/fe-dist/assets/timeline-definition-PNZ67QCA-BlMj8Fl1.js +121 -0
- package/resources/fe-dist/assets/vennDiagram-CIIHVFJN-DuGxwZiq.js +35 -0
- package/resources/fe-dist/assets/wardley-L42UT6IY-4VhWbg10.js +174 -0
- package/resources/fe-dist/assets/wardleyDiagram-YWT4CUSO-Bu4k6xIx.js +79 -0
- package/resources/fe-dist/assets/xychartDiagram-2RQKCTM6-Cv88IDWh.js +8 -0
- package/resources/fe-dist/fonts/GeistMonoNerdFontMono-Bold.woff2 +0 -0
- package/resources/fe-dist/fonts/GeistMonoNerdFontMono-Regular.woff2 +0 -0
- package/resources/fe-dist/index.html +2 -2
- package/resources/fe-dist/tmex-maskable.png +0 -0
- package/resources/fe-dist/tmex.png +0 -0
- package/resources/gateway-drizzle/0007_fearless_pestilence.sql +8 -0
- package/resources/gateway-drizzle/0008_perfect_ozymandias.sql +7 -0
- package/resources/gateway-drizzle/meta/_journal.json +14 -0
- package/resources/fe-dist/assets/DevicePage-RqH-pnZX.js +0 -26
- package/resources/fe-dist/assets/DevicesPage-BPLJu-5J.js +0 -2
- package/resources/fe-dist/assets/SettingsPage-CKP305BP.js +0 -22
- package/resources/fe-dist/assets/card-X35xwXzW.js +0 -2
- package/resources/fe-dist/assets/index-CKQEHZcV.css +0 -1
- package/resources/fe-dist/assets/index-O2a9149K.js +0 -400
- package/resources/fe-dist/fonts/GeistMono-Bold.woff2 +0 -0
- package/resources/fe-dist/fonts/GeistMono-Regular.woff2 +0 -0
package/dist/runtime/server.js
CHANGED
|
@@ -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
|
|
22553
|
-
import { extname, join as
|
|
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: "
|
|
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: "\
|
|
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: "\
|
|
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+
|
|
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+
|
|
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+
|
|
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+
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
103961
|
+
return json5({ error: t2("apiError.llmProviderNameRequired") }, 400);
|
|
102477
103962
|
}
|
|
102478
103963
|
if (!PROTOCOLS.includes(body.protocol)) {
|
|
102479
|
-
return
|
|
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
|
|
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
|
|
103972
|
+
return json5({ error: t2("apiError.llmProviderApiKeyRequired") }, 400);
|
|
102488
103973
|
}
|
|
102489
103974
|
if (body.enabled !== undefined && typeof body.enabled !== "boolean") {
|
|
102490
|
-
return
|
|
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
|
|
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
|
|
103990
|
+
return json5({ error: t2("apiError.llmProviderNotFound") }, 404);
|
|
102506
103991
|
}
|
|
102507
103992
|
const raw = await readJsonObjectBody2(req);
|
|
102508
103993
|
if (!raw) {
|
|
102509
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
104060
|
+
return json5({ error: t2("apiError.llmProviderNotFound") }, 404);
|
|
102576
104061
|
}
|
|
102577
104062
|
deleteLlmProvider(id);
|
|
102578
|
-
return
|
|
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
|
|
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
|
|
104072
|
+
return json5({ error: modelsError ?? t2("apiError.invalidRequest") }, 502);
|
|
102588
104073
|
}
|
|
102589
|
-
return
|
|
104074
|
+
return json5({ models });
|
|
102590
104075
|
}
|
|
102591
104076
|
async function handleGetSettings() {
|
|
102592
|
-
return
|
|
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
|
|
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
|
|
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
|
|
104094
|
+
return json5({ error: t2("apiError.invalidRequest") }, 400);
|
|
102610
104095
|
}
|
|
102611
104096
|
if (body.defaultProviderId !== null && !getLlmProviderById(body.defaultProviderId)) {
|
|
102612
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
104122
|
+
return json5({ settings: toSettingsDto(settings) });
|
|
102638
104123
|
}
|
|
102639
|
-
function
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
105644
|
+
return json8({ error: t2("apiError.agentDeviceRequired") }, 400);
|
|
103822
105645
|
}
|
|
103823
105646
|
if (!getDeviceById(deviceId)) {
|
|
103824
|
-
return
|
|
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
|
|
105651
|
+
return json8({ error: t2("apiError.agentPaneRequired") }, 400);
|
|
103829
105652
|
}
|
|
103830
105653
|
if (!TRIGGER_TYPES.includes(body.triggerType)) {
|
|
103831
|
-
return
|
|
105654
|
+
return json8({ error: t2("apiError.watchTriggerTypeInvalid") }, 400);
|
|
103832
105655
|
}
|
|
103833
105656
|
const parsed = parseRuleFields(raw);
|
|
103834
105657
|
if (!parsed.ok) {
|
|
103835
|
-
return
|
|
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
|
|
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
|
|
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
|
|
105699
|
+
return json8({ error: t2("apiError.watchRuleNotFound") }, 404);
|
|
103877
105700
|
}
|
|
103878
105701
|
const state = getWatchRuleState(id);
|
|
103879
|
-
return
|
|
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
|
|
105707
|
+
return json8({ error: t2("apiError.watchRuleNotFound") }, 404);
|
|
103885
105708
|
}
|
|
103886
105709
|
const raw = await readJsonObjectBody3(req);
|
|
103887
105710
|
if (!raw) {
|
|
103888
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
105751
|
+
return json8({ error: semanticError }, 400);
|
|
103929
105752
|
}
|
|
103930
105753
|
const rule = updateWatchRule(id, updates);
|
|
103931
105754
|
if (!rule) {
|
|
103932
|
-
return
|
|
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
|
|
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
|
|
105764
|
+
return json8({ error: t2("apiError.watchRuleNotFound") }, 404);
|
|
103942
105765
|
}
|
|
103943
105766
|
deleteWatchRule(id);
|
|
103944
105767
|
await deps.service.removeRule(id);
|
|
103945
|
-
return
|
|
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
|
|
105773
|
+
return json8({ error: t2("apiError.watchRuleNotFound") }, 404);
|
|
103951
105774
|
}
|
|
103952
105775
|
const state = getWatchRuleState(id);
|
|
103953
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
106079
|
+
return json9({
|
|
106080
|
+
status: "ok",
|
|
106081
|
+
restarting: runtimeController.isRestarting(),
|
|
106082
|
+
env: "development"
|
|
106083
|
+
});
|
|
104245
106084
|
}
|
|
104246
|
-
return
|
|
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
|
|
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
|
|
106104
|
+
return json9({ error: t2("apiError.deviceNotFound") }, 404);
|
|
104266
106105
|
}
|
|
104267
|
-
return
|
|
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
|
|
106111
|
+
return json9({ error: t2("apiError.missingFields") }, 400);
|
|
104273
106112
|
}
|
|
104274
106113
|
if (body.type === "ssh" && !body.host && !body.sshConfigRef) {
|
|
104275
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
106176
|
+
return json9({ error: t2("apiError.invalidRequest") }, 400);
|
|
104338
106177
|
}
|
|
104339
106178
|
reorderDevices(body.deviceIds);
|
|
104340
|
-
return
|
|
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
|
|
106184
|
+
return json9({ error: t2("apiError.deviceNotFound") }, 404);
|
|
104346
106185
|
}
|
|
104347
106186
|
deleteDevice(id);
|
|
104348
106187
|
pushSupervisor.remove(id);
|
|
104349
|
-
return
|
|
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
|
|
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
|
|
106201
|
+
return json9({ settings });
|
|
104363
106202
|
} catch (err) {
|
|
104364
|
-
return
|
|
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
|
|
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
|
|
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
|
|
106222
|
+
return json9({ error: t2("apiError.botNameRequired") }, 400);
|
|
104384
106223
|
}
|
|
104385
106224
|
if (!body.token?.trim()) {
|
|
104386
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
106275
|
+
return json9({ error: t2("apiError.botNotFound") }, 404);
|
|
104437
106276
|
}
|
|
104438
106277
|
deleteTelegramBot(botId);
|
|
104439
106278
|
await telegramService.refresh();
|
|
104440
|
-
return
|
|
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
|
|
106284
|
+
return json9({ error: t2("apiError.botNotFound") }, 404);
|
|
104446
106285
|
}
|
|
104447
106286
|
const chats = listTelegramChatsByBot(botId);
|
|
104448
|
-
return
|
|
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
|
|
106292
|
+
return json9({ error: t2("apiError.botNotFound") }, 404);
|
|
104454
106293
|
}
|
|
104455
106294
|
const chat = approveTelegramChat(botId, chatId);
|
|
104456
106295
|
if (!chat) {
|
|
104457
|
-
return
|
|
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
|
|
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
|
|
106308
|
+
return json9({ error: t2("apiError.botNotFound") }, 404);
|
|
104470
106309
|
}
|
|
104471
106310
|
deleteTelegramChat(botId, chatId);
|
|
104472
|
-
return
|
|
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
|
|
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
|
|
106323
|
+
return json9({ success: true });
|
|
104485
106324
|
}
|
|
104486
106325
|
async function handleGetWebhooks() {
|
|
104487
106326
|
const webhooks = getAllWebhookEndpoints();
|
|
104488
|
-
return
|
|
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
|
|
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
|
|
106345
|
+
return json9({ webhook: endpoint }, 201);
|
|
104507
106346
|
}
|
|
104508
106347
|
async function handleDeleteWebhook(id) {
|
|
104509
106348
|
deleteWebhookEndpoint(id);
|
|
104510
|
-
return
|
|
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
|
|
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
|
|
104561
|
-
import { resolve as
|
|
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 =
|
|
104607
|
-
if (
|
|
106445
|
+
const byCwd = resolve5(process.cwd(), "drizzle");
|
|
106446
|
+
if (existsSync7(byCwd))
|
|
104608
106447
|
return byCwd;
|
|
104609
|
-
return
|
|
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
|
|
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 =
|
|
106407
|
-
return
|
|
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
|
|
108250
|
+
return resolve6(process.env.TMEX_FE_DIST_DIR);
|
|
106412
108251
|
}
|
|
106413
|
-
return
|
|
108252
|
+
return resolve6(import.meta.dir, "../../resources/fe-dist");
|
|
106414
108253
|
}
|
|
106415
108254
|
function resolveRequestedFile(staticRoot, pathname) {
|
|
106416
|
-
const root =
|
|
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 =
|
|
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 (!
|
|
108274
|
+
if (!existsSync8(requestedPath) && extname2(url2.pathname) !== "") {
|
|
106436
108275
|
return new Response(t3("runtime.notFound"), { status: 404 });
|
|
106437
108276
|
}
|
|
106438
|
-
const indexPath =
|
|
106439
|
-
const targetPath =
|
|
106440
|
-
if (!
|
|
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();
|