tmex-cli 0.10.0 → 0.12.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 +43 -0
- package/dist/runtime/server.js +1339 -135
- package/package.json +4 -3
- package/resources/fe-dist/assets/DevicePage-B2xsa-ys.js +21 -0
- package/resources/fe-dist/assets/DevicesPage-Bm7E9QAf.js +2 -0
- package/resources/fe-dist/assets/FilePage-DtXS6Krk.js +16 -0
- package/resources/fe-dist/assets/SettingsPage-Bxat-d_V.js +32 -0
- package/resources/fe-dist/assets/{arc-Wv219fq_.js → arc-CWkT1O24.js} +2 -2
- package/resources/fe-dist/assets/{architectureDiagram-3BPJPVTR-DkPPo2hY.js → architectureDiagram-3BPJPVTR-nu1LX68y.js} +2 -2
- package/resources/fe-dist/assets/{blockDiagram-GPEHLZMM-DPAThozG.js → blockDiagram-GPEHLZMM-DLFLNzT_.js} +2 -2
- package/resources/fe-dist/assets/{c4Diagram-AAUBKEIU-DBs_N-_p.js → c4Diagram-AAUBKEIU-BE8aWxc1.js} +2 -2
- package/resources/fe-dist/assets/{card-Dl0HC88A.js → card-Duis2VHj.js} +2 -2
- package/resources/fe-dist/assets/channel-zKL2OH3S.js +2 -0
- package/resources/fe-dist/assets/{chunk-2J33WTMH-D3mrUPCp.js → chunk-2J33WTMH-Dlc3MV0y.js} +2 -2
- package/resources/fe-dist/assets/{chunk-4BX2VUAB-C0DRv58j.js → chunk-4BX2VUAB-E9XGobks.js} +2 -2
- package/resources/fe-dist/assets/{chunk-55IACEB6-DheUp7iQ.js → chunk-55IACEB6-Bp7ueFZf.js} +2 -2
- package/resources/fe-dist/assets/{chunk-727SXJPM-Cvew2yIJ.js → chunk-727SXJPM-CQrvyHfQ.js} +2 -2
- package/resources/fe-dist/assets/{chunk-AQP2D5EJ-DUoa4Wdg.js → chunk-AQP2D5EJ-c0B1XNZr.js} +2 -2
- package/resources/fe-dist/assets/{chunk-FMBD7UC4-Bt9Oi52P.js → chunk-FMBD7UC4-BWQBkKWs.js} +2 -2
- package/resources/fe-dist/assets/{chunk-ND2GUHAM-DlWpyDaS.js → chunk-ND2GUHAM-D9TROogS.js} +2 -2
- package/resources/fe-dist/assets/{chunk-QZHKN3VN-BZtXwgRW.js → chunk-QZHKN3VN-DRXr6zVP.js} +2 -2
- package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-Drw216KI.js +2 -0
- package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-Drw216KI.js +2 -0
- package/resources/fe-dist/assets/{cose-bilkent-S5V4N54A-yKy6sTY_.js → cose-bilkent-S5V4N54A-CBgJRn7g.js} +2 -2
- package/resources/fe-dist/assets/{dagre-BM42HDAG-DL4fpno6.js → dagre-BM42HDAG-ClYznWvX.js} +2 -2
- package/resources/fe-dist/assets/{diagram-2AECGRRQ-BueYws-9.js → diagram-2AECGRRQ-Br_41uIc.js} +2 -2
- package/resources/fe-dist/assets/{diagram-5GNKFQAL-GQEQSUeb.js → diagram-5GNKFQAL-BD9o1BrS.js} +2 -2
- package/resources/fe-dist/assets/{diagram-KO2AKTUF-LE3yI4ed.js → diagram-KO2AKTUF-Diq2zS9Y.js} +2 -2
- package/resources/fe-dist/assets/{diagram-LMA3HP47-BkHjYrU4.js → diagram-LMA3HP47-CLaOalPy.js} +2 -2
- package/resources/fe-dist/assets/{diagram-OG6HWLK6-DrnHic-P.js → diagram-OG6HWLK6-BcSlvdPy.js} +2 -2
- package/resources/fe-dist/assets/{erDiagram-TEJ5UH35-BCwWv-rO.js → erDiagram-TEJ5UH35-BNlad0c4.js} +2 -2
- package/resources/fe-dist/assets/{flowDiagram-I6XJVG4X-DEC2Z_fh.js → flowDiagram-I6XJVG4X-Zb1FLY00.js} +2 -2
- package/resources/fe-dist/assets/{ganttDiagram-6RSMTGT7-H1T5bCsY.js → ganttDiagram-6RSMTGT7-DPRPR5Is.js} +2 -2
- package/resources/fe-dist/assets/{gitGraphDiagram-PVQCEYII-DmZUfKfl.js → gitGraphDiagram-PVQCEYII-BENU2uEN.js} +2 -2
- package/resources/fe-dist/assets/index-CRCD4gPw.css +1 -0
- package/resources/fe-dist/assets/index-Den36xcn.js +488 -0
- package/resources/fe-dist/assets/{infoDiagram-5YYISTIA-fDOg9Qr5.js → infoDiagram-5YYISTIA-NfMlH21e.js} +2 -2
- package/resources/fe-dist/assets/{ishikawaDiagram-YF4QCWOH-QIvSJI3q.js → ishikawaDiagram-YF4QCWOH-CzK9-7uY.js} +2 -2
- package/resources/fe-dist/assets/{journeyDiagram-JHISSGLW-Iz7u4iH8.js → journeyDiagram-JHISSGLW-CIX5Ahuw.js} +2 -2
- package/resources/fe-dist/assets/{kanban-definition-UN3LZRKU-BYiFSFgn.js → kanban-definition-UN3LZRKU-C5Ccl5Cw.js} +2 -2
- package/resources/fe-dist/assets/{linear-DNHZFKop.js → linear-CBC08yeS.js} +2 -2
- package/resources/fe-dist/assets/markdown-preview-Bf_6UH9R.css +1 -0
- package/resources/fe-dist/assets/markdown-preview-Drd503bU.js +269 -0
- package/resources/fe-dist/assets/{mermaid.core-DUipsjFg.js → mermaid.core-DYl6mxS0.js} +6 -6
- package/resources/fe-dist/assets/{mindmap-definition-RKZ34NQL-tPbrKC3m.js → mindmap-definition-RKZ34NQL-ACR2SUDm.js} +2 -2
- package/resources/fe-dist/assets/{pieDiagram-4H26LBE5-26o8qBzb.js → pieDiagram-4H26LBE5-BuDDdwwy.js} +2 -2
- package/resources/fe-dist/assets/{quadrantDiagram-W4KKPZXB-BgpGo9Ak.js → quadrantDiagram-W4KKPZXB-CzM2Ydhy.js} +2 -2
- package/resources/fe-dist/assets/{refresh-cw-DdcimDhs.js → refresh-cw-DhzvbVDA.js} +2 -2
- package/resources/fe-dist/assets/{requirementDiagram-4Y6WPE33-BEfrxsDZ.js → requirementDiagram-4Y6WPE33-BveKh_kW.js} +2 -2
- package/resources/fe-dist/assets/{sankeyDiagram-5OEKKPKP-BuIeHyjh.js → sankeyDiagram-5OEKKPKP-BGQ6jtyE.js} +2 -2
- package/resources/fe-dist/assets/{sequenceDiagram-3UESZ5HK-CL0sinOn.js → sequenceDiagram-3UESZ5HK-D2fdqire.js} +2 -2
- package/resources/fe-dist/assets/{stateDiagram-AJRCARHV-BCq5X6hO.js → stateDiagram-AJRCARHV-DtuVBIq9.js} +2 -2
- package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-gun__PvU.js +2 -0
- package/resources/fe-dist/assets/{timeline-definition-PNZ67QCA-CnqqA91O.js → timeline-definition-PNZ67QCA-Dslo0JFS.js} +2 -2
- package/resources/fe-dist/assets/{vennDiagram-CIIHVFJN-BqFG3JXB.js → vennDiagram-CIIHVFJN-BLcoN4xU.js} +2 -2
- package/resources/fe-dist/assets/{wardley-L42UT6IY-DvfZKNwT.js → wardley-L42UT6IY-OsbD5r1L.js} +2 -2
- package/resources/fe-dist/assets/{wardleyDiagram-YWT4CUSO-DlABpMGt.js → wardleyDiagram-YWT4CUSO-B_33zIif.js} +2 -2
- package/resources/fe-dist/assets/{xychartDiagram-2RQKCTM6-BDiyUuus.js → xychartDiagram-2RQKCTM6-DcB27Ap1.js} +2 -2
- 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/fe-dist/assets/DevicePage-5vkaNOjl.js +0 -26
- package/resources/fe-dist/assets/DevicesPage-CKnRUGQm.js +0 -2
- package/resources/fe-dist/assets/FilePage-Dj0cszD4.css +0 -1
- package/resources/fe-dist/assets/FilePage-Uj-tzGW7.js +0 -283
- package/resources/fe-dist/assets/SettingsPage-Cfzr_Phh.js +0 -22
- package/resources/fe-dist/assets/channel-DyLhDM_y.js +0 -2
- package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-DA8DiM06.js +0 -2
- package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-DA8DiM06.js +0 -2
- package/resources/fe-dist/assets/index-OpBFduyh.js +0 -466
- package/resources/fe-dist/assets/index-yMgNjHUH.css +0 -1
- package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-DH2ctCND.js +0 -2
- 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 as extname2, join as
|
|
22552
|
+
import { existsSync as existsSync8 } from "fs";
|
|
22553
|
+
import { extname as extname2, join as join8, normalize, resolve as resolve6, sep } from "path";
|
|
22554
22554
|
|
|
22555
22555
|
// ../../apps/gateway/src/crypto/errors.ts
|
|
22556
22556
|
function contextLabel(context) {
|
|
@@ -22659,6 +22659,8 @@ var I18N_RESOURCES = {
|
|
|
22659
22659
|
authKey: "Private Key",
|
|
22660
22660
|
authAgent: "SSH Agent",
|
|
22661
22661
|
authConfigRef: "SSH Config",
|
|
22662
|
+
sshConfigRefPlaceholder: "Host alias, e.g. my-server",
|
|
22663
|
+
sshConfigRefHint: "A Host alias from your ssh config, resolved via `ssh -G`. Not the path to the config file.",
|
|
22662
22664
|
password: "Password",
|
|
22663
22665
|
privateKey: "Private Key",
|
|
22664
22666
|
privateKeyPlaceholder: "-----BEGIN OPENSSH PRIVATE KEY-----",
|
|
@@ -22740,11 +22742,22 @@ var I18N_RESOURCES = {
|
|
|
22740
22742
|
language_en_US: "English",
|
|
22741
22743
|
language_zh_CN: "\u7B80\u4F53\u4E2D\u6587",
|
|
22742
22744
|
language_ja_JP: "\u65E5\u672C\u8A9E",
|
|
22743
|
-
theme: "
|
|
22745
|
+
theme: "Dark Mode",
|
|
22744
22746
|
themeLight: "Light",
|
|
22745
22747
|
themeDark: "Dark",
|
|
22746
22748
|
siteTab: "Site",
|
|
22747
22749
|
notificationsTab: "Notifications",
|
|
22750
|
+
tabGroup: {
|
|
22751
|
+
general: "General",
|
|
22752
|
+
devicesAndFiles: "Devices & Files",
|
|
22753
|
+
notifications: "Notifications",
|
|
22754
|
+
ai: "AI"
|
|
22755
|
+
},
|
|
22756
|
+
deviceManagement: {
|
|
22757
|
+
title: "Device Management",
|
|
22758
|
+
description: "Add, edit, and connect to your local and SSH devices.",
|
|
22759
|
+
openButton: "Open Device Management"
|
|
22760
|
+
},
|
|
22748
22761
|
refreshToApply: "Refresh to apply language change",
|
|
22749
22762
|
saveSettings: "Save Settings",
|
|
22750
22763
|
settingsSaved: "Settings saved",
|
|
@@ -22798,6 +22811,7 @@ var I18N_RESOURCES = {
|
|
|
22798
22811
|
search: {
|
|
22799
22812
|
title: "Search",
|
|
22800
22813
|
provider: "Search Provider",
|
|
22814
|
+
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
22815
|
providerNone: "Disabled",
|
|
22802
22816
|
tavilyApiKey: "Tavily API Key",
|
|
22803
22817
|
braveApiKey: "Brave API Key",
|
|
@@ -22830,6 +22844,35 @@ var I18N_RESOURCES = {
|
|
|
22830
22844
|
toggleFailed: "Failed to update directory",
|
|
22831
22845
|
deleteTitle: "Remove directory?",
|
|
22832
22846
|
deleteDesc: 'Remove "{{path}}" from the whitelist? Files under it will no longer be accessible.'
|
|
22847
|
+
},
|
|
22848
|
+
version: {
|
|
22849
|
+
title: "Version & Updates",
|
|
22850
|
+
currentVersion: "Current version",
|
|
22851
|
+
installMethod: "Install method",
|
|
22852
|
+
installMethodCli: "Installed via CLI",
|
|
22853
|
+
installMethodNonCli: "Not installed via CLI",
|
|
22854
|
+
deployment: "Deployment",
|
|
22855
|
+
deploymentLaunchd: "launchd (macOS)",
|
|
22856
|
+
deploymentSystemd: "systemd (Linux)",
|
|
22857
|
+
deploymentNone: "None",
|
|
22858
|
+
checkUpdate: "Check for updates",
|
|
22859
|
+
checking: "Checking\u2026",
|
|
22860
|
+
upToDate: "You are on the latest version.",
|
|
22861
|
+
updateAvailable: "Update available: {{version}}",
|
|
22862
|
+
changelog: "Changelog",
|
|
22863
|
+
changelogUnavailable: "No changelog available for this version.",
|
|
22864
|
+
publishedAt: "Published {{date}}",
|
|
22865
|
+
upgrade: "Upgrade now",
|
|
22866
|
+
upgradeDisabledDev: "In-app update is disabled outside the production environment.",
|
|
22867
|
+
upgradeDisabledNonCli: "In-app update is only available for CLI installations.",
|
|
22868
|
+
upgradeWarningTitle: "Confirm upgrade",
|
|
22869
|
+
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?",
|
|
22870
|
+
upgradeStarted: "Upgrade started",
|
|
22871
|
+
stateDownloading: "Downloading the new version\u2026",
|
|
22872
|
+
stateExecuting: "Applying the upgrade, the service is restarting\u2026",
|
|
22873
|
+
interruptNotice: "The connection will drop while the service restarts; it will recover shortly.",
|
|
22874
|
+
checkFailed: "Failed to check for updates",
|
|
22875
|
+
terminalHint: "Or upgrade from a terminal: npx tmex-cli@<version> upgrade"
|
|
22833
22876
|
}
|
|
22834
22877
|
},
|
|
22835
22878
|
telegram: {
|
|
@@ -22839,6 +22882,7 @@ var I18N_RESOURCES = {
|
|
|
22839
22882
|
botToken: "Bot Token",
|
|
22840
22883
|
botTokenPlaceholder: "123456:AA...",
|
|
22841
22884
|
addBot: "Add Bot",
|
|
22885
|
+
editBot: "Edit Bot",
|
|
22842
22886
|
enableBot: "Enable Bot",
|
|
22843
22887
|
allowAuthRequests: "Allow Authorization Requests",
|
|
22844
22888
|
pendingChats: "Pending Authorization",
|
|
@@ -23015,7 +23059,11 @@ Time: {{time}}`,
|
|
|
23015
23059
|
fileTooLarge: "File is too large",
|
|
23016
23060
|
fileBinary: "Binary files are not supported",
|
|
23017
23061
|
fileRootDeviceInvalid: "Invalid or unknown device",
|
|
23018
|
-
fileRootDuplicate: "This directory is already added for the device"
|
|
23062
|
+
fileRootDuplicate: "This directory is already added for the device",
|
|
23063
|
+
upgradeNotAllowed: "In-app update is not available for this installation.",
|
|
23064
|
+
upgradeInProgress: "An upgrade is already in progress.",
|
|
23065
|
+
upgradeVersionRequired: "Target version is required.",
|
|
23066
|
+
updateCheckFailed: "Failed to query the npm registry."
|
|
23019
23067
|
},
|
|
23020
23068
|
notification: {
|
|
23021
23069
|
clickToJump: "Click to jump to corresponding pane",
|
|
@@ -23304,7 +23352,10 @@ Time: {{time}}`,
|
|
|
23304
23352
|
},
|
|
23305
23353
|
validation: {
|
|
23306
23354
|
deviceNameRequired: "Device name is required",
|
|
23307
|
-
hostRequired: "Host is required for SSH devices"
|
|
23355
|
+
hostRequired: "Host is required for SSH devices",
|
|
23356
|
+
portRequired: "A valid port is required for SSH devices",
|
|
23357
|
+
usernameRequired: "Username is required for SSH devices",
|
|
23358
|
+
sshConfigRequired: "SSH config path is required for SSH devices"
|
|
23308
23359
|
},
|
|
23309
23360
|
files: {
|
|
23310
23361
|
title: "Files",
|
|
@@ -23333,12 +23384,45 @@ Time: {{time}}`,
|
|
|
23333
23384
|
unknown: "Failed to load"
|
|
23334
23385
|
},
|
|
23335
23386
|
retry: "Retry (collapse then expand)",
|
|
23387
|
+
menu: {
|
|
23388
|
+
copyAbsolute: "Copy absolute path",
|
|
23389
|
+
copyRelative: "Copy relative path",
|
|
23390
|
+
sendToAgent: "Send to Agent",
|
|
23391
|
+
expand: "Expand",
|
|
23392
|
+
collapse: "Collapse",
|
|
23393
|
+
upload: "Upload files here",
|
|
23394
|
+
open: "Open"
|
|
23395
|
+
},
|
|
23396
|
+
copied: "Copied to clipboard",
|
|
23397
|
+
copyFailed: "Copy failed",
|
|
23398
|
+
sendToAgent: {
|
|
23399
|
+
prompt: "Please work with this path: `{{path}}`"
|
|
23400
|
+
},
|
|
23401
|
+
upload: {
|
|
23402
|
+
uploading: "Uploading {{name}}\u2026",
|
|
23403
|
+
success: "Uploaded {{name}}",
|
|
23404
|
+
fail: "Failed to upload {{name}}"
|
|
23405
|
+
},
|
|
23406
|
+
transfer: {
|
|
23407
|
+
legUserToTmex: "Browser \u2192 tmex",
|
|
23408
|
+
legTmexToServer: "tmex \u2192 Server",
|
|
23409
|
+
legServerToTmex: "Server \u2192 tmex",
|
|
23410
|
+
legTmexToUser: "tmex \u2192 Browser",
|
|
23411
|
+
cancel: "Cancel",
|
|
23412
|
+
canceled: "Canceled {{name}}",
|
|
23413
|
+
downloaded: "Downloaded {{name}}",
|
|
23414
|
+
downloadFailed: "Failed to download {{name}}",
|
|
23415
|
+
dragDownloadStarted: "Started downloading {{name}} (handled by browser)",
|
|
23416
|
+
tooLarge: "{{name}} exceeds the size limit ({{max}})"
|
|
23417
|
+
},
|
|
23418
|
+
agentLaunch: {
|
|
23419
|
+
connectFailed: "Failed to connect to the device",
|
|
23420
|
+
windowFailed: "Failed to create a window"
|
|
23421
|
+
},
|
|
23336
23422
|
install: {
|
|
23337
23423
|
button: "Install rsync",
|
|
23338
23424
|
scopeLocal: "server",
|
|
23339
23425
|
scopeRemote: "remote",
|
|
23340
|
-
connectFailed: "Failed to connect to the device",
|
|
23341
|
-
windowFailed: "Failed to create a window",
|
|
23342
23426
|
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."
|
|
23343
23427
|
}
|
|
23344
23428
|
},
|
|
@@ -23438,6 +23522,8 @@ Time: {{time}}`,
|
|
|
23438
23522
|
authKey: "\u79C1\u94A5",
|
|
23439
23523
|
authAgent: "SSH Agent",
|
|
23440
23524
|
authConfigRef: "SSH Config",
|
|
23525
|
+
sshConfigRefPlaceholder: "Host \u522B\u540D\uFF0C\u5982 my-server",
|
|
23526
|
+
sshConfigRefHint: "\u586B ssh config \u91CC\u7684 Host \u522B\u540D\uFF0C\u7ECF `ssh -G` \u89E3\u6790\uFF1B\u4E0D\u662F config \u6587\u4EF6\u8DEF\u5F84\u3002",
|
|
23441
23527
|
password: "\u5BC6\u7801",
|
|
23442
23528
|
privateKey: "\u79C1\u94A5",
|
|
23443
23529
|
privateKeyPlaceholder: "-----BEGIN OPENSSH PRIVATE KEY-----",
|
|
@@ -23519,11 +23605,22 @@ Time: {{time}}`,
|
|
|
23519
23605
|
language_en_US: "English",
|
|
23520
23606
|
language_zh_CN: "\u7B80\u4F53\u4E2D\u6587",
|
|
23521
23607
|
language_ja_JP: "\u65E5\u672C\u8A9E",
|
|
23522
|
-
theme: "\
|
|
23608
|
+
theme: "\u6DF1\u8272\u6A21\u5F0F",
|
|
23523
23609
|
themeLight: "\u6D45\u8272",
|
|
23524
23610
|
themeDark: "\u6DF1\u8272",
|
|
23525
23611
|
siteTab: "\u7AD9\u70B9",
|
|
23526
23612
|
notificationsTab: "\u901A\u77E5",
|
|
23613
|
+
tabGroup: {
|
|
23614
|
+
general: "\u901A\u7528\u8BBE\u7F6E",
|
|
23615
|
+
devicesAndFiles: "\u8BBE\u5907\u4E0E\u6587\u4EF6",
|
|
23616
|
+
notifications: "\u901A\u77E5\u8BBE\u7F6E",
|
|
23617
|
+
ai: "AI \u8BBE\u7F6E"
|
|
23618
|
+
},
|
|
23619
|
+
deviceManagement: {
|
|
23620
|
+
title: "\u8BBE\u5907\u7BA1\u7406",
|
|
23621
|
+
description: "\u6DFB\u52A0\u3001\u7F16\u8F91\u5E76\u8FDE\u63A5\u4F60\u7684\u672C\u5730\u4E0E SSH \u8BBE\u5907\u3002",
|
|
23622
|
+
openButton: "\u6253\u5F00\u8BBE\u5907\u7BA1\u7406"
|
|
23623
|
+
},
|
|
23527
23624
|
refreshToApply: "\u5237\u65B0\u540E\u751F\u6548",
|
|
23528
23625
|
saveSettings: "\u4FDD\u5B58\u8BBE\u7F6E",
|
|
23529
23626
|
settingsSaved: "\u7AD9\u70B9\u8BBE\u7F6E\u5DF2\u4FDD\u5B58",
|
|
@@ -23577,6 +23674,7 @@ Time: {{time}}`,
|
|
|
23577
23674
|
search: {
|
|
23578
23675
|
title: "\u641C\u7D22",
|
|
23579
23676
|
provider: "\u641C\u7D22\u63D0\u4F9B\u5546",
|
|
23677
|
+
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",
|
|
23580
23678
|
providerNone: "\u4E0D\u542F\u7528",
|
|
23581
23679
|
tavilyApiKey: "Tavily API Key",
|
|
23582
23680
|
braveApiKey: "Brave API Key",
|
|
@@ -23609,6 +23707,35 @@ Time: {{time}}`,
|
|
|
23609
23707
|
toggleFailed: "\u66F4\u65B0\u76EE\u5F55\u5931\u8D25",
|
|
23610
23708
|
deleteTitle: "\u79FB\u9664\u76EE\u5F55\uFF1F",
|
|
23611
23709
|
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"
|
|
23710
|
+
},
|
|
23711
|
+
version: {
|
|
23712
|
+
title: "\u7248\u672C\u4E0E\u66F4\u65B0",
|
|
23713
|
+
currentVersion: "\u5F53\u524D\u7248\u672C",
|
|
23714
|
+
installMethod: "\u5B89\u88C5\u65B9\u5F0F",
|
|
23715
|
+
installMethodCli: "\u901A\u8FC7 CLI \u5B89\u88C5",
|
|
23716
|
+
installMethodNonCli: "\u975E CLI \u5B89\u88C5",
|
|
23717
|
+
deployment: "\u90E8\u7F72\u65B9\u5F0F",
|
|
23718
|
+
deploymentLaunchd: "launchd\uFF08macOS\uFF09",
|
|
23719
|
+
deploymentSystemd: "systemd\uFF08Linux\uFF09",
|
|
23720
|
+
deploymentNone: "\u65E0",
|
|
23721
|
+
checkUpdate: "\u68C0\u67E5\u66F4\u65B0",
|
|
23722
|
+
checking: "\u68C0\u67E5\u4E2D\u2026",
|
|
23723
|
+
upToDate: "\u5DF2\u662F\u6700\u65B0\u7248\u672C\u3002",
|
|
23724
|
+
updateAvailable: "\u6709\u53EF\u7528\u66F4\u65B0\uFF1A{{version}}",
|
|
23725
|
+
changelog: "\u66F4\u65B0\u65E5\u5FD7",
|
|
23726
|
+
changelogUnavailable: "\u8BE5\u7248\u672C\u6682\u65E0\u66F4\u65B0\u65E5\u5FD7\u3002",
|
|
23727
|
+
publishedAt: "\u53D1\u5E03\u4E8E {{date}}",
|
|
23728
|
+
upgrade: "\u7ACB\u5373\u5347\u7EA7",
|
|
23729
|
+
upgradeDisabledDev: "\u975E production \u73AF\u5883\u5DF2\u7981\u7528\u7A0B\u5E8F\u5185\u66F4\u65B0\u3002",
|
|
23730
|
+
upgradeDisabledNonCli: "\u7A0B\u5E8F\u5185\u66F4\u65B0\u4EC5\u5728\u901A\u8FC7 CLI \u5B89\u88C5\u65F6\u53EF\u7528\u3002",
|
|
23731
|
+
upgradeWarningTitle: "\u786E\u8BA4\u5347\u7EA7",
|
|
23732
|
+
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",
|
|
23733
|
+
upgradeStarted: "\u5347\u7EA7\u5DF2\u5F00\u59CB",
|
|
23734
|
+
stateDownloading: "\u6B63\u5728\u4E0B\u8F7D\u65B0\u7248\u672C\u2026",
|
|
23735
|
+
stateExecuting: "\u6B63\u5728\u6267\u884C\u5347\u7EA7\uFF0C\u670D\u52A1\u5373\u5C06\u91CD\u542F\u2026",
|
|
23736
|
+
interruptNotice: "\u670D\u52A1\u91CD\u542F\u671F\u95F4\u8FDE\u63A5\u4F1A\u65AD\u5F00\uFF0C\u7A0D\u5019\u5373\u53EF\u6062\u590D\u3002",
|
|
23737
|
+
checkFailed: "\u68C0\u67E5\u66F4\u65B0\u5931\u8D25",
|
|
23738
|
+
terminalHint: "\u6216\u901A\u8FC7\u7EC8\u7AEF\u5347\u7EA7\uFF1Anpx tmex-cli@<version> upgrade"
|
|
23612
23739
|
}
|
|
23613
23740
|
},
|
|
23614
23741
|
telegram: {
|
|
@@ -23618,6 +23745,7 @@ Time: {{time}}`,
|
|
|
23618
23745
|
botToken: "Bot Token",
|
|
23619
23746
|
botTokenPlaceholder: "123456:AA...",
|
|
23620
23747
|
addBot: "\u65B0\u589E Bot",
|
|
23748
|
+
editBot: "\u7F16\u8F91 Bot",
|
|
23621
23749
|
enableBot: "\u542F\u7528 Bot",
|
|
23622
23750
|
allowAuthRequests: "\u5141\u8BB8\u7533\u8BF7\u6388\u6743",
|
|
23623
23751
|
pendingChats: "\u5F85\u6388\u6743",
|
|
@@ -23794,7 +23922,11 @@ Bot\uFF1A{{botName}}
|
|
|
23794
23922
|
fileTooLarge: "\u6587\u4EF6\u8FC7\u5927",
|
|
23795
23923
|
fileBinary: "\u4E0D\u652F\u6301\u4E8C\u8FDB\u5236\u6587\u4EF6",
|
|
23796
23924
|
fileRootDeviceInvalid: "\u65E0\u6548\u6216\u672A\u77E5\u7684\u8BBE\u5907",
|
|
23797
|
-
fileRootDuplicate: "\u8BE5\u8BBE\u5907\u4E0B\u5DF2\u6DFB\u52A0\u8FC7\u6B64\u76EE\u5F55"
|
|
23925
|
+
fileRootDuplicate: "\u8BE5\u8BBE\u5907\u4E0B\u5DF2\u6DFB\u52A0\u8FC7\u6B64\u76EE\u5F55",
|
|
23926
|
+
upgradeNotAllowed: "\u5F53\u524D\u5B89\u88C5\u65B9\u5F0F\u4E0D\u652F\u6301\u7A0B\u5E8F\u5185\u66F4\u65B0\u3002",
|
|
23927
|
+
upgradeInProgress: "\u5DF2\u6709\u5347\u7EA7\u4EFB\u52A1\u6B63\u5728\u8FDB\u884C\u3002",
|
|
23928
|
+
upgradeVersionRequired: "\u7F3A\u5C11\u76EE\u6807\u7248\u672C\u53F7\u3002",
|
|
23929
|
+
updateCheckFailed: "\u67E5\u8BE2 npm registry \u5931\u8D25\u3002"
|
|
23798
23930
|
},
|
|
23799
23931
|
notification: {
|
|
23800
23932
|
clickToJump: "\u70B9\u51FB\u8DF3\u8F6C\u5230\u5BF9\u5E94 Pane",
|
|
@@ -24083,7 +24215,10 @@ Bot\uFF1A{{botName}}
|
|
|
24083
24215
|
},
|
|
24084
24216
|
validation: {
|
|
24085
24217
|
deviceNameRequired: "\u8BBE\u5907\u540D\u79F0\u4E3A\u5FC5\u586B\u9879",
|
|
24086
|
-
hostRequired: "SSH \u8BBE\u5907\u9700\u8981\u586B\u5199\u4E3B\u673A\u5730\u5740"
|
|
24218
|
+
hostRequired: "SSH \u8BBE\u5907\u9700\u8981\u586B\u5199\u4E3B\u673A\u5730\u5740",
|
|
24219
|
+
portRequired: "SSH \u8BBE\u5907\u9700\u8981\u586B\u5199\u6709\u6548\u7AEF\u53E3",
|
|
24220
|
+
usernameRequired: "SSH \u8BBE\u5907\u9700\u8981\u586B\u5199\u7528\u6237\u540D",
|
|
24221
|
+
sshConfigRequired: "SSH \u8BBE\u5907\u9700\u8981\u586B\u5199 ssh config \u8DEF\u5F84"
|
|
24087
24222
|
},
|
|
24088
24223
|
files: {
|
|
24089
24224
|
title: "\u6587\u4EF6",
|
|
@@ -24112,12 +24247,45 @@ Bot\uFF1A{{botName}}
|
|
|
24112
24247
|
unknown: "\u52A0\u8F7D\u5931\u8D25"
|
|
24113
24248
|
},
|
|
24114
24249
|
retry: "\u91CD\u8BD5\uFF08\u6536\u8D77\u540E\u518D\u5C55\u5F00\uFF09",
|
|
24250
|
+
menu: {
|
|
24251
|
+
copyAbsolute: "\u590D\u5236\u7EDD\u5BF9\u4F4D\u7F6E",
|
|
24252
|
+
copyRelative: "\u590D\u5236\u76F8\u5BF9\u4F4D\u7F6E",
|
|
24253
|
+
sendToAgent: "\u53D1\u9001\u5230 Agent",
|
|
24254
|
+
expand: "\u5C55\u5F00",
|
|
24255
|
+
collapse: "\u6536\u8D77",
|
|
24256
|
+
upload: "\u4E0A\u4F20\u6587\u4EF6\u5230\u8FD9\u4E2A\u6587\u4EF6\u5939",
|
|
24257
|
+
open: "\u6253\u5F00"
|
|
24258
|
+
},
|
|
24259
|
+
copied: "\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F",
|
|
24260
|
+
copyFailed: "\u590D\u5236\u5931\u8D25",
|
|
24261
|
+
sendToAgent: {
|
|
24262
|
+
prompt: "\u8BF7\u5904\u7406\u8FD9\u4E2A\u8DEF\u5F84\uFF1A`{{path}}`"
|
|
24263
|
+
},
|
|
24264
|
+
upload: {
|
|
24265
|
+
uploading: "\u6B63\u5728\u4E0A\u4F20 {{name}}\u2026",
|
|
24266
|
+
success: "\u5DF2\u4E0A\u4F20 {{name}}",
|
|
24267
|
+
fail: "\u4E0A\u4F20 {{name}} \u5931\u8D25"
|
|
24268
|
+
},
|
|
24269
|
+
transfer: {
|
|
24270
|
+
legUserToTmex: "\u7528\u6237 \u2192 tmex",
|
|
24271
|
+
legTmexToServer: "tmex \u2192 \u670D\u52A1\u5668",
|
|
24272
|
+
legServerToTmex: "\u670D\u52A1\u5668 \u2192 tmex",
|
|
24273
|
+
legTmexToUser: "tmex \u2192 \u7528\u6237",
|
|
24274
|
+
cancel: "\u53D6\u6D88",
|
|
24275
|
+
canceled: "\u5DF2\u53D6\u6D88 {{name}}",
|
|
24276
|
+
downloaded: "\u5DF2\u4E0B\u8F7D {{name}}",
|
|
24277
|
+
downloadFailed: "\u4E0B\u8F7D {{name}} \u5931\u8D25",
|
|
24278
|
+
dragDownloadStarted: "\u5DF2\u5F00\u59CB\u4E0B\u8F7D {{name}}\uFF08\u7531\u6D4F\u89C8\u5668\u63A5\u7BA1\uFF09",
|
|
24279
|
+
tooLarge: "{{name}} \u8D85\u8FC7\u5927\u5C0F\u4E0A\u9650\uFF08{{max}}\uFF09"
|
|
24280
|
+
},
|
|
24281
|
+
agentLaunch: {
|
|
24282
|
+
connectFailed: "\u8FDE\u63A5\u8BBE\u5907\u5931\u8D25",
|
|
24283
|
+
windowFailed: "\u521B\u5EFA\u7A97\u53E3\u5931\u8D25"
|
|
24284
|
+
},
|
|
24115
24285
|
install: {
|
|
24116
24286
|
button: "\u5B89\u88C5 rsync",
|
|
24117
24287
|
scopeLocal: "\u670D\u52A1\u5668",
|
|
24118
24288
|
scopeRemote: "\u8FDC\u7A0B",
|
|
24119
|
-
connectFailed: "\u8FDE\u63A5\u8BBE\u5907\u5931\u8D25",
|
|
24120
|
-
windowFailed: "\u521B\u5EFA\u7A97\u53E3\u5931\u8D25",
|
|
24121
24289
|
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"
|
|
24122
24290
|
}
|
|
24123
24291
|
},
|
|
@@ -24217,6 +24385,8 @@ Bot\uFF1A{{botName}}
|
|
|
24217
24385
|
authKey: "\u79D8\u5BC6\u9375",
|
|
24218
24386
|
authAgent: "SSH Agent",
|
|
24219
24387
|
authConfigRef: "SSH Config",
|
|
24388
|
+
sshConfigRefPlaceholder: "Host \u30A8\u30A4\u30EA\u30A2\u30B9\uFF08\u4F8B: my-server\uFF09",
|
|
24389
|
+
sshConfigRefHint: "ssh config \u306E Host \u30A8\u30A4\u30EA\u30A2\u30B9\u3092\u5165\u529B\u3057\u307E\u3059\u3002`ssh -G` \u3067\u89E3\u6C7A\u3055\u308C\u307E\u3059\u3002config \u30D5\u30A1\u30A4\u30EB\u306E\u30D1\u30B9\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002",
|
|
24220
24390
|
password: "\u30D1\u30B9\u30EF\u30FC\u30C9",
|
|
24221
24391
|
privateKey: "\u79D8\u5BC6\u9375",
|
|
24222
24392
|
privateKeyPlaceholder: "-----BEGIN OPENSSH PRIVATE KEY-----",
|
|
@@ -24298,11 +24468,22 @@ Bot\uFF1A{{botName}}
|
|
|
24298
24468
|
language_en_US: "English",
|
|
24299
24469
|
language_zh_CN: "\u7B80\u4F53\u4E2D\u6587",
|
|
24300
24470
|
language_ja_JP: "\u65E5\u672C\u8A9E",
|
|
24301
|
-
theme: "\
|
|
24471
|
+
theme: "\u30C0\u30FC\u30AF\u30E2\u30FC\u30C9",
|
|
24302
24472
|
themeLight: "\u30E9\u30A4\u30C8",
|
|
24303
24473
|
themeDark: "\u30C0\u30FC\u30AF",
|
|
24304
24474
|
siteTab: "\u30B5\u30A4\u30C8",
|
|
24305
24475
|
notificationsTab: "\u901A\u77E5",
|
|
24476
|
+
tabGroup: {
|
|
24477
|
+
general: "\u4E00\u822C\u8A2D\u5B9A",
|
|
24478
|
+
devicesAndFiles: "\u30C7\u30D0\u30A4\u30B9\u3068\u30D5\u30A1\u30A4\u30EB",
|
|
24479
|
+
notifications: "\u901A\u77E5\u8A2D\u5B9A",
|
|
24480
|
+
ai: "AI \u8A2D\u5B9A"
|
|
24481
|
+
},
|
|
24482
|
+
deviceManagement: {
|
|
24483
|
+
title: "\u30C7\u30D0\u30A4\u30B9\u7BA1\u7406",
|
|
24484
|
+
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",
|
|
24485
|
+
openButton: "\u30C7\u30D0\u30A4\u30B9\u7BA1\u7406\u3092\u958B\u304F"
|
|
24486
|
+
},
|
|
24306
24487
|
refreshToApply: "\u8A00\u8A9E\u5909\u66F4\u3092\u9069\u7528\u3059\u308B\u306B\u306F\u66F4\u65B0\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
24307
24488
|
saveSettings: "\u8A2D\u5B9A\u3092\u4FDD\u5B58",
|
|
24308
24489
|
settingsSaved: "\u8A2D\u5B9A\u3092\u4FDD\u5B58\u3057\u307E\u3057\u305F",
|
|
@@ -24356,6 +24537,7 @@ Bot\uFF1A{{botName}}
|
|
|
24356
24537
|
search: {
|
|
24357
24538
|
title: "\u691C\u7D22",
|
|
24358
24539
|
provider: "\u691C\u7D22\u30D7\u30ED\u30D0\u30A4\u30C0\u30FC",
|
|
24540
|
+
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",
|
|
24359
24541
|
providerNone: "\u7121\u52B9",
|
|
24360
24542
|
tavilyApiKey: "Tavily API \u30AD\u30FC",
|
|
24361
24543
|
braveApiKey: "Brave API \u30AD\u30FC",
|
|
@@ -24388,6 +24570,35 @@ Bot\uFF1A{{botName}}
|
|
|
24388
24570
|
toggleFailed: "\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306E\u66F4\u65B0\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
24389
24571
|
deleteTitle: "\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u524A\u9664\u3057\u307E\u3059\u304B\uFF1F",
|
|
24390
24572
|
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"
|
|
24573
|
+
},
|
|
24574
|
+
version: {
|
|
24575
|
+
title: "\u30D0\u30FC\u30B8\u30E7\u30F3\u3068\u66F4\u65B0",
|
|
24576
|
+
currentVersion: "\u73FE\u5728\u306E\u30D0\u30FC\u30B8\u30E7\u30F3",
|
|
24577
|
+
installMethod: "\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u65B9\u6CD5",
|
|
24578
|
+
installMethodCli: "CLI \u3067\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB",
|
|
24579
|
+
installMethodNonCli: "CLI \u4EE5\u5916\u3067\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB",
|
|
24580
|
+
deployment: "\u30C7\u30D7\u30ED\u30A4\u65B9\u5F0F",
|
|
24581
|
+
deploymentLaunchd: "launchd\uFF08macOS\uFF09",
|
|
24582
|
+
deploymentSystemd: "systemd\uFF08Linux\uFF09",
|
|
24583
|
+
deploymentNone: "\u306A\u3057",
|
|
24584
|
+
checkUpdate: "\u66F4\u65B0\u3092\u78BA\u8A8D",
|
|
24585
|
+
checking: "\u78BA\u8A8D\u4E2D\u2026",
|
|
24586
|
+
upToDate: "\u6700\u65B0\u30D0\u30FC\u30B8\u30E7\u30F3\u3067\u3059\u3002",
|
|
24587
|
+
updateAvailable: "\u66F4\u65B0\u304C\u3042\u308A\u307E\u3059\uFF1A{{version}}",
|
|
24588
|
+
changelog: "\u5909\u66F4\u5C65\u6B74",
|
|
24589
|
+
changelogUnavailable: "\u3053\u306E\u30D0\u30FC\u30B8\u30E7\u30F3\u306E\u5909\u66F4\u5C65\u6B74\u306F\u3042\u308A\u307E\u305B\u3093\u3002",
|
|
24590
|
+
publishedAt: "\u516C\u958B\u65E5 {{date}}",
|
|
24591
|
+
upgrade: "\u4ECA\u3059\u3050\u66F4\u65B0",
|
|
24592
|
+
upgradeDisabledDev: "production \u4EE5\u5916\u306E\u74B0\u5883\u3067\u306F\u30A2\u30D7\u30EA\u5185\u66F4\u65B0\u306F\u7121\u52B9\u3067\u3059\u3002",
|
|
24593
|
+
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",
|
|
24594
|
+
upgradeWarningTitle: "\u66F4\u65B0\u306E\u78BA\u8A8D",
|
|
24595
|
+
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",
|
|
24596
|
+
upgradeStarted: "\u66F4\u65B0\u3092\u958B\u59CB\u3057\u307E\u3057\u305F",
|
|
24597
|
+
stateDownloading: "\u65B0\u3057\u3044\u30D0\u30FC\u30B8\u30E7\u30F3\u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u4E2D\u2026",
|
|
24598
|
+
stateExecuting: "\u66F4\u65B0\u3092\u9069\u7528\u4E2D\u3001\u30B5\u30FC\u30D3\u30B9\u3092\u518D\u8D77\u52D5\u3057\u3066\u3044\u307E\u3059\u2026",
|
|
24599
|
+
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",
|
|
24600
|
+
checkFailed: "\u66F4\u65B0\u306E\u78BA\u8A8D\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
24601
|
+
terminalHint: "\u307E\u305F\u306F\u30BF\u30FC\u30DF\u30CA\u30EB\u304B\u3089\u66F4\u65B0\uFF1Anpx tmex-cli@<version> upgrade"
|
|
24391
24602
|
}
|
|
24392
24603
|
},
|
|
24393
24604
|
telegram: {
|
|
@@ -24397,6 +24608,7 @@ Bot\uFF1A{{botName}}
|
|
|
24397
24608
|
botToken: "Bot Token",
|
|
24398
24609
|
botTokenPlaceholder: "123456:AA...",
|
|
24399
24610
|
addBot: "Bot \u3092\u8FFD\u52A0",
|
|
24611
|
+
editBot: "Bot \u3092\u7DE8\u96C6",
|
|
24400
24612
|
enableBot: "Bot \u3092\u6709\u52B9\u306B\u3059\u308B",
|
|
24401
24613
|
allowAuthRequests: "\u8A8D\u8A3C\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u8A31\u53EF",
|
|
24402
24614
|
pendingChats: "\u627F\u8A8D\u5F85\u3061",
|
|
@@ -24573,7 +24785,11 @@ Bot\uFF1A{{botName}}
|
|
|
24573
24785
|
fileTooLarge: "\u30D5\u30A1\u30A4\u30EB\u304C\u5927\u304D\u3059\u304E\u307E\u3059",
|
|
24574
24786
|
fileBinary: "\u30D0\u30A4\u30CA\u30EA\u30D5\u30A1\u30A4\u30EB\u306F\u30B5\u30DD\u30FC\u30C8\u3055\u308C\u3066\u3044\u307E\u305B\u3093",
|
|
24575
24787
|
fileRootDeviceInvalid: "\u7121\u52B9\u307E\u305F\u306F\u4E0D\u660E\u306A\u30C7\u30D0\u30A4\u30B9",
|
|
24576
|
-
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"
|
|
24788
|
+
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",
|
|
24789
|
+
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",
|
|
24790
|
+
upgradeInProgress: "\u3059\u3067\u306B\u66F4\u65B0\u304C\u9032\u884C\u4E2D\u3067\u3059\u3002",
|
|
24791
|
+
upgradeVersionRequired: "\u5BFE\u8C61\u30D0\u30FC\u30B8\u30E7\u30F3\u304C\u5FC5\u8981\u3067\u3059\u3002",
|
|
24792
|
+
updateCheckFailed: "npm registry \u306E\u7167\u4F1A\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002"
|
|
24577
24793
|
},
|
|
24578
24794
|
notification: {
|
|
24579
24795
|
clickToJump: "\u5BFE\u5FDC\u3059\u308B\u30DA\u30A4\u30F3\u306B\u30B8\u30E3\u30F3\u30D7",
|
|
@@ -24862,7 +25078,10 @@ Bot\uFF1A{{botName}}
|
|
|
24862
25078
|
},
|
|
24863
25079
|
validation: {
|
|
24864
25080
|
deviceNameRequired: "\u30C7\u30D0\u30A4\u30B9\u540D\u306F\u5FC5\u9808\u3067\u3059",
|
|
24865
|
-
hostRequired: "SSH \u30C7\u30D0\u30A4\u30B9\u306B\u306F\u30DB\u30B9\u30C8\u30A2\u30C9\u30EC\u30B9\u304C\u5FC5\u8981\u3067\u3059"
|
|
25081
|
+
hostRequired: "SSH \u30C7\u30D0\u30A4\u30B9\u306B\u306F\u30DB\u30B9\u30C8\u30A2\u30C9\u30EC\u30B9\u304C\u5FC5\u8981\u3067\u3059",
|
|
25082
|
+
portRequired: "SSH \u30C7\u30D0\u30A4\u30B9\u306B\u306F\u6709\u52B9\u306A\u30DD\u30FC\u30C8\u304C\u5FC5\u8981\u3067\u3059",
|
|
25083
|
+
usernameRequired: "SSH \u30C7\u30D0\u30A4\u30B9\u306B\u306F\u30E6\u30FC\u30B6\u30FC\u540D\u304C\u5FC5\u8981\u3067\u3059",
|
|
25084
|
+
sshConfigRequired: "SSH \u30C7\u30D0\u30A4\u30B9\u306B\u306F ssh config \u306E\u30D1\u30B9\u304C\u5FC5\u8981\u3067\u3059"
|
|
24866
25085
|
},
|
|
24867
25086
|
files: {
|
|
24868
25087
|
title: "\u30D5\u30A1\u30A4\u30EB",
|
|
@@ -24891,12 +25110,45 @@ Bot\uFF1A{{botName}}
|
|
|
24891
25110
|
unknown: "\u8AAD\u307F\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
|
|
24892
25111
|
},
|
|
24893
25112
|
retry: "\u518D\u8A66\u884C\uFF08\u6298\u308A\u305F\u305F\u3093\u3067\u518D\u5C55\u958B\uFF09",
|
|
25113
|
+
menu: {
|
|
25114
|
+
copyAbsolute: "\u7D76\u5BFE\u30D1\u30B9\u3092\u30B3\u30D4\u30FC",
|
|
25115
|
+
copyRelative: "\u76F8\u5BFE\u30D1\u30B9\u3092\u30B3\u30D4\u30FC",
|
|
25116
|
+
sendToAgent: "Agent \u306B\u9001\u308B",
|
|
25117
|
+
expand: "\u5C55\u958B",
|
|
25118
|
+
collapse: "\u6298\u308A\u305F\u305F\u3080",
|
|
25119
|
+
upload: "\u3053\u306E\u30D5\u30A9\u30EB\u30C0\u306B\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9",
|
|
25120
|
+
open: "\u958B\u304F"
|
|
25121
|
+
},
|
|
25122
|
+
copied: "\u30AF\u30EA\u30C3\u30D7\u30DC\u30FC\u30C9\u306B\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F",
|
|
25123
|
+
copyFailed: "\u30B3\u30D4\u30FC\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25124
|
+
sendToAgent: {
|
|
25125
|
+
prompt: "\u3053\u306E\u30D1\u30B9\u3092\u51E6\u7406\u3057\u3066\u304F\u3060\u3055\u3044\uFF1A`{{path}}`"
|
|
25126
|
+
},
|
|
25127
|
+
upload: {
|
|
25128
|
+
uploading: "{{name}} \u3092\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u4E2D\u2026",
|
|
25129
|
+
success: "{{name}} \u3092\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u3057\u307E\u3057\u305F",
|
|
25130
|
+
fail: "{{name}} \u306E\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
|
|
25131
|
+
},
|
|
25132
|
+
transfer: {
|
|
25133
|
+
legUserToTmex: "\u30D6\u30E9\u30A6\u30B6 \u2192 tmex",
|
|
25134
|
+
legTmexToServer: "tmex \u2192 \u30B5\u30FC\u30D0\u30FC",
|
|
25135
|
+
legServerToTmex: "\u30B5\u30FC\u30D0\u30FC \u2192 tmex",
|
|
25136
|
+
legTmexToUser: "tmex \u2192 \u30D6\u30E9\u30A6\u30B6",
|
|
25137
|
+
cancel: "\u30AD\u30E3\u30F3\u30BB\u30EB",
|
|
25138
|
+
canceled: "{{name}} \u3092\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u307E\u3057\u305F",
|
|
25139
|
+
downloaded: "{{name}} \u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u3057\u307E\u3057\u305F",
|
|
25140
|
+
downloadFailed: "{{name}} \u306E\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25141
|
+
dragDownloadStarted: "{{name}} \u306E\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u3092\u958B\u59CB\uFF08\u30D6\u30E9\u30A6\u30B6\u304C\u51E6\u7406\uFF09",
|
|
25142
|
+
tooLarge: "{{name}} \u306F\u30B5\u30A4\u30BA\u4E0A\u9650\uFF08{{max}}\uFF09\u3092\u8D85\u3048\u3066\u3044\u307E\u3059"
|
|
25143
|
+
},
|
|
25144
|
+
agentLaunch: {
|
|
25145
|
+
connectFailed: "\u30C7\u30D0\u30A4\u30B9\u3078\u306E\u63A5\u7D9A\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25146
|
+
windowFailed: "\u30A6\u30A3\u30F3\u30C9\u30A6\u306E\u4F5C\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
|
|
25147
|
+
},
|
|
24894
25148
|
install: {
|
|
24895
25149
|
button: "rsync \u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB",
|
|
24896
25150
|
scopeLocal: "\u30B5\u30FC\u30D0\u30FC",
|
|
24897
25151
|
scopeRemote: "\u30EA\u30E2\u30FC\u30C8",
|
|
24898
|
-
connectFailed: "\u30C7\u30D0\u30A4\u30B9\u3078\u306E\u63A5\u7D9A\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
24899
|
-
windowFailed: "\u30A6\u30A3\u30F3\u30C9\u30A6\u306E\u4F5C\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
24900
25152
|
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"
|
|
24901
25153
|
}
|
|
24902
25154
|
},
|
|
@@ -24920,6 +25172,11 @@ function toBCP47(locale) {
|
|
|
24920
25172
|
return locale.replace("_", "-");
|
|
24921
25173
|
}
|
|
24922
25174
|
var AVAILABLE_LOCALES = ["en_US", "zh_CN", "ja_JP"];
|
|
25175
|
+
// ../shared/src/version.ts
|
|
25176
|
+
function formatDisplayVersion(baseVersion, isProd) {
|
|
25177
|
+
const base = baseVersion?.trim() || "unknown";
|
|
25178
|
+
return isProd ? base : `${base}_dev`;
|
|
25179
|
+
}
|
|
24923
25180
|
// ../shared/src/ws-borsh/index.ts
|
|
24924
25181
|
var exports_ws_borsh = {};
|
|
24925
25182
|
__export(exports_ws_borsh, {
|
|
@@ -27431,11 +27688,13 @@ var config = {
|
|
|
27431
27688
|
baseUrl: getEnv("TMEX_BASE_URL", "http://127.0.0.1:8085"),
|
|
27432
27689
|
siteNameDefault: getEnv("TMEX_SITE_NAME", "tmex"),
|
|
27433
27690
|
databaseUrl: getEnv("DATABASE_URL", "./tmex.db"),
|
|
27691
|
+
transferMaxBytes: Number.parseInt(getEnv("TMEX_TRANSFER_MAX_BYTES", "2147483648"), 10),
|
|
27434
27692
|
bellThrottleSecondsDefault: Number.parseInt(getEnv("TMEX_BELL_THROTTLE_SECONDS", "6"), 10),
|
|
27435
27693
|
notificationThrottleSecondsDefault: Number.parseInt(getEnv("TMEX_NOTIFICATION_THROTTLE_SECONDS", "3"), 10),
|
|
27436
27694
|
tmuxAllowPassthrough: getBooleanEnv("TMEX_TMUX_ALLOW_PASSTHROUGH", false),
|
|
27437
27695
|
tmuxTermProgram: getEnv("TMEX_TMUX_TERM_PROGRAM", "ghostty"),
|
|
27438
27696
|
tmuxWindowStyle: getEnv("TMEX_TMUX_WINDOW_STYLE", "fg=#d0d0d0,bg=#262626"),
|
|
27697
|
+
tmuxSocket: getEnv("TMEX_TMUX_SOCKET", ""),
|
|
27439
27698
|
sshReconnectMaxRetriesDefault: Number.parseInt(getEnv("TMEX_SSH_RECONNECT_MAX_RETRIES", "2"), 10),
|
|
27440
27699
|
sshReconnectDelaySecondsDefault: Number.parseInt(getEnv("TMEX_SSH_RECONNECT_DELAY_SECONDS", "10"), 10),
|
|
27441
27700
|
languageDefault: getEnv("TMEX_DEFAULT_LANGUAGE", "en_US"),
|
|
@@ -86600,9 +86859,6 @@ function normalizeHttpUrl(input) {
|
|
|
86600
86859
|
return null;
|
|
86601
86860
|
}
|
|
86602
86861
|
}
|
|
86603
|
-
function encodePercentForTelegramUrl(url2) {
|
|
86604
|
-
return url2.replace(/%/g, "%25");
|
|
86605
|
-
}
|
|
86606
86862
|
|
|
86607
86863
|
class EventNotifier {
|
|
86608
86864
|
webhooks = [];
|
|
@@ -86746,8 +87002,7 @@ class EventNotifier {
|
|
|
86746
87002
|
lines.push(...this.buildPaneMetaLines(event));
|
|
86747
87003
|
const paneUrl = normalizeHttpUrl(buildPaneUrl(event));
|
|
86748
87004
|
if (paneUrl) {
|
|
86749
|
-
|
|
86750
|
-
lines.push("", `<a href="${escapeTelegramHtmlAttribute(tgSafePaneUrl)}">${escapeTelegramHtmlText(t2("notification.telegramBell.viewLink"))}</a>`);
|
|
87005
|
+
lines.push("", `<a href="${escapeTelegramHtmlAttribute(paneUrl)}">${escapeTelegramHtmlText(t2("notification.telegramBell.viewLink"))}</a>`);
|
|
86751
87006
|
}
|
|
86752
87007
|
return lines.join(`
|
|
86753
87008
|
`);
|
|
@@ -86767,8 +87022,7 @@ class EventNotifier {
|
|
|
86767
87022
|
const topbarLabel = this.buildTerminalTopbarLabel(event);
|
|
86768
87023
|
const footer = `from ${event.site.name}: ${topbarLabel}`;
|
|
86769
87024
|
if (paneUrl) {
|
|
86770
|
-
|
|
86771
|
-
lines.push("", `<a href="${escapeTelegramHtmlAttribute(tgSafePaneUrl)}">${escapeTelegramHtmlText(footer)}</a>`);
|
|
87025
|
+
lines.push("", `<a href="${escapeTelegramHtmlAttribute(paneUrl)}">${escapeTelegramHtmlText(footer)}</a>`);
|
|
86772
87026
|
} else {
|
|
86773
87027
|
lines.push("", escapeTelegramHtmlText(footer));
|
|
86774
87028
|
}
|
|
@@ -86808,8 +87062,7 @@ class EventNotifier {
|
|
|
86808
87062
|
}
|
|
86809
87063
|
const paneUrl = normalizeHttpUrl(buildPaneUrl(event));
|
|
86810
87064
|
if (paneUrl) {
|
|
86811
|
-
|
|
86812
|
-
lines.push("", `<a href="${escapeTelegramHtmlAttribute(tgSafePaneUrl)}">${esc2(t2("notification.directLink"))}</a>`);
|
|
87065
|
+
lines.push("", `<a href="${escapeTelegramHtmlAttribute(paneUrl)}">${esc2(t2("notification.directLink"))}</a>`);
|
|
86813
87066
|
}
|
|
86814
87067
|
return lines.join(`
|
|
86815
87068
|
`);
|
|
@@ -95730,10 +95983,12 @@ class ConnectionAlertNotifier {
|
|
|
95730
95983
|
const classified = classifySshError(errObj);
|
|
95731
95984
|
const friendlyMessage = t2(classified.messageKey, { ...classified.messageParams });
|
|
95732
95985
|
const rawMessage = errObj.message;
|
|
95986
|
+
const persistedMessage = rawMessage && !friendlyMessage.includes(rawMessage) ? `${friendlyMessage}
|
|
95987
|
+
${rawMessage}` : friendlyMessage;
|
|
95733
95988
|
console.error(`[conn-alert] device ${device.id} (${device.name}) source=${source} type=${classified.type}: ${rawMessage}`);
|
|
95734
95989
|
if (persist) {
|
|
95735
95990
|
try {
|
|
95736
|
-
this.persister(device.id,
|
|
95991
|
+
this.persister(device.id, persistedMessage, classified.type);
|
|
95737
95992
|
} catch (dbErr) {
|
|
95738
95993
|
console.error("[conn-alert] failed to persist runtime status:", dbErr);
|
|
95739
95994
|
}
|
|
@@ -97013,6 +97268,19 @@ function shouldIgnoreReaderAbortError(error51) {
|
|
|
97013
97268
|
const maybeError = error51;
|
|
97014
97269
|
return maybeError.name === "AbortError" && maybeError.code === "ERR_STREAM_RELEASE_LOCK" && typeof maybeError.message === "string" && maybeError.message.includes("releaseLock");
|
|
97015
97270
|
}
|
|
97271
|
+
var TRANSIENT_SPAWN_ERROR_CODES = new Set(["EAGAIN", "EMFILE", "ENFILE", "ENOMEM"]);
|
|
97272
|
+
var TMUX_SPAWN_UNAVAILABLE_EXIT = -2;
|
|
97273
|
+
function isTransientSpawnError(error51) {
|
|
97274
|
+
if (!error51 || typeof error51 !== "object") {
|
|
97275
|
+
return false;
|
|
97276
|
+
}
|
|
97277
|
+
const code2 = error51.code;
|
|
97278
|
+
if (typeof code2 === "string" && TRANSIENT_SPAWN_ERROR_CODES.has(code2)) {
|
|
97279
|
+
return true;
|
|
97280
|
+
}
|
|
97281
|
+
const message = error51.message;
|
|
97282
|
+
return typeof message === "string" && (message.includes("EAGAIN") || message.includes("posix_spawn") || message.includes("resource temporarily unavailable") || message.includes("Too many open files"));
|
|
97283
|
+
}
|
|
97016
97284
|
function defaultRun(argv) {
|
|
97017
97285
|
return new Promise((resolve3, reject) => {
|
|
97018
97286
|
const subprocess = Bun.spawn(argv, {
|
|
@@ -97072,6 +97340,7 @@ class LocalExternalTmuxConnection {
|
|
|
97072
97340
|
controlStartedAt = 0;
|
|
97073
97341
|
controlRestartCount = 0;
|
|
97074
97342
|
controlStderrTail = "";
|
|
97343
|
+
spawnUnavailableNotified = false;
|
|
97075
97344
|
constructor(options, inputDeps = {}) {
|
|
97076
97345
|
this.deviceId = options.deviceId;
|
|
97077
97346
|
this.callbacks = options;
|
|
@@ -97123,7 +97392,13 @@ class LocalExternalTmuxConnection {
|
|
|
97123
97392
|
this.stopControlClient();
|
|
97124
97393
|
}
|
|
97125
97394
|
requestSnapshot() {
|
|
97126
|
-
this.requestSnapshotInternal()
|
|
97395
|
+
this.requestSnapshotInternal().catch((error51) => {
|
|
97396
|
+
if (isTransientSpawnError(error51)) {
|
|
97397
|
+
this.handleSpawnUnavailable(error51 instanceof Error ? error51.message : String(error51));
|
|
97398
|
+
return;
|
|
97399
|
+
}
|
|
97400
|
+
this.callbacks.onError(error51 instanceof Error ? error51 : new Error(String(error51)));
|
|
97401
|
+
});
|
|
97127
97402
|
}
|
|
97128
97403
|
sendInput(paneId, data) {
|
|
97129
97404
|
if (!this.connected) {
|
|
@@ -97423,6 +97698,7 @@ class LocalExternalTmuxConnection {
|
|
|
97423
97698
|
});
|
|
97424
97699
|
const proc = this.deps.spawnControlClient([
|
|
97425
97700
|
"tmux",
|
|
97701
|
+
...config.tmuxSocket ? ["-L", config.tmuxSocket] : [],
|
|
97426
97702
|
"-C",
|
|
97427
97703
|
"attach-session",
|
|
97428
97704
|
"-t",
|
|
@@ -97511,6 +97787,16 @@ class LocalExternalTmuxConnection {
|
|
|
97511
97787
|
return;
|
|
97512
97788
|
}
|
|
97513
97789
|
const probe = await this.runTmuxAllowFailure(["has-session", "-t", this.sessionName]);
|
|
97790
|
+
if (probe.exitCode === TMUX_SPAWN_UNAVAILABLE_EXIT) {
|
|
97791
|
+
this.handleSpawnUnavailable(probe.stderr);
|
|
97792
|
+
this.controlRestartCount = Math.max(0, this.controlRestartCount - 1);
|
|
97793
|
+
if (this.connected && !this.manualDisconnect) {
|
|
97794
|
+
setTimeout(() => {
|
|
97795
|
+
this.reconnectControlClient(exitCode);
|
|
97796
|
+
}, CONTROL_RESTART_DELAY_MS * 4);
|
|
97797
|
+
}
|
|
97798
|
+
return;
|
|
97799
|
+
}
|
|
97514
97800
|
if (probe.exitCode !== 0) {
|
|
97515
97801
|
const message = probe.stderr.trim() || probe.stdout.trim() || "tmux session gone";
|
|
97516
97802
|
console.warn(`[local] tmux session gone on ${this.deviceId}: ${message}`);
|
|
@@ -97622,6 +97908,11 @@ class LocalExternalTmuxConnection {
|
|
|
97622
97908
|
].join(SNAPSHOT_FIELD_SEPARATOR)
|
|
97623
97909
|
])
|
|
97624
97910
|
]);
|
|
97911
|
+
const transientResult = [sessionRes, windowsRes, panesRes].find((res) => res.exitCode === TMUX_SPAWN_UNAVAILABLE_EXIT);
|
|
97912
|
+
if (transientResult) {
|
|
97913
|
+
this.handleSpawnUnavailable(transientResult.stderr);
|
|
97914
|
+
return;
|
|
97915
|
+
}
|
|
97625
97916
|
if (sessionRes.exitCode !== 0 || windowsRes.exitCode !== 0 || panesRes.exitCode !== 0) {
|
|
97626
97917
|
const stderrBlob = `${sessionRes.stderr}
|
|
97627
97918
|
${windowsRes.stderr}
|
|
@@ -97645,6 +97936,7 @@ ${panesRes.stderr}`;
|
|
|
97645
97936
|
this.parseSnapshotPanes(panesRes.stdout.split(/\r?\n/));
|
|
97646
97937
|
this.discardInvalidSnapshot();
|
|
97647
97938
|
this.controlSubscription?.prunePanes(new Set(this.getExpectedPaneIds()));
|
|
97939
|
+
this.markSpawnRecovered();
|
|
97648
97940
|
this.emitSnapshot();
|
|
97649
97941
|
}
|
|
97650
97942
|
parseSnapshotSession(lines) {
|
|
@@ -97830,8 +98122,29 @@ ${panesRes.stderr}`;
|
|
|
97830
98122
|
silentTelegram: true
|
|
97831
98123
|
});
|
|
97832
98124
|
}
|
|
98125
|
+
handleSpawnUnavailable(message) {
|
|
98126
|
+
if (this.spawnUnavailableNotified) {
|
|
98127
|
+
return;
|
|
98128
|
+
}
|
|
98129
|
+
this.spawnUnavailableNotified = true;
|
|
98130
|
+
const detail = (message || "tmux spawn unavailable (process table exhausted)").trim();
|
|
98131
|
+
console.warn(`[local] tmux spawn unavailable on ${this.deviceId} (process pressure), degrading without shutdown: ${detail}`);
|
|
98132
|
+
this.notifyRuntimeError(detail);
|
|
98133
|
+
}
|
|
98134
|
+
markSpawnRecovered() {
|
|
98135
|
+
this.spawnUnavailableNotified = false;
|
|
98136
|
+
}
|
|
97833
98137
|
async runTmuxAllowFailure(argv) {
|
|
97834
|
-
|
|
98138
|
+
const socketArgs = config.tmuxSocket ? ["-L", config.tmuxSocket] : [];
|
|
98139
|
+
try {
|
|
98140
|
+
return await this.deps.run(["tmux", ...socketArgs, ...argv]);
|
|
98141
|
+
} catch (error51) {
|
|
98142
|
+
if (isTransientSpawnError(error51)) {
|
|
98143
|
+
const message = error51 instanceof Error ? error51.message : String(error51);
|
|
98144
|
+
return { exitCode: TMUX_SPAWN_UNAVAILABLE_EXIT, stdout: "", stderr: message };
|
|
98145
|
+
}
|
|
98146
|
+
throw error51;
|
|
98147
|
+
}
|
|
97835
98148
|
}
|
|
97836
98149
|
isTmuxServerGoneMessage(message) {
|
|
97837
98150
|
const normalized = message.toLowerCase();
|
|
@@ -98040,7 +98353,7 @@ function resolveSshConfigRef(device, deps) {
|
|
|
98040
98353
|
return parseSshConfigOutput(result.stdout, deps.env);
|
|
98041
98354
|
}
|
|
98042
98355
|
function resolveImplicitIdentityFilesForAgentAuth(device, host, port, username, deps) {
|
|
98043
|
-
if (device.authMode !== "agent"
|
|
98356
|
+
if (device.authMode !== "agent") {
|
|
98044
98357
|
return [];
|
|
98045
98358
|
}
|
|
98046
98359
|
const target = username ? `${username}@${host}` : host;
|
|
@@ -98062,7 +98375,7 @@ async function resolveSshConnectConfig(device, decrypt2, inputDeps = {}) {
|
|
|
98062
98375
|
readTextFile: inputDeps.readTextFile ?? ((path) => readFileSync2(path, "utf8"))
|
|
98063
98376
|
};
|
|
98064
98377
|
const sshEnv = toSshAuthEnv(deps.env);
|
|
98065
|
-
const resolvedConfig = resolveSshConfigRef(device, deps);
|
|
98378
|
+
const resolvedConfig = device.authMode === "configRef" ? resolveSshConfigRef(device, deps) : null;
|
|
98066
98379
|
const host = resolvedConfig?.host ?? device.host;
|
|
98067
98380
|
const port = resolvedConfig?.port ?? device.port ?? 22;
|
|
98068
98381
|
const username = resolvedConfig?.username ?? resolveSshUsername(device.username, device.authMode, sshEnv);
|
|
@@ -102660,7 +102973,7 @@ function deleteFileRoot(id) {
|
|
|
102660
102973
|
}
|
|
102661
102974
|
|
|
102662
102975
|
// ../../apps/gateway/src/files/device-storage.ts
|
|
102663
|
-
import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync3, realpathSync, rmSync as rmSync2 } from "fs";
|
|
102976
|
+
import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync3, realpathSync, rmSync as rmSync2, statSync } from "fs";
|
|
102664
102977
|
import { tmpdir as tmpdir2 } from "os";
|
|
102665
102978
|
import { join as join5 } from "path";
|
|
102666
102979
|
|
|
@@ -102895,6 +103208,18 @@ function enqueueDeviceJob(deviceId, job) {
|
|
|
102895
103208
|
}
|
|
102896
103209
|
|
|
102897
103210
|
// ../../apps/gateway/src/files/rsync.ts
|
|
103211
|
+
var PROGRESS_RE = /^\s*([\d,]+)\s+(\d+)%\s+([\d.]+[KMGT]?B\/s)/;
|
|
103212
|
+
function parseRsyncProgress(line) {
|
|
103213
|
+
const m = PROGRESS_RE.exec(line);
|
|
103214
|
+
if (!m)
|
|
103215
|
+
return null;
|
|
103216
|
+
const transferred = Number.parseInt(m[1].replace(/,/g, ""), 10);
|
|
103217
|
+
const pct = Number.parseInt(m[2], 10);
|
|
103218
|
+
if (Number.isNaN(transferred) || Number.isNaN(pct))
|
|
103219
|
+
return null;
|
|
103220
|
+
return { transferred, pct, rate: m[3] };
|
|
103221
|
+
}
|
|
103222
|
+
|
|
102898
103223
|
class RsyncMissingLocalError extends Error {
|
|
102899
103224
|
constructor(message) {
|
|
102900
103225
|
super(message);
|
|
@@ -102916,8 +103241,36 @@ function baseSubprocessEnv() {
|
|
|
102916
103241
|
out.LC_ALL = "C";
|
|
102917
103242
|
return out;
|
|
102918
103243
|
}
|
|
103244
|
+
async function readStdoutWithProgress(stream, onProgress, resetIdle) {
|
|
103245
|
+
const reader = stream.getReader();
|
|
103246
|
+
const decoder4 = new TextDecoder;
|
|
103247
|
+
let full = "";
|
|
103248
|
+
let buf = "";
|
|
103249
|
+
for (;; ) {
|
|
103250
|
+
const { done, value } = await reader.read();
|
|
103251
|
+
if (done)
|
|
103252
|
+
break;
|
|
103253
|
+
resetIdle();
|
|
103254
|
+
const text3 = decoder4.decode(value, { stream: true });
|
|
103255
|
+
full += text3;
|
|
103256
|
+
buf += text3;
|
|
103257
|
+
const parts = buf.split(/[\r\n]/);
|
|
103258
|
+
buf = parts.pop() ?? "";
|
|
103259
|
+
for (const line of parts) {
|
|
103260
|
+
const p = parseRsyncProgress(line);
|
|
103261
|
+
if (p)
|
|
103262
|
+
onProgress(p);
|
|
103263
|
+
}
|
|
103264
|
+
}
|
|
103265
|
+
const tail = parseRsyncProgress(buf);
|
|
103266
|
+
if (tail)
|
|
103267
|
+
onProgress(tail);
|
|
103268
|
+
return full;
|
|
103269
|
+
}
|
|
102919
103270
|
async function runRsync(argv, opts = {}) {
|
|
103271
|
+
const streaming = typeof opts.onProgress === "function";
|
|
102920
103272
|
const timeoutMs = opts.timeoutMs ?? 20000;
|
|
103273
|
+
const idleTimeoutMs = opts.idleTimeoutMs ?? 120000;
|
|
102921
103274
|
let proc;
|
|
102922
103275
|
try {
|
|
102923
103276
|
proc = Bun.spawn(["rsync", ...argv], {
|
|
@@ -102930,21 +103283,36 @@ async function runRsync(argv, opts = {}) {
|
|
|
102930
103283
|
throw new RsyncMissingLocalError(error51 instanceof Error ? error51.message : String(error51));
|
|
102931
103284
|
}
|
|
102932
103285
|
let timedOut = false;
|
|
102933
|
-
const
|
|
102934
|
-
timedOut = true;
|
|
102935
|
-
try {
|
|
102936
|
-
proc.kill();
|
|
102937
|
-
} catch {}
|
|
102938
|
-
}, timeoutMs);
|
|
102939
|
-
const onAbort = () => {
|
|
103286
|
+
const killProc = () => {
|
|
102940
103287
|
try {
|
|
102941
103288
|
proc.kill();
|
|
102942
103289
|
} catch {}
|
|
102943
103290
|
};
|
|
103291
|
+
let killTimer;
|
|
103292
|
+
const armFixed = () => {
|
|
103293
|
+
killTimer = setTimeout(() => {
|
|
103294
|
+
timedOut = true;
|
|
103295
|
+
killProc();
|
|
103296
|
+
}, timeoutMs);
|
|
103297
|
+
};
|
|
103298
|
+
const resetIdle = () => {
|
|
103299
|
+
if (killTimer)
|
|
103300
|
+
clearTimeout(killTimer);
|
|
103301
|
+
killTimer = setTimeout(() => {
|
|
103302
|
+
timedOut = true;
|
|
103303
|
+
killProc();
|
|
103304
|
+
}, idleTimeoutMs);
|
|
103305
|
+
};
|
|
103306
|
+
if (streaming)
|
|
103307
|
+
resetIdle();
|
|
103308
|
+
else
|
|
103309
|
+
armFixed();
|
|
103310
|
+
const onAbort = () => killProc();
|
|
102944
103311
|
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
102945
103312
|
try {
|
|
103313
|
+
const stdoutPromise = streaming ? readStdoutWithProgress(proc.stdout, opts.onProgress, resetIdle) : new Response(proc.stdout).text();
|
|
102946
103314
|
const [stdout, stderr, exitCode] = await Promise.all([
|
|
102947
|
-
|
|
103315
|
+
stdoutPromise,
|
|
102948
103316
|
new Response(proc.stderr).text(),
|
|
102949
103317
|
proc.exited
|
|
102950
103318
|
]);
|
|
@@ -102954,7 +103322,8 @@ async function runRsync(argv, opts = {}) {
|
|
|
102954
103322
|
}
|
|
102955
103323
|
return { stdout, stderr, exitCode };
|
|
102956
103324
|
} finally {
|
|
102957
|
-
|
|
103325
|
+
if (killTimer)
|
|
103326
|
+
clearTimeout(killTimer);
|
|
102958
103327
|
opts.signal?.removeEventListener("abort", onAbort);
|
|
102959
103328
|
}
|
|
102960
103329
|
}
|
|
@@ -103136,17 +103505,25 @@ function rsyncListArgs(spec, remotePath) {
|
|
|
103136
103505
|
return args;
|
|
103137
103506
|
}
|
|
103138
103507
|
function rsyncCopyArgs(spec, remotePath, dest) {
|
|
103139
|
-
const args = ["-L"];
|
|
103508
|
+
const args = ["-L", "--progress"];
|
|
103140
103509
|
if (spec.rsh)
|
|
103141
103510
|
args.push("-e", spec.rsh);
|
|
103142
103511
|
args.push(rsyncTargetArg(spec, remotePath), dest);
|
|
103143
103512
|
return args;
|
|
103144
103513
|
}
|
|
103514
|
+
function rsyncUploadArgs(spec, localSource, remoteDest) {
|
|
103515
|
+
const args = ["--progress"];
|
|
103516
|
+
if (spec.rsh)
|
|
103517
|
+
args.push("-e", spec.rsh);
|
|
103518
|
+
args.push(localSource, rsyncTargetArg(spec, remoteDest));
|
|
103519
|
+
return args;
|
|
103520
|
+
}
|
|
103145
103521
|
|
|
103146
103522
|
// ../../apps/gateway/src/files/device-storage.ts
|
|
103147
103523
|
var RAW_MAX_BYTES = 50 * 1024 * 1024;
|
|
103148
103524
|
var LIST_TIMEOUT_MS = 20000;
|
|
103149
103525
|
var COPY_TIMEOUT_MS = 60000;
|
|
103526
|
+
var TRANSFER_IDLE_TIMEOUT_MS = 120000;
|
|
103150
103527
|
function ok(data) {
|
|
103151
103528
|
return { ok: true, data };
|
|
103152
103529
|
}
|
|
@@ -103456,8 +103833,241 @@ async function readRawFile(rootId, inputPath) {
|
|
|
103456
103833
|
}
|
|
103457
103834
|
});
|
|
103458
103835
|
}
|
|
103836
|
+
function sanitizeUploadName(raw) {
|
|
103837
|
+
const base = raw.split("/").pop() ?? "";
|
|
103838
|
+
if (base === "" || base === "." || base === "..")
|
|
103839
|
+
return null;
|
|
103840
|
+
if (base.includes("/") || base.includes("\\") || base.includes("\x00"))
|
|
103841
|
+
return null;
|
|
103842
|
+
return base;
|
|
103843
|
+
}
|
|
103844
|
+
async function pushFileToDevice(rootId, destDir, srcPath, name24, opts = {}) {
|
|
103845
|
+
const safeName = sanitizeUploadName(name24);
|
|
103846
|
+
if (!safeName)
|
|
103847
|
+
return fail("invalid");
|
|
103848
|
+
const r = resolveContext(rootId);
|
|
103849
|
+
if (!r.ok)
|
|
103850
|
+
return fail(r.code);
|
|
103851
|
+
const { root, device } = r.ctx;
|
|
103852
|
+
const norm = checkAndNormalize(device, root.path, destDir);
|
|
103853
|
+
if (!norm.ok)
|
|
103854
|
+
return fail(norm.code);
|
|
103855
|
+
return enqueueDeviceJob(device.id, async () => {
|
|
103856
|
+
let spec;
|
|
103857
|
+
try {
|
|
103858
|
+
spec = await buildRsyncDeviceSpec(device);
|
|
103859
|
+
} catch (error51) {
|
|
103860
|
+
if (error51 instanceof RsyncAuthError)
|
|
103861
|
+
return fail(error51.code, error51.message);
|
|
103862
|
+
throw error51;
|
|
103863
|
+
}
|
|
103864
|
+
try {
|
|
103865
|
+
const destStat = await statViaRsync(spec, norm.path);
|
|
103866
|
+
if (!destStat.ok)
|
|
103867
|
+
return destStat;
|
|
103868
|
+
if (destStat.data.type !== "dir")
|
|
103869
|
+
return fail("not_a_directory");
|
|
103870
|
+
const remoteDest = posixJoin(norm.path, safeName);
|
|
103871
|
+
let res;
|
|
103872
|
+
try {
|
|
103873
|
+
res = await runRsync(rsyncUploadArgs(spec, srcPath, remoteDest), {
|
|
103874
|
+
env: spec.env,
|
|
103875
|
+
onProgress: opts.onProgress,
|
|
103876
|
+
idleTimeoutMs: TRANSFER_IDLE_TIMEOUT_MS,
|
|
103877
|
+
signal: opts.signal
|
|
103878
|
+
});
|
|
103879
|
+
} catch (error51) {
|
|
103880
|
+
if (error51 instanceof RsyncMissingLocalError)
|
|
103881
|
+
return fail("rsync_missing_local");
|
|
103882
|
+
throw error51;
|
|
103883
|
+
}
|
|
103884
|
+
if (res.exitCode !== 0)
|
|
103885
|
+
return fail(classifyRsyncFailure(res.exitCode, res.stderr), res.stderr);
|
|
103886
|
+
return ok({ uploaded: safeName });
|
|
103887
|
+
} finally {
|
|
103888
|
+
spec.cleanup();
|
|
103889
|
+
}
|
|
103890
|
+
});
|
|
103891
|
+
}
|
|
103892
|
+
async function pullFileFromDevice(rootId, inputPath, opts = {}) {
|
|
103893
|
+
const r = resolveContext(rootId);
|
|
103894
|
+
if (!r.ok)
|
|
103895
|
+
return fail(r.code);
|
|
103896
|
+
const { root, device } = r.ctx;
|
|
103897
|
+
const norm = checkAndNormalize(device, root.path, inputPath);
|
|
103898
|
+
if (!norm.ok)
|
|
103899
|
+
return fail(norm.code);
|
|
103900
|
+
return enqueueDeviceJob(device.id, async () => {
|
|
103901
|
+
let spec;
|
|
103902
|
+
try {
|
|
103903
|
+
spec = await buildRsyncDeviceSpec(device);
|
|
103904
|
+
} catch (error51) {
|
|
103905
|
+
if (error51 instanceof RsyncAuthError)
|
|
103906
|
+
return fail(error51.code, error51.message);
|
|
103907
|
+
throw error51;
|
|
103908
|
+
}
|
|
103909
|
+
try {
|
|
103910
|
+
const st = await statViaRsync(spec, norm.path);
|
|
103911
|
+
if (!st.ok)
|
|
103912
|
+
return st;
|
|
103913
|
+
if (st.data.type === "dir")
|
|
103914
|
+
return fail("is_directory");
|
|
103915
|
+
if (st.data.size != null && st.data.size > config.transferMaxBytes)
|
|
103916
|
+
return fail("too_large");
|
|
103917
|
+
const dir2 = mkdtempSync2(join5(tmpdir2(), "tmex-dl-"));
|
|
103918
|
+
const dest = join5(dir2, "f");
|
|
103919
|
+
const cleanup = () => {
|
|
103920
|
+
try {
|
|
103921
|
+
rmSync2(dir2, { recursive: true, force: true });
|
|
103922
|
+
} catch {}
|
|
103923
|
+
};
|
|
103924
|
+
let res;
|
|
103925
|
+
try {
|
|
103926
|
+
res = await runRsync(rsyncCopyArgs(spec, norm.path, dest), {
|
|
103927
|
+
env: spec.env,
|
|
103928
|
+
onProgress: opts.onProgress,
|
|
103929
|
+
idleTimeoutMs: TRANSFER_IDLE_TIMEOUT_MS,
|
|
103930
|
+
signal: opts.signal
|
|
103931
|
+
});
|
|
103932
|
+
} catch (error51) {
|
|
103933
|
+
cleanup();
|
|
103934
|
+
if (error51 instanceof RsyncMissingLocalError)
|
|
103935
|
+
return fail("rsync_missing_local");
|
|
103936
|
+
throw error51;
|
|
103937
|
+
}
|
|
103938
|
+
if (res.exitCode !== 0) {
|
|
103939
|
+
cleanup();
|
|
103940
|
+
return fail(classifyRsyncFailure(res.exitCode, res.stderr), res.stderr);
|
|
103941
|
+
}
|
|
103942
|
+
const name24 = posixBasename(norm.path);
|
|
103943
|
+
let size = st.data.size ?? 0;
|
|
103944
|
+
try {
|
|
103945
|
+
size = statSync(dest).size;
|
|
103946
|
+
} catch {}
|
|
103947
|
+
if (size > config.transferMaxBytes) {
|
|
103948
|
+
cleanup();
|
|
103949
|
+
return fail("too_large");
|
|
103950
|
+
}
|
|
103951
|
+
return ok({ tmpPath: dest, size, name: name24, mime: mimeOf(name24), cleanup });
|
|
103952
|
+
} finally {
|
|
103953
|
+
spec.cleanup();
|
|
103954
|
+
}
|
|
103955
|
+
});
|
|
103956
|
+
}
|
|
103957
|
+
|
|
103958
|
+
// ../../apps/gateway/src/files/transfer-session.ts
|
|
103959
|
+
import { appendFileSync, mkdtempSync as mkdtempSync3, readdirSync, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
103960
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
103961
|
+
import { join as join6 } from "path";
|
|
103962
|
+
var sessions = new Map;
|
|
103963
|
+
var SESSION_TTL_MS = 30 * 60000;
|
|
103964
|
+
var downloads = new Map;
|
|
103965
|
+
function sweepStale(now2) {
|
|
103966
|
+
for (const [id, s] of sessions) {
|
|
103967
|
+
if (!s.committing && now2 - s.createdAt > SESSION_TTL_MS) {
|
|
103968
|
+
removeUploadSession(id);
|
|
103969
|
+
}
|
|
103970
|
+
}
|
|
103971
|
+
for (const [id, d] of downloads) {
|
|
103972
|
+
if (now2 - d.createdAt > SESSION_TTL_MS) {
|
|
103973
|
+
removeDownloadSession(id);
|
|
103974
|
+
}
|
|
103975
|
+
}
|
|
103976
|
+
}
|
|
103977
|
+
function createDownloadSession(data) {
|
|
103978
|
+
sweepStale(Date.now());
|
|
103979
|
+
const session = { id: crypto.randomUUID(), createdAt: Date.now(), ...data };
|
|
103980
|
+
downloads.set(session.id, session);
|
|
103981
|
+
return session;
|
|
103982
|
+
}
|
|
103983
|
+
function getDownloadSession(id) {
|
|
103984
|
+
return downloads.get(id);
|
|
103985
|
+
}
|
|
103986
|
+
function removeDownloadSession(id) {
|
|
103987
|
+
const d = downloads.get(id);
|
|
103988
|
+
if (!d)
|
|
103989
|
+
return;
|
|
103990
|
+
downloads.delete(id);
|
|
103991
|
+
try {
|
|
103992
|
+
d.cleanup();
|
|
103993
|
+
} catch {}
|
|
103994
|
+
}
|
|
103995
|
+
function createUploadSession(args) {
|
|
103996
|
+
const now2 = Date.now();
|
|
103997
|
+
sweepStale(now2);
|
|
103998
|
+
const tmpDir = mkdtempSync3(join6(tmpdir3(), "tmex-up-"));
|
|
103999
|
+
const tmpPath = join6(tmpDir, "f");
|
|
104000
|
+
writeFileSync2(tmpPath, new Uint8Array(0));
|
|
104001
|
+
const session = {
|
|
104002
|
+
id: crypto.randomUUID(),
|
|
104003
|
+
rootId: args.rootId,
|
|
104004
|
+
destDir: args.destDir,
|
|
104005
|
+
name: args.name,
|
|
104006
|
+
size: args.size,
|
|
104007
|
+
received: 0,
|
|
104008
|
+
tmpDir,
|
|
104009
|
+
tmpPath,
|
|
104010
|
+
abort: new AbortController,
|
|
104011
|
+
createdAt: now2,
|
|
104012
|
+
committing: false
|
|
104013
|
+
};
|
|
104014
|
+
sessions.set(session.id, session);
|
|
104015
|
+
return session;
|
|
104016
|
+
}
|
|
104017
|
+
function getUploadSession(id) {
|
|
104018
|
+
return sessions.get(id);
|
|
104019
|
+
}
|
|
104020
|
+
function appendUploadChunk(id, offset, bytes) {
|
|
104021
|
+
const s = sessions.get(id);
|
|
104022
|
+
if (!s)
|
|
104023
|
+
return { ok: false, reason: "not_found" };
|
|
104024
|
+
if (offset !== s.received)
|
|
104025
|
+
return { ok: false, reason: "bad_offset" };
|
|
104026
|
+
if (s.received + bytes.byteLength > s.size)
|
|
104027
|
+
return { ok: false, reason: "too_large" };
|
|
104028
|
+
appendFileSync(s.tmpPath, bytes);
|
|
104029
|
+
s.received += bytes.byteLength;
|
|
104030
|
+
return { ok: true, received: s.received };
|
|
104031
|
+
}
|
|
104032
|
+
function removeUploadSession(id) {
|
|
104033
|
+
const s = sessions.get(id);
|
|
104034
|
+
if (!s)
|
|
104035
|
+
return;
|
|
104036
|
+
sessions.delete(id);
|
|
104037
|
+
try {
|
|
104038
|
+
s.abort.abort();
|
|
104039
|
+
} catch {}
|
|
104040
|
+
try {
|
|
104041
|
+
rmSync3(s.tmpDir, { recursive: true, force: true });
|
|
104042
|
+
} catch {}
|
|
104043
|
+
}
|
|
104044
|
+
var transferGcTimer = setInterval(() => sweepStale(Date.now()), 5 * 60000);
|
|
104045
|
+
transferGcTimer.unref?.();
|
|
104046
|
+
var ORPHAN_PREFIXES = ["tmex-up-", "tmex-dl-"];
|
|
104047
|
+
var ORPHAN_MAX_AGE_MS = 60 * 60000;
|
|
104048
|
+
function sweepOrphanTransferTemps() {
|
|
104049
|
+
const base = tmpdir3();
|
|
104050
|
+
let entries;
|
|
104051
|
+
try {
|
|
104052
|
+
entries = readdirSync(base);
|
|
104053
|
+
} catch {
|
|
104054
|
+
return;
|
|
104055
|
+
}
|
|
104056
|
+
const now2 = Date.now();
|
|
104057
|
+
for (const name24 of entries) {
|
|
104058
|
+
if (!ORPHAN_PREFIXES.some((p) => name24.startsWith(p)))
|
|
104059
|
+
continue;
|
|
104060
|
+
const full = join6(base, name24);
|
|
104061
|
+
try {
|
|
104062
|
+
if (now2 - statSync2(full).mtimeMs > ORPHAN_MAX_AGE_MS) {
|
|
104063
|
+
rmSync3(full, { recursive: true, force: true });
|
|
104064
|
+
}
|
|
104065
|
+
} catch {}
|
|
104066
|
+
}
|
|
104067
|
+
}
|
|
103459
104068
|
|
|
103460
104069
|
// ../../apps/gateway/src/api/files.ts
|
|
104070
|
+
var UPLOAD_CHUNK_SIZE = 8 * 1024 * 1024;
|
|
103461
104071
|
function json4(data, status = 200, headers = {}) {
|
|
103462
104072
|
return new Response(JSON.stringify(data), {
|
|
103463
104073
|
status,
|
|
@@ -103621,6 +104231,223 @@ async function handleRaw(url2) {
|
|
|
103621
104231
|
}
|
|
103622
104232
|
return new Response(result.data.data, { status: 200, headers });
|
|
103623
104233
|
}
|
|
104234
|
+
async function handleUploadInit(req) {
|
|
104235
|
+
let body;
|
|
104236
|
+
try {
|
|
104237
|
+
body = await req.json();
|
|
104238
|
+
} catch {
|
|
104239
|
+
return json4({ error: t2("apiError.invalidRequest") }, 400);
|
|
104240
|
+
}
|
|
104241
|
+
const rootId = typeof body.rootId === "string" ? body.rootId : "";
|
|
104242
|
+
const destDir = typeof body.path === "string" ? body.path : "";
|
|
104243
|
+
const rawName = typeof body.name === "string" ? body.name : "";
|
|
104244
|
+
const size = typeof body.size === "number" && Number.isFinite(body.size) ? body.size : -1;
|
|
104245
|
+
if (!rootId || !destDir || !rawName || size < 0) {
|
|
104246
|
+
return json4({ error: t2("apiError.invalidRequest") }, 400);
|
|
104247
|
+
}
|
|
104248
|
+
const name24 = sanitizeUploadName(rawName);
|
|
104249
|
+
if (!name24)
|
|
104250
|
+
return codeError("invalid");
|
|
104251
|
+
if (size > config.transferMaxBytes)
|
|
104252
|
+
return codeError("too_large");
|
|
104253
|
+
const stat = await statFile(rootId, destDir);
|
|
104254
|
+
if (!stat.ok)
|
|
104255
|
+
return codeError(stat.code, stat.detail);
|
|
104256
|
+
if (stat.data.type !== "dir")
|
|
104257
|
+
return codeError("not_a_directory");
|
|
104258
|
+
const session = createUploadSession({ rootId, destDir, name: name24, size });
|
|
104259
|
+
return json4({ uploadId: session.id, chunkSize: UPLOAD_CHUNK_SIZE });
|
|
104260
|
+
}
|
|
104261
|
+
async function handleUploadChunk(req, id, url2) {
|
|
104262
|
+
const offset = Number.parseInt(url2.searchParams.get("offset") ?? "", 10);
|
|
104263
|
+
if (Number.isNaN(offset) || offset < 0)
|
|
104264
|
+
return json4({ error: t2("apiError.invalidRequest") }, 400);
|
|
104265
|
+
const bytes = new Uint8Array(await req.arrayBuffer());
|
|
104266
|
+
const res = appendUploadChunk(id, offset, bytes);
|
|
104267
|
+
if (!res.ok) {
|
|
104268
|
+
if (res.reason === "not_found")
|
|
104269
|
+
return codeError("not_found");
|
|
104270
|
+
if (res.reason === "too_large")
|
|
104271
|
+
return codeError("too_large");
|
|
104272
|
+
return json4({ error: t2("apiError.invalidRequest") }, 409);
|
|
104273
|
+
}
|
|
104274
|
+
return json4({ received: res.received });
|
|
104275
|
+
}
|
|
104276
|
+
function handleUploadCommit(id) {
|
|
104277
|
+
const session = getUploadSession(id);
|
|
104278
|
+
if (!session)
|
|
104279
|
+
return codeError("not_found");
|
|
104280
|
+
if (session.received !== session.size)
|
|
104281
|
+
return codeError("invalid", "incomplete upload");
|
|
104282
|
+
session.committing = true;
|
|
104283
|
+
const encoder3 = new TextEncoder;
|
|
104284
|
+
const stream = new ReadableStream({
|
|
104285
|
+
start(controller) {
|
|
104286
|
+
const emit = (obj) => {
|
|
104287
|
+
try {
|
|
104288
|
+
controller.enqueue(encoder3.encode(`${JSON.stringify(obj)}
|
|
104289
|
+
`));
|
|
104290
|
+
} catch {}
|
|
104291
|
+
};
|
|
104292
|
+
pushFileToDevice(session.rootId, session.destDir, session.tmpPath, session.name, {
|
|
104293
|
+
signal: session.abort.signal,
|
|
104294
|
+
onProgress: (p) => emit({ type: "progress", ...p })
|
|
104295
|
+
}).then((res) => {
|
|
104296
|
+
if (res.ok)
|
|
104297
|
+
emit({ type: "done", uploaded: res.data.uploaded });
|
|
104298
|
+
else
|
|
104299
|
+
emit({ type: "error", code: res.code, detail: res.detail });
|
|
104300
|
+
}).catch((e) => emit({ type: "error", code: "unknown", detail: String(e) })).finally(() => {
|
|
104301
|
+
try {
|
|
104302
|
+
controller.close();
|
|
104303
|
+
} catch {}
|
|
104304
|
+
removeUploadSession(id);
|
|
104305
|
+
});
|
|
104306
|
+
},
|
|
104307
|
+
cancel() {
|
|
104308
|
+
removeUploadSession(id);
|
|
104309
|
+
}
|
|
104310
|
+
});
|
|
104311
|
+
return new Response(stream, {
|
|
104312
|
+
status: 200,
|
|
104313
|
+
headers: { "Content-Type": "application/x-ndjson; charset=utf-8", "Cache-Control": "no-store" }
|
|
104314
|
+
});
|
|
104315
|
+
}
|
|
104316
|
+
function handleUploadCancel(id) {
|
|
104317
|
+
removeUploadSession(id);
|
|
104318
|
+
return json4({ success: true });
|
|
104319
|
+
}
|
|
104320
|
+
function attachmentHeaders(name24, mime, size) {
|
|
104321
|
+
const encoded = encodeURIComponent(name24);
|
|
104322
|
+
const ascii = name24.replace(/["\\\r\n]/g, "_");
|
|
104323
|
+
return {
|
|
104324
|
+
"Content-Type": mime ?? "application/octet-stream",
|
|
104325
|
+
"Content-Length": String(size),
|
|
104326
|
+
"Content-Disposition": `attachment; filename="${ascii}"; filename*=UTF-8''${encoded}`,
|
|
104327
|
+
"Cache-Control": "no-store"
|
|
104328
|
+
};
|
|
104329
|
+
}
|
|
104330
|
+
function streamTempFile(tmpPath, cleanupAfter) {
|
|
104331
|
+
let reader;
|
|
104332
|
+
try {
|
|
104333
|
+
reader = Bun.file(tmpPath).stream().getReader();
|
|
104334
|
+
} catch {
|
|
104335
|
+
cleanupAfter();
|
|
104336
|
+
return null;
|
|
104337
|
+
}
|
|
104338
|
+
return new ReadableStream({
|
|
104339
|
+
async pull(controller) {
|
|
104340
|
+
try {
|
|
104341
|
+
const { done, value } = await reader.read();
|
|
104342
|
+
if (done) {
|
|
104343
|
+
controller.close();
|
|
104344
|
+
cleanupAfter();
|
|
104345
|
+
return;
|
|
104346
|
+
}
|
|
104347
|
+
controller.enqueue(value);
|
|
104348
|
+
} catch (e) {
|
|
104349
|
+
controller.error(e);
|
|
104350
|
+
cleanupAfter();
|
|
104351
|
+
}
|
|
104352
|
+
},
|
|
104353
|
+
cancel() {
|
|
104354
|
+
reader.cancel();
|
|
104355
|
+
cleanupAfter();
|
|
104356
|
+
}
|
|
104357
|
+
});
|
|
104358
|
+
}
|
|
104359
|
+
function handleDownloadPrepare(req) {
|
|
104360
|
+
let abort = null;
|
|
104361
|
+
const encoder3 = new TextEncoder;
|
|
104362
|
+
const stream = new ReadableStream({
|
|
104363
|
+
async start(controller) {
|
|
104364
|
+
const emit = (obj) => {
|
|
104365
|
+
try {
|
|
104366
|
+
controller.enqueue(encoder3.encode(`${JSON.stringify(obj)}
|
|
104367
|
+
`));
|
|
104368
|
+
} catch {}
|
|
104369
|
+
};
|
|
104370
|
+
const close = () => {
|
|
104371
|
+
try {
|
|
104372
|
+
controller.close();
|
|
104373
|
+
} catch {}
|
|
104374
|
+
};
|
|
104375
|
+
let body;
|
|
104376
|
+
try {
|
|
104377
|
+
body = await req.json();
|
|
104378
|
+
} catch {
|
|
104379
|
+
emit({ type: "error", code: "invalid" });
|
|
104380
|
+
close();
|
|
104381
|
+
return;
|
|
104382
|
+
}
|
|
104383
|
+
const rootId = typeof body.rootId === "string" ? body.rootId : "";
|
|
104384
|
+
const path = typeof body.path === "string" ? body.path : "";
|
|
104385
|
+
if (!rootId || !path) {
|
|
104386
|
+
emit({ type: "error", code: "invalid" });
|
|
104387
|
+
close();
|
|
104388
|
+
return;
|
|
104389
|
+
}
|
|
104390
|
+
abort = new AbortController;
|
|
104391
|
+
const result = await pullFileFromDevice(rootId, path, {
|
|
104392
|
+
signal: abort.signal,
|
|
104393
|
+
onProgress: (p) => emit({ type: "progress", ...p })
|
|
104394
|
+
});
|
|
104395
|
+
if (result.ok) {
|
|
104396
|
+
const s = createDownloadSession({
|
|
104397
|
+
tmpPath: result.data.tmpPath,
|
|
104398
|
+
size: result.data.size,
|
|
104399
|
+
name: result.data.name,
|
|
104400
|
+
mime: result.data.mime,
|
|
104401
|
+
cleanup: result.data.cleanup
|
|
104402
|
+
});
|
|
104403
|
+
emit({ type: "done", downloadId: s.id, size: s.size, name: s.name });
|
|
104404
|
+
} else {
|
|
104405
|
+
emit({ type: "error", code: result.code, detail: result.detail });
|
|
104406
|
+
}
|
|
104407
|
+
close();
|
|
104408
|
+
},
|
|
104409
|
+
cancel() {
|
|
104410
|
+
abort?.abort();
|
|
104411
|
+
}
|
|
104412
|
+
});
|
|
104413
|
+
return new Response(stream, {
|
|
104414
|
+
status: 200,
|
|
104415
|
+
headers: { "Content-Type": "application/x-ndjson; charset=utf-8", "Cache-Control": "no-store" }
|
|
104416
|
+
});
|
|
104417
|
+
}
|
|
104418
|
+
function handleDownloadContent(id) {
|
|
104419
|
+
const session = getDownloadSession(id);
|
|
104420
|
+
if (!session)
|
|
104421
|
+
return codeError("not_found");
|
|
104422
|
+
const body = streamTempFile(session.tmpPath, () => removeDownloadSession(id));
|
|
104423
|
+
if (!body)
|
|
104424
|
+
return codeError("unknown");
|
|
104425
|
+
return new Response(body, {
|
|
104426
|
+
status: 200,
|
|
104427
|
+
headers: attachmentHeaders(session.name, session.mime, session.size)
|
|
104428
|
+
});
|
|
104429
|
+
}
|
|
104430
|
+
function handleDownloadCancel(id) {
|
|
104431
|
+
removeDownloadSession(id);
|
|
104432
|
+
return json4({ success: true });
|
|
104433
|
+
}
|
|
104434
|
+
async function handleDownload(req, url2) {
|
|
104435
|
+
const rootId = url2.searchParams.get("rootId");
|
|
104436
|
+
const path = url2.searchParams.get("path");
|
|
104437
|
+
if (!rootId || !path)
|
|
104438
|
+
return json4({ error: t2("apiError.invalidRequest") }, 400);
|
|
104439
|
+
const result = await pullFileFromDevice(rootId, path, {
|
|
104440
|
+
signal: req.signal,
|
|
104441
|
+
onProgress: () => {}
|
|
104442
|
+
});
|
|
104443
|
+
if (!result.ok)
|
|
104444
|
+
return codeError(result.code, result.detail);
|
|
104445
|
+
const { tmpPath, size, name: name24, mime, cleanup } = result.data;
|
|
104446
|
+
const body = streamTempFile(tmpPath, cleanup);
|
|
104447
|
+
if (!body)
|
|
104448
|
+
return codeError("unknown");
|
|
104449
|
+
return new Response(body, { status: 200, headers: attachmentHeaders(name24, mime, size) });
|
|
104450
|
+
}
|
|
103624
104451
|
function handleFilesApiRequest(req, path) {
|
|
103625
104452
|
const url2 = new URL(req.url);
|
|
103626
104453
|
if (path === "/api/files/roots" && req.method === "GET")
|
|
@@ -103641,6 +104468,32 @@ function handleFilesApiRequest(req, path) {
|
|
|
103641
104468
|
return handleStat(url2);
|
|
103642
104469
|
if (path === "/api/files/raw" && req.method === "GET")
|
|
103643
104470
|
return handleRaw(url2);
|
|
104471
|
+
if (path === "/api/files/download" && req.method === "GET")
|
|
104472
|
+
return handleDownload(req, url2);
|
|
104473
|
+
if (path === "/api/files/download/prepare" && req.method === "POST") {
|
|
104474
|
+
return handleDownloadPrepare(req);
|
|
104475
|
+
}
|
|
104476
|
+
const dlContentMatch = path.match(/^\/api\/files\/download\/([^/]+)\/content$/);
|
|
104477
|
+
if (dlContentMatch && req.method === "GET") {
|
|
104478
|
+
return handleDownloadContent(decodeURIComponent(dlContentMatch[1]));
|
|
104479
|
+
}
|
|
104480
|
+
const dlCancelMatch = path.match(/^\/api\/files\/download\/([^/]+)$/);
|
|
104481
|
+
if (dlCancelMatch && req.method === "DELETE") {
|
|
104482
|
+
return handleDownloadCancel(decodeURIComponent(dlCancelMatch[1]));
|
|
104483
|
+
}
|
|
104484
|
+
if (path === "/api/files/upload/init" && req.method === "POST")
|
|
104485
|
+
return handleUploadInit(req);
|
|
104486
|
+
const commitMatch = path.match(/^\/api\/files\/upload\/([^/]+)\/commit$/);
|
|
104487
|
+
if (commitMatch && req.method === "POST") {
|
|
104488
|
+
return handleUploadCommit(decodeURIComponent(commitMatch[1]));
|
|
104489
|
+
}
|
|
104490
|
+
const uploadMatch = path.match(/^\/api\/files\/upload\/([^/]+)$/);
|
|
104491
|
+
if (uploadMatch && req.method === "PUT") {
|
|
104492
|
+
return handleUploadChunk(req, decodeURIComponent(uploadMatch[1]), url2);
|
|
104493
|
+
}
|
|
104494
|
+
if (uploadMatch && req.method === "DELETE") {
|
|
104495
|
+
return handleUploadCancel(decodeURIComponent(uploadMatch[1]));
|
|
104496
|
+
}
|
|
103644
104497
|
return;
|
|
103645
104498
|
}
|
|
103646
104499
|
|
|
@@ -103919,6 +104772,345 @@ function json5(data, status = 200) {
|
|
|
103919
104772
|
});
|
|
103920
104773
|
}
|
|
103921
104774
|
|
|
104775
|
+
// ../../apps/gateway/src/system/install-info.ts
|
|
104776
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
104777
|
+
import { resolve as resolve3 } from "path";
|
|
104778
|
+
function resolveInstallDir() {
|
|
104779
|
+
const feDist = process.env.TMEX_FE_DIST_DIR;
|
|
104780
|
+
if (feDist) {
|
|
104781
|
+
return resolve3(feDist, "..", "..");
|
|
104782
|
+
}
|
|
104783
|
+
return process.cwd();
|
|
104784
|
+
}
|
|
104785
|
+
function readInstallMeta() {
|
|
104786
|
+
const metaPath = resolve3(resolveInstallDir(), "install-meta.json");
|
|
104787
|
+
try {
|
|
104788
|
+
if (!existsSync4(metaPath))
|
|
104789
|
+
return null;
|
|
104790
|
+
return JSON.parse(readFileSync4(metaPath, "utf8"));
|
|
104791
|
+
} catch {
|
|
104792
|
+
return null;
|
|
104793
|
+
}
|
|
104794
|
+
}
|
|
104795
|
+
function deploymentFromPlatform(platform) {
|
|
104796
|
+
if (platform === "darwin")
|
|
104797
|
+
return "launchd";
|
|
104798
|
+
if (platform === "linux")
|
|
104799
|
+
return "systemd";
|
|
104800
|
+
return "none";
|
|
104801
|
+
}
|
|
104802
|
+
function getInstallInfo() {
|
|
104803
|
+
if (!config.isProd) {
|
|
104804
|
+
return {
|
|
104805
|
+
installedViaCli: false,
|
|
104806
|
+
deployment: "none",
|
|
104807
|
+
installDir: null,
|
|
104808
|
+
serviceName: null,
|
|
104809
|
+
cliVersion: null
|
|
104810
|
+
};
|
|
104811
|
+
}
|
|
104812
|
+
const meta3 = readInstallMeta();
|
|
104813
|
+
if (!meta3) {
|
|
104814
|
+
return {
|
|
104815
|
+
installedViaCli: false,
|
|
104816
|
+
deployment: "none",
|
|
104817
|
+
installDir: resolveInstallDir(),
|
|
104818
|
+
serviceName: null,
|
|
104819
|
+
cliVersion: null
|
|
104820
|
+
};
|
|
104821
|
+
}
|
|
104822
|
+
return {
|
|
104823
|
+
installedViaCli: true,
|
|
104824
|
+
deployment: deploymentFromPlatform(meta3.platform ?? process.platform),
|
|
104825
|
+
installDir: meta3.installDir ?? resolveInstallDir(),
|
|
104826
|
+
serviceName: meta3.serviceName ?? null,
|
|
104827
|
+
cliVersion: meta3.cliVersion ?? null
|
|
104828
|
+
};
|
|
104829
|
+
}
|
|
104830
|
+
|
|
104831
|
+
// ../../apps/gateway/src/system/version.ts
|
|
104832
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
104833
|
+
import { resolve as resolve4 } from "path";
|
|
104834
|
+
var cachedBase;
|
|
104835
|
+
function readRepoPackageVersion() {
|
|
104836
|
+
const dir2 = import.meta.dir;
|
|
104837
|
+
if (!dir2)
|
|
104838
|
+
return null;
|
|
104839
|
+
const path = resolve4(dir2, "../../../../packages/app/package.json");
|
|
104840
|
+
try {
|
|
104841
|
+
if (!existsSync5(path))
|
|
104842
|
+
return null;
|
|
104843
|
+
const pkg = JSON.parse(readFileSync5(path, "utf8"));
|
|
104844
|
+
return pkg.version ?? null;
|
|
104845
|
+
} catch {
|
|
104846
|
+
return null;
|
|
104847
|
+
}
|
|
104848
|
+
}
|
|
104849
|
+
function getBaseVersion() {
|
|
104850
|
+
if (cachedBase !== undefined)
|
|
104851
|
+
return cachedBase;
|
|
104852
|
+
let base = null;
|
|
104853
|
+
if ("0.12.0") {
|
|
104854
|
+
base = "0.12.0";
|
|
104855
|
+
}
|
|
104856
|
+
if (!base && config.isProd) {
|
|
104857
|
+
base = readInstallMeta()?.cliVersion ?? null;
|
|
104858
|
+
}
|
|
104859
|
+
if (!base) {
|
|
104860
|
+
base = readRepoPackageVersion();
|
|
104861
|
+
}
|
|
104862
|
+
cachedBase = base ?? "unknown";
|
|
104863
|
+
return cachedBase;
|
|
104864
|
+
}
|
|
104865
|
+
function getDisplayVersion() {
|
|
104866
|
+
return formatDisplayVersion(getBaseVersion(), config.isProd);
|
|
104867
|
+
}
|
|
104868
|
+
// ../../apps/gateway/src/system/semver.ts
|
|
104869
|
+
function parse5(input) {
|
|
104870
|
+
const match = input.trim().match(/^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/);
|
|
104871
|
+
if (!match)
|
|
104872
|
+
return null;
|
|
104873
|
+
return {
|
|
104874
|
+
major: Number(match[1]),
|
|
104875
|
+
minor: Number(match[2]),
|
|
104876
|
+
patch: Number(match[3]),
|
|
104877
|
+
prerelease: match[4] ?? null
|
|
104878
|
+
};
|
|
104879
|
+
}
|
|
104880
|
+
function compareVersions(a, b3) {
|
|
104881
|
+
const pa = parse5(a);
|
|
104882
|
+
const pb = parse5(b3);
|
|
104883
|
+
if (!pa || !pb)
|
|
104884
|
+
return 0;
|
|
104885
|
+
if (pa.major !== pb.major)
|
|
104886
|
+
return pa.major > pb.major ? 1 : -1;
|
|
104887
|
+
if (pa.minor !== pb.minor)
|
|
104888
|
+
return pa.minor > pb.minor ? 1 : -1;
|
|
104889
|
+
if (pa.patch !== pb.patch)
|
|
104890
|
+
return pa.patch > pb.patch ? 1 : -1;
|
|
104891
|
+
if (pa.prerelease === pb.prerelease)
|
|
104892
|
+
return 0;
|
|
104893
|
+
if (pa.prerelease === null)
|
|
104894
|
+
return 1;
|
|
104895
|
+
if (pb.prerelease === null)
|
|
104896
|
+
return -1;
|
|
104897
|
+
return pa.prerelease > pb.prerelease ? 1 : pa.prerelease < pb.prerelease ? -1 : 0;
|
|
104898
|
+
}
|
|
104899
|
+
|
|
104900
|
+
// ../../apps/gateway/src/system/update-check.ts
|
|
104901
|
+
var REGISTRY_URL = "https://registry.npmjs.org/tmex-cli";
|
|
104902
|
+
var changelogCdnUrl = (version3) => `https://cdn.jsdelivr.net/npm/tmex-cli@${version3}/CHANGELOG.md`;
|
|
104903
|
+
var FETCH_TIMEOUT_MS = 1e4;
|
|
104904
|
+
async function checkForUpdate() {
|
|
104905
|
+
const current = getBaseVersion();
|
|
104906
|
+
const res = await fetch(REGISTRY_URL, {
|
|
104907
|
+
cache: "no-store",
|
|
104908
|
+
headers: { accept: "application/json", "cache-control": "no-cache" },
|
|
104909
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
104910
|
+
});
|
|
104911
|
+
if (!res.ok) {
|
|
104912
|
+
throw new Error(`npm registry HTTP ${res.status}`);
|
|
104913
|
+
}
|
|
104914
|
+
const packument = await res.json();
|
|
104915
|
+
const latest = packument["dist-tags"]?.latest ?? null;
|
|
104916
|
+
const publishedAt = latest ? packument.time?.[latest] ?? null : null;
|
|
104917
|
+
const hasUpdate = latest !== null && current !== "unknown" && compareVersions(latest, current) > 0;
|
|
104918
|
+
const changelog = latest ? await fetchChangelog(latest) : null;
|
|
104919
|
+
return {
|
|
104920
|
+
currentVersion: current,
|
|
104921
|
+
latestVersion: latest,
|
|
104922
|
+
hasUpdate,
|
|
104923
|
+
changelog,
|
|
104924
|
+
publishedAt
|
|
104925
|
+
};
|
|
104926
|
+
}
|
|
104927
|
+
async function fetchChangelog(version3) {
|
|
104928
|
+
try {
|
|
104929
|
+
const res = await fetch(changelogCdnUrl(version3), {
|
|
104930
|
+
cache: "no-store",
|
|
104931
|
+
signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
|
|
104932
|
+
});
|
|
104933
|
+
if (!res.ok)
|
|
104934
|
+
return null;
|
|
104935
|
+
const text3 = await res.text();
|
|
104936
|
+
return text3.trim() ? text3 : null;
|
|
104937
|
+
} catch {
|
|
104938
|
+
return null;
|
|
104939
|
+
}
|
|
104940
|
+
}
|
|
104941
|
+
// ../../apps/gateway/src/system/upgrade.ts
|
|
104942
|
+
import { spawn } from "child_process";
|
|
104943
|
+
import { closeSync, existsSync as existsSync6, openSync } from "fs";
|
|
104944
|
+
import { mkdtemp, rm, writeFile } from "fs/promises";
|
|
104945
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
104946
|
+
import { join as join7 } from "path";
|
|
104947
|
+
class UpgradeController {
|
|
104948
|
+
state = "idle";
|
|
104949
|
+
targetVersion = null;
|
|
104950
|
+
error = null;
|
|
104951
|
+
startedAt = null;
|
|
104952
|
+
status() {
|
|
104953
|
+
return {
|
|
104954
|
+
state: this.state,
|
|
104955
|
+
targetVersion: this.targetVersion,
|
|
104956
|
+
error: this.error,
|
|
104957
|
+
startedAt: this.startedAt
|
|
104958
|
+
};
|
|
104959
|
+
}
|
|
104960
|
+
isBusy() {
|
|
104961
|
+
return this.state !== "idle";
|
|
104962
|
+
}
|
|
104963
|
+
start(version3) {
|
|
104964
|
+
if (this.isBusy())
|
|
104965
|
+
return false;
|
|
104966
|
+
this.state = "downloading";
|
|
104967
|
+
this.targetVersion = version3;
|
|
104968
|
+
this.error = null;
|
|
104969
|
+
this.startedAt = new Date().toISOString();
|
|
104970
|
+
this.run(version3);
|
|
104971
|
+
return true;
|
|
104972
|
+
}
|
|
104973
|
+
async run(version3) {
|
|
104974
|
+
const install = getInstallInfo();
|
|
104975
|
+
const installDir = install.installDir;
|
|
104976
|
+
let stageDir = null;
|
|
104977
|
+
let cacheDir = null;
|
|
104978
|
+
try {
|
|
104979
|
+
if (!installDir) {
|
|
104980
|
+
throw new Error("install directory could not be resolved");
|
|
104981
|
+
}
|
|
104982
|
+
stageDir = await mkdtemp(join7(tmpdir4(), "tmex-upg-"));
|
|
104983
|
+
cacheDir = await mkdtemp(join7(tmpdir4(), "tmex-upg-cache-"));
|
|
104984
|
+
await writeFile(join7(stageDir, "package.json"), `${JSON.stringify({ name: "tmex-upgrade-stage", private: true })}
|
|
104985
|
+
`);
|
|
104986
|
+
await this.runBunAdd(stageDir, cacheDir, version3);
|
|
104987
|
+
const binPath = join7(stageDir, "node_modules", "tmex-cli", "bin", "tmex.js");
|
|
104988
|
+
if (!existsSync6(binPath)) {
|
|
104989
|
+
throw new Error(`downloaded tmex-cli binary not found at ${binPath}`);
|
|
104990
|
+
}
|
|
104991
|
+
this.state = "executing";
|
|
104992
|
+
this.spawnUpgrade(binPath, installDir, version3);
|
|
104993
|
+
} catch (err) {
|
|
104994
|
+
this.error = err instanceof Error ? err.message : String(err);
|
|
104995
|
+
this.state = "idle";
|
|
104996
|
+
this.targetVersion = null;
|
|
104997
|
+
if (stageDir)
|
|
104998
|
+
await rm(stageDir, { recursive: true, force: true }).catch(() => {});
|
|
104999
|
+
if (cacheDir)
|
|
105000
|
+
await rm(cacheDir, { recursive: true, force: true }).catch(() => {});
|
|
105001
|
+
}
|
|
105002
|
+
}
|
|
105003
|
+
runBunAdd(stageDir, cacheDir, version3) {
|
|
105004
|
+
return new Promise((resolve5, reject) => {
|
|
105005
|
+
const child = spawn(process.execPath, ["add", `tmex-cli@${version3}`], {
|
|
105006
|
+
cwd: stageDir,
|
|
105007
|
+
env: { ...process.env, BUN_INSTALL_CACHE_DIR: cacheDir },
|
|
105008
|
+
stdio: "ignore"
|
|
105009
|
+
});
|
|
105010
|
+
child.on("error", reject);
|
|
105011
|
+
child.on("close", (code2) => {
|
|
105012
|
+
if (code2 === 0)
|
|
105013
|
+
resolve5();
|
|
105014
|
+
else
|
|
105015
|
+
reject(new Error(`bun add tmex-cli@${version3} exited with code ${code2 ?? "null"}`));
|
|
105016
|
+
});
|
|
105017
|
+
});
|
|
105018
|
+
}
|
|
105019
|
+
spawnUpgrade(binPath, installDir, version3) {
|
|
105020
|
+
let logFd = null;
|
|
105021
|
+
try {
|
|
105022
|
+
logFd = openSync(join7(installDir, "upgrade.log"), "a");
|
|
105023
|
+
} catch {
|
|
105024
|
+
logFd = null;
|
|
105025
|
+
}
|
|
105026
|
+
const child = spawn(process.execPath, [
|
|
105027
|
+
binPath,
|
|
105028
|
+
"upgrade",
|
|
105029
|
+
"--apply-current-package",
|
|
105030
|
+
"--install-dir",
|
|
105031
|
+
installDir,
|
|
105032
|
+
"--version",
|
|
105033
|
+
version3
|
|
105034
|
+
], {
|
|
105035
|
+
cwd: installDir,
|
|
105036
|
+
env: process.env,
|
|
105037
|
+
detached: true,
|
|
105038
|
+
stdio: logFd !== null ? ["ignore", logFd, logFd] : "ignore"
|
|
105039
|
+
});
|
|
105040
|
+
child.unref();
|
|
105041
|
+
if (logFd !== null)
|
|
105042
|
+
closeSync(logFd);
|
|
105043
|
+
}
|
|
105044
|
+
}
|
|
105045
|
+
var upgradeController = new UpgradeController;
|
|
105046
|
+
|
|
105047
|
+
// ../../apps/gateway/src/system/index.ts
|
|
105048
|
+
function getSystemInfo() {
|
|
105049
|
+
const install = getInstallInfo();
|
|
105050
|
+
const canSelfUpdate = config.isProd && install.installedViaCli && install.deployment !== "none";
|
|
105051
|
+
return {
|
|
105052
|
+
version: getDisplayVersion(),
|
|
105053
|
+
baseVersion: getBaseVersion(),
|
|
105054
|
+
isProd: config.isProd,
|
|
105055
|
+
installedViaCli: install.installedViaCli,
|
|
105056
|
+
deployment: install.deployment,
|
|
105057
|
+
canSelfUpdate,
|
|
105058
|
+
serviceName: install.serviceName,
|
|
105059
|
+
transferMaxBytes: config.transferMaxBytes
|
|
105060
|
+
};
|
|
105061
|
+
}
|
|
105062
|
+
|
|
105063
|
+
// ../../apps/gateway/src/api/system.ts
|
|
105064
|
+
function json6(data, status = 200) {
|
|
105065
|
+
return new Response(JSON.stringify(data), {
|
|
105066
|
+
status,
|
|
105067
|
+
headers: { "Content-Type": "application/json" }
|
|
105068
|
+
});
|
|
105069
|
+
}
|
|
105070
|
+
function handleSystemApiRequest(req, path) {
|
|
105071
|
+
if (path === "/api/system/info" && req.method === "GET") {
|
|
105072
|
+
return json6(getSystemInfo());
|
|
105073
|
+
}
|
|
105074
|
+
if (path === "/api/system/update-check" && req.method === "GET") {
|
|
105075
|
+
return handleUpdateCheck();
|
|
105076
|
+
}
|
|
105077
|
+
if (path === "/api/system/upgrade" && req.method === "GET") {
|
|
105078
|
+
return json6(upgradeController.status());
|
|
105079
|
+
}
|
|
105080
|
+
if (path === "/api/system/upgrade" && req.method === "POST") {
|
|
105081
|
+
return handleStartUpgrade(req);
|
|
105082
|
+
}
|
|
105083
|
+
return;
|
|
105084
|
+
}
|
|
105085
|
+
async function handleUpdateCheck() {
|
|
105086
|
+
try {
|
|
105087
|
+
return json6(await checkForUpdate());
|
|
105088
|
+
} catch {
|
|
105089
|
+
return json6({ error: t2("apiError.updateCheckFailed") }, 502);
|
|
105090
|
+
}
|
|
105091
|
+
}
|
|
105092
|
+
async function handleStartUpgrade(req) {
|
|
105093
|
+
const info = getSystemInfo();
|
|
105094
|
+
if (!info.canSelfUpdate) {
|
|
105095
|
+
return json6({ error: t2("apiError.upgradeNotAllowed") }, 403);
|
|
105096
|
+
}
|
|
105097
|
+
let version3 = "";
|
|
105098
|
+
try {
|
|
105099
|
+
const body = await req.json();
|
|
105100
|
+
version3 = (body?.version ?? "").trim();
|
|
105101
|
+
} catch {
|
|
105102
|
+
version3 = "";
|
|
105103
|
+
}
|
|
105104
|
+
if (!version3) {
|
|
105105
|
+
return json6({ error: t2("apiError.upgradeVersionRequired") }, 400);
|
|
105106
|
+
}
|
|
105107
|
+
const started = upgradeController.start(version3);
|
|
105108
|
+
if (!started) {
|
|
105109
|
+
return json6({ ...upgradeController.status(), error: t2("apiError.upgradeInProgress") }, 409);
|
|
105110
|
+
}
|
|
105111
|
+
return json6(upgradeController.status());
|
|
105112
|
+
}
|
|
105113
|
+
|
|
103922
105114
|
// ../../apps/gateway/src/api/test-connection.ts
|
|
103923
105115
|
function inferFailurePhase(errorType) {
|
|
103924
105116
|
if (errorType === "tmux_unavailable") {
|
|
@@ -103926,7 +105118,7 @@ function inferFailurePhase(errorType) {
|
|
|
103926
105118
|
}
|
|
103927
105119
|
return "connect";
|
|
103928
105120
|
}
|
|
103929
|
-
function
|
|
105121
|
+
function json7(data, status = 200) {
|
|
103930
105122
|
return new Response(JSON.stringify(data), {
|
|
103931
105123
|
status,
|
|
103932
105124
|
headers: {
|
|
@@ -103945,7 +105137,7 @@ async function handleDeviceTestConnection(deviceId, inputDeps = {}) {
|
|
|
103945
105137
|
};
|
|
103946
105138
|
const device = deps.getDevice(deviceId);
|
|
103947
105139
|
if (!device) {
|
|
103948
|
-
return
|
|
105140
|
+
return json7({ error: deps.translate("apiError.deviceNotFound") }, 404);
|
|
103949
105141
|
}
|
|
103950
105142
|
const classifyErrorResponse = (error51) => {
|
|
103951
105143
|
const rawMessage = error51 instanceof Error ? error51.message : String(error51);
|
|
@@ -103958,7 +105150,7 @@ async function handleDeviceTestConnection(deviceId, inputDeps = {}) {
|
|
|
103958
105150
|
message: deps.translate(classified.messageKey, classified.messageParams),
|
|
103959
105151
|
rawMessage
|
|
103960
105152
|
};
|
|
103961
|
-
return
|
|
105153
|
+
return json7(payload);
|
|
103962
105154
|
};
|
|
103963
105155
|
let runtime = null;
|
|
103964
105156
|
try {
|
|
@@ -103971,7 +105163,7 @@ async function handleDeviceTestConnection(deviceId, inputDeps = {}) {
|
|
|
103971
105163
|
phase: "ready",
|
|
103972
105164
|
message: deps.translate("common.success")
|
|
103973
105165
|
};
|
|
103974
|
-
return
|
|
105166
|
+
return json7(payload);
|
|
103975
105167
|
} catch (error51) {
|
|
103976
105168
|
return classifyErrorResponse(error51);
|
|
103977
105169
|
} finally {
|
|
@@ -105078,35 +106270,35 @@ async function handleListRules(req) {
|
|
|
105078
106270
|
if (paneId) {
|
|
105079
106271
|
rules = rules.filter((rule) => rule.paneId === paneId);
|
|
105080
106272
|
}
|
|
105081
|
-
return
|
|
106273
|
+
return json8({ rules: rules.map(toRuleDto) });
|
|
105082
106274
|
}
|
|
105083
106275
|
async function handleCreateRule(req, deps) {
|
|
105084
106276
|
const raw = await readJsonObjectBody3(req);
|
|
105085
106277
|
if (!raw) {
|
|
105086
|
-
return
|
|
106278
|
+
return json8({ error: t2("apiError.invalidRequest") }, 400);
|
|
105087
106279
|
}
|
|
105088
106280
|
const body = raw;
|
|
105089
106281
|
const name24 = typeof body.name === "string" ? body.name.trim() : "";
|
|
105090
106282
|
if (!name24) {
|
|
105091
|
-
return
|
|
106283
|
+
return json8({ error: t2("apiError.watchNameRequired") }, 400);
|
|
105092
106284
|
}
|
|
105093
106285
|
const deviceId = typeof body.deviceId === "string" ? body.deviceId.trim() : "";
|
|
105094
106286
|
if (!deviceId) {
|
|
105095
|
-
return
|
|
106287
|
+
return json8({ error: t2("apiError.agentDeviceRequired") }, 400);
|
|
105096
106288
|
}
|
|
105097
106289
|
if (!getDeviceById(deviceId)) {
|
|
105098
|
-
return
|
|
106290
|
+
return json8({ error: t2("apiError.deviceNotFound") }, 404);
|
|
105099
106291
|
}
|
|
105100
106292
|
const paneId = typeof body.paneId === "string" ? body.paneId.trim() : "";
|
|
105101
106293
|
if (!paneId) {
|
|
105102
|
-
return
|
|
106294
|
+
return json8({ error: t2("apiError.agentPaneRequired") }, 400);
|
|
105103
106295
|
}
|
|
105104
106296
|
if (!TRIGGER_TYPES.includes(body.triggerType)) {
|
|
105105
|
-
return
|
|
106297
|
+
return json8({ error: t2("apiError.watchTriggerTypeInvalid") }, 400);
|
|
105106
106298
|
}
|
|
105107
106299
|
const parsed = parseRuleFields(raw);
|
|
105108
106300
|
if (!parsed.ok) {
|
|
105109
|
-
return
|
|
106301
|
+
return json8({ error: parsed.error }, 400);
|
|
105110
106302
|
}
|
|
105111
106303
|
const fields = parsed.fields;
|
|
105112
106304
|
const effective = {
|
|
@@ -105119,7 +106311,7 @@ async function handleCreateRule(req, deps) {
|
|
|
105119
106311
|
};
|
|
105120
106312
|
const semanticError = validateRuleSemantics(effective);
|
|
105121
106313
|
if (semanticError) {
|
|
105122
|
-
return
|
|
106314
|
+
return json8({ error: semanticError }, 400);
|
|
105123
106315
|
}
|
|
105124
106316
|
const rule = createWatchRule({
|
|
105125
106317
|
name: name24,
|
|
@@ -105142,50 +106334,50 @@ async function handleCreateRule(req, deps) {
|
|
|
105142
106334
|
cooldownSeconds: fields.cooldownSeconds
|
|
105143
106335
|
});
|
|
105144
106336
|
await deps.service.refreshRule(rule.id);
|
|
105145
|
-
return
|
|
106337
|
+
return json8({ rule: toRuleDto(rule), state: null }, 201);
|
|
105146
106338
|
}
|
|
105147
106339
|
async function handleGetRule(id) {
|
|
105148
106340
|
const rule = getWatchRuleById(id);
|
|
105149
106341
|
if (!rule) {
|
|
105150
|
-
return
|
|
106342
|
+
return json8({ error: t2("apiError.watchRuleNotFound") }, 404);
|
|
105151
106343
|
}
|
|
105152
106344
|
const state = getWatchRuleState(id);
|
|
105153
|
-
return
|
|
106345
|
+
return json8({ rule: toRuleDto(rule), state: state ? toStateDto(state) : null });
|
|
105154
106346
|
}
|
|
105155
106347
|
async function handleUpdateRule(req, id, deps) {
|
|
105156
106348
|
const existing = getWatchRuleById(id);
|
|
105157
106349
|
if (!existing) {
|
|
105158
|
-
return
|
|
106350
|
+
return json8({ error: t2("apiError.watchRuleNotFound") }, 404);
|
|
105159
106351
|
}
|
|
105160
106352
|
const raw = await readJsonObjectBody3(req);
|
|
105161
106353
|
if (!raw) {
|
|
105162
|
-
return
|
|
106354
|
+
return json8({ error: t2("apiError.invalidRequest") }, 400);
|
|
105163
106355
|
}
|
|
105164
106356
|
const body = raw;
|
|
105165
106357
|
const updates = {};
|
|
105166
106358
|
if (body.name !== undefined) {
|
|
105167
106359
|
const name24 = typeof body.name === "string" ? body.name.trim() : "";
|
|
105168
106360
|
if (!name24) {
|
|
105169
|
-
return
|
|
106361
|
+
return json8({ error: t2("apiError.watchNameRequired") }, 400);
|
|
105170
106362
|
}
|
|
105171
106363
|
updates.name = name24;
|
|
105172
106364
|
}
|
|
105173
106365
|
if (body.paneId !== undefined) {
|
|
105174
106366
|
const paneId = typeof body.paneId === "string" ? body.paneId.trim() : "";
|
|
105175
106367
|
if (!paneId) {
|
|
105176
|
-
return
|
|
106368
|
+
return json8({ error: t2("apiError.agentPaneRequired") }, 400);
|
|
105177
106369
|
}
|
|
105178
106370
|
updates.paneId = paneId;
|
|
105179
106371
|
}
|
|
105180
106372
|
if (body.triggerType !== undefined) {
|
|
105181
106373
|
if (!TRIGGER_TYPES.includes(body.triggerType)) {
|
|
105182
|
-
return
|
|
106374
|
+
return json8({ error: t2("apiError.watchTriggerTypeInvalid") }, 400);
|
|
105183
106375
|
}
|
|
105184
106376
|
updates.triggerType = body.triggerType;
|
|
105185
106377
|
}
|
|
105186
106378
|
const parsed = parseRuleFields(raw);
|
|
105187
106379
|
if (!parsed.ok) {
|
|
105188
|
-
return
|
|
106380
|
+
return json8({ error: parsed.error }, 400);
|
|
105189
106381
|
}
|
|
105190
106382
|
const fields = parsed.fields;
|
|
105191
106383
|
Object.assign(updates, fields);
|
|
@@ -105199,32 +106391,32 @@ async function handleUpdateRule(req, id, deps) {
|
|
|
105199
106391
|
};
|
|
105200
106392
|
const semanticError = validateRuleSemantics(effective);
|
|
105201
106393
|
if (semanticError) {
|
|
105202
|
-
return
|
|
106394
|
+
return json8({ error: semanticError }, 400);
|
|
105203
106395
|
}
|
|
105204
106396
|
const rule = updateWatchRule(id, updates);
|
|
105205
106397
|
if (!rule) {
|
|
105206
|
-
return
|
|
106398
|
+
return json8({ error: t2("apiError.watchRuleNotFound") }, 404);
|
|
105207
106399
|
}
|
|
105208
106400
|
await deps.service.refreshRule(id);
|
|
105209
106401
|
const state = getWatchRuleState(id);
|
|
105210
|
-
return
|
|
106402
|
+
return json8({ rule: toRuleDto(rule), state: state ? toStateDto(state) : null });
|
|
105211
106403
|
}
|
|
105212
106404
|
async function handleDeleteRule(id, deps) {
|
|
105213
106405
|
const existing = getWatchRuleById(id);
|
|
105214
106406
|
if (!existing) {
|
|
105215
|
-
return
|
|
106407
|
+
return json8({ error: t2("apiError.watchRuleNotFound") }, 404);
|
|
105216
106408
|
}
|
|
105217
106409
|
deleteWatchRule(id);
|
|
105218
106410
|
await deps.service.removeRule(id);
|
|
105219
|
-
return
|
|
106411
|
+
return json8({ success: true });
|
|
105220
106412
|
}
|
|
105221
106413
|
async function handleGetRuleState(id, deps) {
|
|
105222
106414
|
const rule = getWatchRuleById(id);
|
|
105223
106415
|
if (!rule) {
|
|
105224
|
-
return
|
|
106416
|
+
return json8({ error: t2("apiError.watchRuleNotFound") }, 404);
|
|
105225
106417
|
}
|
|
105226
106418
|
const state = getWatchRuleState(id);
|
|
105227
|
-
return
|
|
106419
|
+
return json8({
|
|
105228
106420
|
state: state ? toStateDto(state) : null,
|
|
105229
106421
|
samples: deps.service.getSamples(id)
|
|
105230
106422
|
});
|
|
@@ -105248,17 +106440,17 @@ function buildAssistPrompt(description, screen) {
|
|
|
105248
106440
|
async function handleAssistRegex(req, deps) {
|
|
105249
106441
|
const raw = await readJsonObjectBody3(req);
|
|
105250
106442
|
if (!raw) {
|
|
105251
|
-
return
|
|
106443
|
+
return json8({ error: t2("apiError.invalidRequest") }, 400);
|
|
105252
106444
|
}
|
|
105253
106445
|
const body = raw;
|
|
105254
106446
|
const description = typeof body.description === "string" ? body.description.trim() : "";
|
|
105255
106447
|
if (!description) {
|
|
105256
|
-
return
|
|
106448
|
+
return json8({ error: t2("apiError.watchAssistDescriptionRequired") }, 400);
|
|
105257
106449
|
}
|
|
105258
106450
|
let providerId = null;
|
|
105259
106451
|
if (body.providerId !== undefined && body.providerId !== null) {
|
|
105260
106452
|
if (typeof body.providerId !== "string" || !getLlmProviderById(body.providerId)) {
|
|
105261
|
-
return
|
|
106453
|
+
return json8({ error: t2("apiError.llmProviderNotFound") }, 400);
|
|
105262
106454
|
}
|
|
105263
106455
|
providerId = body.providerId;
|
|
105264
106456
|
}
|
|
@@ -105268,7 +106460,7 @@ async function handleAssistRegex(req, deps) {
|
|
|
105268
106460
|
const paneId = typeof body.paneId === "string" ? body.paneId.trim() : "";
|
|
105269
106461
|
if (deviceId && paneId) {
|
|
105270
106462
|
if (!getDeviceById(deviceId)) {
|
|
105271
|
-
return
|
|
106463
|
+
return json8({ error: t2("apiError.deviceNotFound") }, 404);
|
|
105272
106464
|
}
|
|
105273
106465
|
try {
|
|
105274
106466
|
screen = await deps.captureScreen(deviceId, paneId);
|
|
@@ -105289,14 +106481,14 @@ async function handleAssistRegex(req, deps) {
|
|
|
105289
106481
|
object3 = result.object;
|
|
105290
106482
|
} catch (error51) {
|
|
105291
106483
|
const detail = error51 instanceof Error ? error51.message : String(error51);
|
|
105292
|
-
return
|
|
106484
|
+
return json8({ error: t2("apiError.watchAssistModelUnavailable", { detail }) }, 502);
|
|
105293
106485
|
}
|
|
105294
106486
|
let regex;
|
|
105295
106487
|
try {
|
|
105296
106488
|
regex = compileWatchPattern(object3.pattern, object3.flags);
|
|
105297
106489
|
} catch (error51) {
|
|
105298
106490
|
const detail = error51 instanceof Error ? error51.message : String(error51);
|
|
105299
|
-
return
|
|
106491
|
+
return json8({ error: t2("apiError.watchPatternInvalid", { detail }) }, 502);
|
|
105300
106492
|
}
|
|
105301
106493
|
const preview = [];
|
|
105302
106494
|
if (screen) {
|
|
@@ -105310,7 +106502,7 @@ async function handleAssistRegex(req, deps) {
|
|
|
105310
106502
|
match = regex.exec(screen);
|
|
105311
106503
|
}
|
|
105312
106504
|
}
|
|
105313
|
-
return
|
|
106505
|
+
return json8({
|
|
105314
106506
|
pattern: object3.pattern,
|
|
105315
106507
|
flags: object3.flags,
|
|
105316
106508
|
extractGroup: object3.extractGroup >= 0 ? object3.extractGroup : 0,
|
|
@@ -105318,7 +106510,7 @@ async function handleAssistRegex(req, deps) {
|
|
|
105318
106510
|
preview
|
|
105319
106511
|
});
|
|
105320
106512
|
}
|
|
105321
|
-
function
|
|
106513
|
+
function json8(data, status = 200) {
|
|
105322
106514
|
return new Response(JSON.stringify(data), {
|
|
105323
106515
|
status,
|
|
105324
106516
|
headers: {
|
|
@@ -105508,6 +106700,12 @@ function handleApiRequest(req, _server) {
|
|
|
105508
106700
|
return filesResponse;
|
|
105509
106701
|
}
|
|
105510
106702
|
}
|
|
106703
|
+
if (path.startsWith("/api/system/")) {
|
|
106704
|
+
const systemResponse = handleSystemApiRequest(req, path);
|
|
106705
|
+
if (systemResponse) {
|
|
106706
|
+
return systemResponse;
|
|
106707
|
+
}
|
|
106708
|
+
}
|
|
105511
106709
|
if (path === "/api/webhooks" && req.method === "GET") {
|
|
105512
106710
|
return handleGetWebhooks();
|
|
105513
106711
|
}
|
|
@@ -105521,9 +106719,13 @@ function handleApiRequest(req, _server) {
|
|
|
105521
106719
|
return handleGetManifest(req.method);
|
|
105522
106720
|
}
|
|
105523
106721
|
if (path === "/healthz" && req.method === "GET") {
|
|
105524
|
-
return
|
|
106722
|
+
return json9({
|
|
106723
|
+
status: "ok",
|
|
106724
|
+
restarting: runtimeController.isRestarting(),
|
|
106725
|
+
env: "development"
|
|
106726
|
+
});
|
|
105525
106727
|
}
|
|
105526
|
-
return
|
|
106728
|
+
return json9({ error: t2("apiError.notFound") }, 404);
|
|
105527
106729
|
}
|
|
105528
106730
|
function enrichDeviceWithRuntime(device) {
|
|
105529
106731
|
const status = getDeviceRuntimeStatus(device.id);
|
|
@@ -105537,22 +106739,22 @@ function enrichDeviceWithRuntime(device) {
|
|
|
105537
106739
|
}
|
|
105538
106740
|
async function handleGetDevices() {
|
|
105539
106741
|
const devices2 = getAllDevices().map(enrichDeviceWithRuntime);
|
|
105540
|
-
return
|
|
106742
|
+
return json9({ devices: devices2 });
|
|
105541
106743
|
}
|
|
105542
106744
|
async function handleGetDevice(id) {
|
|
105543
106745
|
const device = getDeviceById(id);
|
|
105544
106746
|
if (!device) {
|
|
105545
|
-
return
|
|
106747
|
+
return json9({ error: t2("apiError.deviceNotFound") }, 404);
|
|
105546
106748
|
}
|
|
105547
|
-
return
|
|
106749
|
+
return json9({ device: enrichDeviceWithRuntime(device) });
|
|
105548
106750
|
}
|
|
105549
106751
|
async function handleCreateDevice(req) {
|
|
105550
106752
|
const body = await req.json();
|
|
105551
106753
|
if (!body.name || !body.type || !body.authMode) {
|
|
105552
|
-
return
|
|
106754
|
+
return json9({ error: t2("apiError.missingFields") }, 400);
|
|
105553
106755
|
}
|
|
105554
106756
|
if (body.type === "ssh" && !body.host && !body.sshConfigRef) {
|
|
105555
|
-
return
|
|
106757
|
+
return json9({ error: t2("apiError.sshRequiresHost") }, 400);
|
|
105556
106758
|
}
|
|
105557
106759
|
const now2 = new Date().toISOString();
|
|
105558
106760
|
const device = {
|
|
@@ -105574,12 +106776,12 @@ async function handleCreateDevice(req) {
|
|
|
105574
106776
|
};
|
|
105575
106777
|
createDevice(device);
|
|
105576
106778
|
await pushSupervisor.upsert(device.id);
|
|
105577
|
-
return
|
|
106779
|
+
return json9({ device: getDeviceById(device.id) ?? device }, 201);
|
|
105578
106780
|
}
|
|
105579
106781
|
async function handleUpdateDevice(req, id) {
|
|
105580
106782
|
const existing = getDeviceById(id);
|
|
105581
106783
|
if (!existing) {
|
|
105582
|
-
return
|
|
106784
|
+
return json9({ error: t2("apiError.deviceNotFound") }, 404);
|
|
105583
106785
|
}
|
|
105584
106786
|
const body = await req.json();
|
|
105585
106787
|
const updates = {};
|
|
@@ -105609,61 +106811,61 @@ async function handleUpdateDevice(req, id) {
|
|
|
105609
106811
|
await pushSupervisor.reconnect(id);
|
|
105610
106812
|
}
|
|
105611
106813
|
const device = getDeviceById(id);
|
|
105612
|
-
return
|
|
106814
|
+
return json9({ device });
|
|
105613
106815
|
}
|
|
105614
106816
|
async function handleReorderDevices(req) {
|
|
105615
106817
|
const body = await req.json();
|
|
105616
106818
|
if (!Array.isArray(body.deviceIds) || body.deviceIds.some((id) => typeof id !== "string")) {
|
|
105617
|
-
return
|
|
106819
|
+
return json9({ error: t2("apiError.invalidRequest") }, 400);
|
|
105618
106820
|
}
|
|
105619
106821
|
reorderDevices(body.deviceIds);
|
|
105620
|
-
return
|
|
106822
|
+
return json9({ devices: getAllDevices().map(enrichDeviceWithRuntime) });
|
|
105621
106823
|
}
|
|
105622
106824
|
async function handleDeleteDevice(id) {
|
|
105623
106825
|
const existing = getDeviceById(id);
|
|
105624
106826
|
if (!existing) {
|
|
105625
|
-
return
|
|
106827
|
+
return json9({ error: t2("apiError.deviceNotFound") }, 404);
|
|
105626
106828
|
}
|
|
105627
106829
|
deleteDevice(id);
|
|
105628
106830
|
pushSupervisor.remove(id);
|
|
105629
|
-
return
|
|
106831
|
+
return json9({ success: true });
|
|
105630
106832
|
}
|
|
105631
106833
|
async function handleTestConnection(id) {
|
|
105632
106834
|
return handleDeviceTestConnection(id);
|
|
105633
106835
|
}
|
|
105634
106836
|
async function handleGetSiteSettings() {
|
|
105635
|
-
return
|
|
106837
|
+
return json9({ settings: getSiteSettings() });
|
|
105636
106838
|
}
|
|
105637
106839
|
async function handleUpdateSiteSettings(req) {
|
|
105638
106840
|
try {
|
|
105639
106841
|
const body = await req.json();
|
|
105640
106842
|
const updates = normalizeSiteSettingsInput(body);
|
|
105641
106843
|
const settings = updateSiteSettings(updates);
|
|
105642
|
-
return
|
|
106844
|
+
return json9({ settings });
|
|
105643
106845
|
} catch (err) {
|
|
105644
|
-
return
|
|
106846
|
+
return json9({ error: err instanceof Error ? err.message : t2("apiError.invalidRequest") }, 400);
|
|
105645
106847
|
}
|
|
105646
106848
|
}
|
|
105647
106849
|
async function handleRestartGateway() {
|
|
105648
106850
|
setTimeout(() => {
|
|
105649
106851
|
runtimeController.requestRestart();
|
|
105650
106852
|
}, 50);
|
|
105651
|
-
return
|
|
106853
|
+
return json9({
|
|
105652
106854
|
success: true,
|
|
105653
106855
|
message: t2("settings.restartScheduled")
|
|
105654
106856
|
});
|
|
105655
106857
|
}
|
|
105656
106858
|
async function handleGetTelegramBots() {
|
|
105657
106859
|
const bots = getTelegramBotsWithStats();
|
|
105658
|
-
return
|
|
106860
|
+
return json9({ bots });
|
|
105659
106861
|
}
|
|
105660
106862
|
async function handleCreateTelegramBot(req) {
|
|
105661
106863
|
const body = await req.json();
|
|
105662
106864
|
if (!body.name?.trim()) {
|
|
105663
|
-
return
|
|
106865
|
+
return json9({ error: t2("apiError.botNameRequired") }, 400);
|
|
105664
106866
|
}
|
|
105665
106867
|
if (!body.token?.trim()) {
|
|
105666
|
-
return
|
|
106868
|
+
return json9({ error: t2("apiError.botTokenRequired") }, 400);
|
|
105667
106869
|
}
|
|
105668
106870
|
const now2 = new Date().toISOString();
|
|
105669
106871
|
createTelegramBot({
|
|
@@ -105677,26 +106879,26 @@ async function handleCreateTelegramBot(req) {
|
|
|
105677
106879
|
updatedAt: now2
|
|
105678
106880
|
});
|
|
105679
106881
|
await telegramService.refresh();
|
|
105680
|
-
return
|
|
106882
|
+
return json9({ success: true }, 201);
|
|
105681
106883
|
}
|
|
105682
106884
|
async function handleUpdateTelegramBot(req, botId) {
|
|
105683
106885
|
const existing = getTelegramBotById(botId);
|
|
105684
106886
|
if (!existing) {
|
|
105685
|
-
return
|
|
106887
|
+
return json9({ error: t2("apiError.botNotFound") }, 404);
|
|
105686
106888
|
}
|
|
105687
106889
|
const body = await req.json();
|
|
105688
106890
|
const updates = {};
|
|
105689
106891
|
if (body.name !== undefined) {
|
|
105690
106892
|
const value = body.name.trim();
|
|
105691
106893
|
if (!value) {
|
|
105692
|
-
return
|
|
106894
|
+
return json9({ error: t2("apiError.botNameRequired") }, 400);
|
|
105693
106895
|
}
|
|
105694
106896
|
updates.name = value;
|
|
105695
106897
|
}
|
|
105696
106898
|
if (body.token !== undefined) {
|
|
105697
106899
|
const token = body.token.trim();
|
|
105698
106900
|
if (!token) {
|
|
105699
|
-
return
|
|
106901
|
+
return json9({ error: t2("apiError.botTokenRequired") }, 400);
|
|
105700
106902
|
}
|
|
105701
106903
|
updates.tokenEnc = await encrypt(token);
|
|
105702
106904
|
}
|
|
@@ -105708,69 +106910,69 @@ async function handleUpdateTelegramBot(req, botId) {
|
|
|
105708
106910
|
}
|
|
105709
106911
|
updateTelegramBot(botId, updates);
|
|
105710
106912
|
await telegramService.refresh();
|
|
105711
|
-
return
|
|
106913
|
+
return json9({ success: true });
|
|
105712
106914
|
}
|
|
105713
106915
|
async function handleDeleteTelegramBot(botId) {
|
|
105714
106916
|
const existing = getTelegramBotById(botId);
|
|
105715
106917
|
if (!existing) {
|
|
105716
|
-
return
|
|
106918
|
+
return json9({ error: t2("apiError.botNotFound") }, 404);
|
|
105717
106919
|
}
|
|
105718
106920
|
deleteTelegramBot(botId);
|
|
105719
106921
|
await telegramService.refresh();
|
|
105720
|
-
return
|
|
106922
|
+
return json9({ success: true });
|
|
105721
106923
|
}
|
|
105722
106924
|
async function handleListTelegramChats(botId) {
|
|
105723
106925
|
const existing = getTelegramBotById(botId);
|
|
105724
106926
|
if (!existing) {
|
|
105725
|
-
return
|
|
106927
|
+
return json9({ error: t2("apiError.botNotFound") }, 404);
|
|
105726
106928
|
}
|
|
105727
106929
|
const chats = listTelegramChatsByBot(botId);
|
|
105728
|
-
return
|
|
106930
|
+
return json9({ chats });
|
|
105729
106931
|
}
|
|
105730
106932
|
async function handleApproveTelegramChat(botId, chatId) {
|
|
105731
106933
|
const existing = getTelegramBotById(botId);
|
|
105732
106934
|
if (!existing) {
|
|
105733
|
-
return
|
|
106935
|
+
return json9({ error: t2("apiError.botNotFound") }, 404);
|
|
105734
106936
|
}
|
|
105735
106937
|
const chat = approveTelegramChat(botId, chatId);
|
|
105736
106938
|
if (!chat) {
|
|
105737
|
-
return
|
|
106939
|
+
return json9({ error: t2("apiError.chatNotFound") }, 404);
|
|
105738
106940
|
}
|
|
105739
106941
|
const settings = getSiteSettings();
|
|
105740
106942
|
await telegramService.sendTestMessage(botId, chatId, t2("telegram.approveMessageTemplate", {
|
|
105741
106943
|
botName: existing.name,
|
|
105742
106944
|
time: new Date().toLocaleString(toBCP47(settings.language))
|
|
105743
106945
|
}));
|
|
105744
|
-
return
|
|
106946
|
+
return json9({ chat });
|
|
105745
106947
|
}
|
|
105746
106948
|
async function handleDeleteTelegramChat(botId, chatId) {
|
|
105747
106949
|
const existing = getTelegramBotById(botId);
|
|
105748
106950
|
if (!existing) {
|
|
105749
|
-
return
|
|
106951
|
+
return json9({ error: t2("apiError.botNotFound") }, 404);
|
|
105750
106952
|
}
|
|
105751
106953
|
deleteTelegramChat(botId, chatId);
|
|
105752
|
-
return
|
|
106954
|
+
return json9({ success: true });
|
|
105753
106955
|
}
|
|
105754
106956
|
async function handleTestTelegramChat(botId, chatId) {
|
|
105755
106957
|
const bot = getTelegramBotById(botId);
|
|
105756
106958
|
if (!bot) {
|
|
105757
|
-
return
|
|
106959
|
+
return json9({ error: t2("apiError.botNotFound") }, 404);
|
|
105758
106960
|
}
|
|
105759
106961
|
const settings = getSiteSettings();
|
|
105760
106962
|
await telegramService.sendTestMessage(botId, chatId, t2("telegram.testMessageTemplate", {
|
|
105761
106963
|
siteName: settings.siteName,
|
|
105762
106964
|
time: new Date().toLocaleString(toBCP47(settings.language))
|
|
105763
106965
|
}));
|
|
105764
|
-
return
|
|
106966
|
+
return json9({ success: true });
|
|
105765
106967
|
}
|
|
105766
106968
|
async function handleGetWebhooks() {
|
|
105767
106969
|
const webhooks = getAllWebhookEndpoints();
|
|
105768
|
-
return
|
|
106970
|
+
return json9({ webhooks });
|
|
105769
106971
|
}
|
|
105770
106972
|
async function handleCreateWebhook(req) {
|
|
105771
106973
|
const body = await req.json();
|
|
105772
106974
|
if (!body.url || !body.secret) {
|
|
105773
|
-
return
|
|
106975
|
+
return json9({ error: t2("apiError.urlAndSecretRequired") }, 400);
|
|
105774
106976
|
}
|
|
105775
106977
|
const now2 = new Date().toISOString();
|
|
105776
106978
|
const endpoint = {
|
|
@@ -105783,11 +106985,11 @@ async function handleCreateWebhook(req) {
|
|
|
105783
106985
|
updatedAt: now2
|
|
105784
106986
|
};
|
|
105785
106987
|
createWebhookEndpoint(endpoint);
|
|
105786
|
-
return
|
|
106988
|
+
return json9({ webhook: endpoint }, 201);
|
|
105787
106989
|
}
|
|
105788
106990
|
async function handleDeleteWebhook(id) {
|
|
105789
106991
|
deleteWebhookEndpoint(id);
|
|
105790
|
-
return
|
|
106992
|
+
return json9({ success: true });
|
|
105791
106993
|
}
|
|
105792
106994
|
async function handleGetManifest(method) {
|
|
105793
106995
|
const settings = getSiteSettings();
|
|
@@ -105808,7 +107010,7 @@ async function handleGetManifest(method) {
|
|
|
105808
107010
|
purpose: "any"
|
|
105809
107011
|
},
|
|
105810
107012
|
{
|
|
105811
|
-
src: "/tmex.png",
|
|
107013
|
+
src: "/tmex-maskable.png",
|
|
105812
107014
|
sizes: "1024x1024",
|
|
105813
107015
|
type: "image/png",
|
|
105814
107016
|
purpose: "maskable"
|
|
@@ -105826,7 +107028,7 @@ function manifestJson(data, method) {
|
|
|
105826
107028
|
}
|
|
105827
107029
|
});
|
|
105828
107030
|
}
|
|
105829
|
-
function
|
|
107031
|
+
function json9(data, status = 200, headers = {}) {
|
|
105830
107032
|
return new Response(JSON.stringify(data), {
|
|
105831
107033
|
status,
|
|
105832
107034
|
headers: {
|
|
@@ -105837,8 +107039,8 @@ function json8(data, status = 200, headers = {}) {
|
|
|
105837
107039
|
}
|
|
105838
107040
|
|
|
105839
107041
|
// ../../apps/gateway/src/db/migrate.ts
|
|
105840
|
-
import { existsSync as
|
|
105841
|
-
import { resolve as
|
|
107042
|
+
import { existsSync as existsSync7 } from "fs";
|
|
107043
|
+
import { resolve as resolve5 } from "path";
|
|
105842
107044
|
|
|
105843
107045
|
// ../../node_modules/.bun/drizzle-orm@0.45.1+d3720f6f296df04b/node_modules/drizzle-orm/migrator.js
|
|
105844
107046
|
import crypto2 from "crypto";
|
|
@@ -105883,10 +107085,10 @@ function resolveMigrationsFolder() {
|
|
|
105883
107085
|
const fromEnv = process.env.TMEX_MIGRATIONS_DIR;
|
|
105884
107086
|
if (fromEnv)
|
|
105885
107087
|
return fromEnv;
|
|
105886
|
-
const byCwd =
|
|
105887
|
-
if (
|
|
107088
|
+
const byCwd = resolve5(process.cwd(), "drizzle");
|
|
107089
|
+
if (existsSync7(byCwd))
|
|
105888
107090
|
return byCwd;
|
|
105889
|
-
return
|
|
107091
|
+
return resolve5(import.meta.dir, "../../drizzle");
|
|
105890
107092
|
}
|
|
105891
107093
|
var migrationsFolder = resolveMigrationsFolder();
|
|
105892
107094
|
function runMigrations() {
|
|
@@ -107390,6 +108592,7 @@ async function createGatewayRuntime(options = {}) {
|
|
|
107390
108592
|
}
|
|
107391
108593
|
runtimeController.reset();
|
|
107392
108594
|
primeLocalShellPath();
|
|
108595
|
+
sweepOrphanTransferTemps();
|
|
107393
108596
|
const wsServer = new WebSocketServer;
|
|
107394
108597
|
connectionAlertNotifier.setBroadcaster((deviceId, payload) => {
|
|
107395
108598
|
wsServer.broadcastDeviceError(deviceId, payload);
|
|
@@ -107688,16 +108891,16 @@ function contentTypeByPath(path) {
|
|
|
107688
108891
|
}
|
|
107689
108892
|
function resolveStaticRoot() {
|
|
107690
108893
|
if (process.env.TMEX_FE_DIST_DIR) {
|
|
107691
|
-
return
|
|
108894
|
+
return resolve6(process.env.TMEX_FE_DIST_DIR);
|
|
107692
108895
|
}
|
|
107693
|
-
return
|
|
108896
|
+
return resolve6(import.meta.dir, "../../resources/fe-dist");
|
|
107694
108897
|
}
|
|
107695
108898
|
function resolveRequestedFile(staticRoot, pathname) {
|
|
107696
|
-
const root =
|
|
108899
|
+
const root = resolve6(staticRoot);
|
|
107697
108900
|
const decoded = decodeURIComponent(pathname);
|
|
107698
108901
|
const normalized = normalize(decoded).replace(/^\.\.(\/|\\|$)/, "");
|
|
107699
108902
|
const requested = normalized === "/" ? "/index.html" : normalized;
|
|
107700
|
-
const absolutePath =
|
|
108903
|
+
const absolutePath = resolve6(root, `.${requested}`);
|
|
107701
108904
|
if (absolutePath !== root && !absolutePath.startsWith(`${root}${sep}`)) {
|
|
107702
108905
|
return null;
|
|
107703
108906
|
}
|
|
@@ -107712,12 +108915,12 @@ async function serveFrontend(req, staticRoot) {
|
|
|
107712
108915
|
if (!requestedPath) {
|
|
107713
108916
|
return new Response(t3("runtime.forbidden"), { status: 403 });
|
|
107714
108917
|
}
|
|
107715
|
-
if (!
|
|
108918
|
+
if (!existsSync8(requestedPath) && extname2(url2.pathname) !== "") {
|
|
107716
108919
|
return new Response(t3("runtime.notFound"), { status: 404 });
|
|
107717
108920
|
}
|
|
107718
|
-
const indexPath =
|
|
107719
|
-
const targetPath =
|
|
107720
|
-
if (!
|
|
108921
|
+
const indexPath = join8(staticRoot, "index.html");
|
|
108922
|
+
const targetPath = existsSync8(requestedPath) ? requestedPath : indexPath;
|
|
108923
|
+
if (!existsSync8(targetPath)) {
|
|
107721
108924
|
return new Response(t3("runtime.frontendMissing"), { status: 500 });
|
|
107722
108925
|
}
|
|
107723
108926
|
const headers = new Headers;
|
|
@@ -107728,6 +108931,7 @@ async function serveFrontend(req, staticRoot) {
|
|
|
107728
108931
|
return new Response(Bun.file(targetPath), { headers });
|
|
107729
108932
|
}
|
|
107730
108933
|
async function main() {
|
|
108934
|
+
console.log(`[tmex] version ${getDisplayVersion()}`);
|
|
107731
108935
|
const host = process.env.TMEX_BIND_HOST || "127.0.0.1";
|
|
107732
108936
|
const port = Number(process.env.GATEWAY_PORT || "9883");
|
|
107733
108937
|
const staticRoot = resolveStaticRoot();
|