tmex-cli 0.11.0 → 0.12.2
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 +22 -12
- package/dist/runtime/server.js +752 -48
- package/package.json +1 -1
- package/resources/fe-dist/assets/DevicePage-B0fQ6yWB.js +25 -0
- package/resources/fe-dist/assets/DevicesPage-C7THDzTX.js +2 -0
- package/resources/fe-dist/assets/{FilePage-YWma96aj.js → FilePage-D8V8P2IA.js} +8 -8
- package/resources/fe-dist/assets/{SettingsPage-LhUD03Ln.js → SettingsPage-BOTyBhHH.js} +8 -8
- package/resources/fe-dist/assets/{arc-DLVa298O.js → arc-DcujnLz7.js} +2 -2
- package/resources/fe-dist/assets/architectureDiagram-3BPJPVTR-i8499Wfc.js +37 -0
- package/resources/fe-dist/assets/{blockDiagram-GPEHLZMM-BTwwkzZh.js → blockDiagram-GPEHLZMM-BFh4xZY2.js} +2 -2
- package/resources/fe-dist/assets/{c4Diagram-AAUBKEIU-xvTqXxl9.js → c4Diagram-AAUBKEIU-nUyGK7Ke.js} +2 -2
- package/resources/fe-dist/assets/{card-cbVDy0Xp.js → card-BkRqR9zz.js} +2 -2
- package/resources/fe-dist/assets/channel-DqmfsuLY.js +2 -0
- package/resources/fe-dist/assets/{chunk-2J33WTMH-Blr6l1Nk.js → chunk-2J33WTMH-z2oMKYMn.js} +2 -2
- package/resources/fe-dist/assets/{chunk-4BX2VUAB-D_Y2TeLg.js → chunk-4BX2VUAB-CCD_pyAh.js} +2 -2
- package/resources/fe-dist/assets/{chunk-55IACEB6-CimNoYDH.js → chunk-55IACEB6-Dx2_9d-z.js} +2 -2
- package/resources/fe-dist/assets/{chunk-727SXJPM-JPYDN0Cb.js → chunk-727SXJPM-DA36poir.js} +2 -2
- package/resources/fe-dist/assets/{chunk-AQP2D5EJ-Cb3NZBib.js → chunk-AQP2D5EJ-CQoXkVd4.js} +2 -2
- package/resources/fe-dist/assets/{chunk-FMBD7UC4-DyZ7Ot2F.js → chunk-FMBD7UC4-A6Yn7A44.js} +2 -2
- package/resources/fe-dist/assets/{chunk-ND2GUHAM-SU6Ea3wF.js → chunk-ND2GUHAM-BsNl2EMT.js} +2 -2
- package/resources/fe-dist/assets/{chunk-QZHKN3VN-B6qXX6sU.js → chunk-QZHKN3VN-BM_QNxf6.js} +2 -2
- package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-D3Z9BeOE.js +2 -0
- package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-D3Z9BeOE.js +2 -0
- package/resources/fe-dist/assets/cose-bilkent-S5V4N54A-1lMO_Dod.js +2 -0
- package/resources/fe-dist/assets/{dagre-BM42HDAG-Ckrx9BXZ.js → dagre-BM42HDAG-C9Up3ckQ.js} +2 -2
- package/resources/fe-dist/assets/{diagram-2AECGRRQ-DTnZtf2k.js → diagram-2AECGRRQ-CtuDndmY.js} +2 -2
- package/resources/fe-dist/assets/{diagram-5GNKFQAL-DBpgZj4a.js → diagram-5GNKFQAL-BQfskK8S.js} +2 -2
- package/resources/fe-dist/assets/{diagram-KO2AKTUF-CfijYCeY.js → diagram-KO2AKTUF-CGJi8q81.js} +2 -2
- package/resources/fe-dist/assets/{diagram-LMA3HP47-D7nBmQ-E.js → diagram-LMA3HP47-DAMQIoLQ.js} +2 -2
- package/resources/fe-dist/assets/{diagram-OG6HWLK6-Bi8sHV0O.js → diagram-OG6HWLK6-D2s__dFc.js} +2 -2
- package/resources/fe-dist/assets/{erDiagram-TEJ5UH35-DcoDesO2.js → erDiagram-TEJ5UH35-_UJYLIQN.js} +2 -2
- package/resources/fe-dist/assets/{flowDiagram-I6XJVG4X-Bbcl9kxS.js → flowDiagram-I6XJVG4X-DN-qktDh.js} +2 -2
- package/resources/fe-dist/assets/ganttDiagram-6RSMTGT7-CNuuuhXX.js +293 -0
- package/resources/fe-dist/assets/{gitGraphDiagram-PVQCEYII-x3wpNQcW.js → gitGraphDiagram-PVQCEYII-Qar2sVRo.js} +2 -2
- package/resources/fe-dist/assets/index-BhU3LRKb.css +1 -0
- package/resources/fe-dist/assets/index-Ra6pj0rt.js +488 -0
- package/resources/fe-dist/assets/{infoDiagram-5YYISTIA-Bl6cgwVb.js → infoDiagram-5YYISTIA-2e6tU_v9.js} +2 -2
- package/resources/fe-dist/assets/{ishikawaDiagram-YF4QCWOH-BfQ7aZxN.js → ishikawaDiagram-YF4QCWOH-BghKvoM4.js} +2 -2
- package/resources/fe-dist/assets/{journeyDiagram-JHISSGLW-DSwQzmdL.js → journeyDiagram-JHISSGLW-D-cHSGb_.js} +2 -2
- package/resources/fe-dist/assets/{kanban-definition-UN3LZRKU-B1aWDl4E.js → kanban-definition-UN3LZRKU-BE6Cb-OK.js} +2 -2
- package/resources/fe-dist/assets/{linear-CfhRC4iX.js → linear-CwDBnUH0.js} +2 -2
- package/resources/fe-dist/assets/{markdown-preview-CfBRMViB.js → markdown-preview-BlQiC8ND.js} +4 -4
- package/resources/fe-dist/assets/{mermaid.core-M-o4g4TO.js → mermaid.core-DDMkLXrp.js} +6 -6
- package/resources/fe-dist/assets/{mindmap-definition-RKZ34NQL-D976sxOA.js → mindmap-definition-RKZ34NQL-C9xVR7Ut.js} +2 -2
- package/resources/fe-dist/assets/{pieDiagram-4H26LBE5-PVQ3-EB6.js → pieDiagram-4H26LBE5-DW_4RcBv.js} +2 -2
- package/resources/fe-dist/assets/{quadrantDiagram-W4KKPZXB-BNEFhYNq.js → quadrantDiagram-W4KKPZXB-DoIrZmk2.js} +2 -2
- package/resources/fe-dist/assets/{requirementDiagram-4Y6WPE33-B7Zd2lpN.js → requirementDiagram-4Y6WPE33-CYcJSuPe.js} +2 -2
- package/resources/fe-dist/assets/{sankeyDiagram-5OEKKPKP-D_Td4j-B.js → sankeyDiagram-5OEKKPKP-CrIkKM6h.js} +2 -2
- package/resources/fe-dist/assets/{sequenceDiagram-3UESZ5HK-DQSVX5mL.js → sequenceDiagram-3UESZ5HK-DgzGHhGk.js} +2 -2
- package/resources/fe-dist/assets/{stateDiagram-AJRCARHV-C0PNCTfs.js → stateDiagram-AJRCARHV-C6Dhr3fY.js} +2 -2
- package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-BiSh2azE.js +2 -0
- package/resources/fe-dist/assets/terminal-settings-panel-CpnmWQtf.js +9 -0
- package/resources/fe-dist/assets/{timeline-definition-PNZ67QCA-BlMj8Fl1.js → timeline-definition-PNZ67QCA-jJ1iauQ0.js} +2 -2
- package/resources/fe-dist/assets/{vennDiagram-CIIHVFJN-DuGxwZiq.js → vennDiagram-CIIHVFJN-DLRoaoJ9.js} +2 -2
- package/resources/fe-dist/assets/{wardley-L42UT6IY-4VhWbg10.js → wardley-L42UT6IY-CykeHWp8.js} +2 -2
- package/resources/fe-dist/assets/{wardleyDiagram-YWT4CUSO-Bu4k6xIx.js → wardleyDiagram-YWT4CUSO-U22jzD8a.js} +2 -2
- package/resources/fe-dist/assets/{xychartDiagram-2RQKCTM6-Cv88IDWh.js → xychartDiagram-2RQKCTM6-CiXS9PJ2.js} +2 -2
- package/resources/fe-dist/fonts/generated/blex-mono/blex-mono-bold.woff2 +0 -0
- package/resources/fe-dist/fonts/generated/blex-mono/blex-mono-regular.woff2 +0 -0
- package/resources/fe-dist/fonts/generated/fira-code/fira-code-bold.woff2 +0 -0
- package/resources/fe-dist/fonts/generated/fira-code/fira-code-regular.woff2 +0 -0
- package/resources/fe-dist/fonts/generated/jetbrains-mono/jetbrains-mono-bold.woff2 +0 -0
- package/resources/fe-dist/fonts/generated/jetbrains-mono/jetbrains-mono-regular.woff2 +0 -0
- package/resources/fe-dist/fonts/generated/noto-sans-mono/noto-sans-mono-bold.woff2 +0 -0
- package/resources/fe-dist/fonts/generated/noto-sans-mono/noto-sans-mono-regular.woff2 +0 -0
- package/resources/fe-dist/fonts/generated/victor-mono/victor-mono-bold.woff2 +0 -0
- package/resources/fe-dist/fonts/generated/victor-mono/victor-mono-regular.woff2 +0 -0
- package/resources/fe-dist/fonts/generated/zed-mono/zed-mono-bold.woff2 +0 -0
- package/resources/fe-dist/fonts/generated/zed-mono/zed-mono-regular.woff2 +0 -0
- package/resources/fe-dist/index.html +2 -2
- package/resources/fe-dist/assets/DevicePage-fGI-do8S.js +0 -26
- package/resources/fe-dist/assets/DevicesPage-kGrzwhGj.js +0 -2
- package/resources/fe-dist/assets/architectureDiagram-3BPJPVTR-EOUpuRby.js +0 -37
- package/resources/fe-dist/assets/channel-DbG6qNF6.js +0 -2
- package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-rB1DgxOZ.js +0 -2
- package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-rB1DgxOZ.js +0 -2
- package/resources/fe-dist/assets/cose-bilkent-S5V4N54A-BAyMopHS.js +0 -2
- package/resources/fe-dist/assets/ganttDiagram-6RSMTGT7-BM8Y7jwp.js +0 -293
- package/resources/fe-dist/assets/index-cfY8KuW7.js +0 -466
- package/resources/fe-dist/assets/index-vcS0VPIW.css +0 -1
- package/resources/fe-dist/assets/refresh-cw-cfX5JiIH.js +0 -7
- package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-CB5OePKf.js +0 -2
package/dist/runtime/server.js
CHANGED
|
@@ -22550,7 +22550,7 @@ loadEnv();
|
|
|
22550
22550
|
|
|
22551
22551
|
// src/runtime/server.ts
|
|
22552
22552
|
import { existsSync as existsSync8 } from "fs";
|
|
22553
|
-
import { extname as extname2, join as
|
|
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-----",
|
|
@@ -22686,6 +22688,16 @@ var I18N_RESOURCES = {
|
|
|
22686
22688
|
delete: "Delete"
|
|
22687
22689
|
},
|
|
22688
22690
|
terminal: {
|
|
22691
|
+
keyboardBehavior: {
|
|
22692
|
+
title: "Keyboard Behavior",
|
|
22693
|
+
description: "How the page makes room for the on-screen keyboard",
|
|
22694
|
+
modeLift: "Shift page",
|
|
22695
|
+
modeLiftDesc: "Move the whole page up; terminal size stays the same",
|
|
22696
|
+
modeResize: "Resize terminal",
|
|
22697
|
+
modeResizeDesc: "Shrink the terminal to fit above the keyboard (changes the remote window size)",
|
|
22698
|
+
modeFollow: "Follow cursor",
|
|
22699
|
+
modeFollowDesc: "Shift just enough to keep the cursor right above the keyboard; terminal size stays the same"
|
|
22700
|
+
},
|
|
22689
22701
|
initializing: "Initializing terminal...",
|
|
22690
22702
|
connecting: "Connecting to device...",
|
|
22691
22703
|
initFailed: "Terminal initialization failed",
|
|
@@ -22749,7 +22761,17 @@ var I18N_RESOURCES = {
|
|
|
22749
22761
|
general: "General",
|
|
22750
22762
|
devicesAndFiles: "Devices & Files",
|
|
22751
22763
|
notifications: "Notifications",
|
|
22752
|
-
ai: "AI"
|
|
22764
|
+
ai: "AI",
|
|
22765
|
+
terminal: "Terminal"
|
|
22766
|
+
},
|
|
22767
|
+
terminal: {
|
|
22768
|
+
title: "Terminal Settings",
|
|
22769
|
+
description: "Adjust the terminal font size, line height, family, and mobile keyboard avoidance. The font family applies to all monospaced text across the app.",
|
|
22770
|
+
fontSize: "Font Size",
|
|
22771
|
+
lineHeight: "Line Height",
|
|
22772
|
+
fontFamily: "Font",
|
|
22773
|
+
preview: "Preview",
|
|
22774
|
+
savedInBrowser: "These settings are saved in this browser only."
|
|
22753
22775
|
},
|
|
22754
22776
|
deviceManagement: {
|
|
22755
22777
|
title: "Device Management",
|
|
@@ -23382,12 +23404,45 @@ Time: {{time}}`,
|
|
|
23382
23404
|
unknown: "Failed to load"
|
|
23383
23405
|
},
|
|
23384
23406
|
retry: "Retry (collapse then expand)",
|
|
23407
|
+
menu: {
|
|
23408
|
+
copyAbsolute: "Copy absolute path",
|
|
23409
|
+
copyRelative: "Copy relative path",
|
|
23410
|
+
sendToAgent: "Send to Agent",
|
|
23411
|
+
expand: "Expand",
|
|
23412
|
+
collapse: "Collapse",
|
|
23413
|
+
upload: "Upload files here",
|
|
23414
|
+
open: "Open"
|
|
23415
|
+
},
|
|
23416
|
+
copied: "Copied to clipboard",
|
|
23417
|
+
copyFailed: "Copy failed",
|
|
23418
|
+
sendToAgent: {
|
|
23419
|
+
prompt: "Please work with this path: `{{path}}`"
|
|
23420
|
+
},
|
|
23421
|
+
upload: {
|
|
23422
|
+
uploading: "Uploading {{name}}\u2026",
|
|
23423
|
+
success: "Uploaded {{name}}",
|
|
23424
|
+
fail: "Failed to upload {{name}}"
|
|
23425
|
+
},
|
|
23426
|
+
transfer: {
|
|
23427
|
+
legUserToTmex: "Browser \u2192 tmex",
|
|
23428
|
+
legTmexToServer: "tmex \u2192 Server",
|
|
23429
|
+
legServerToTmex: "Server \u2192 tmex",
|
|
23430
|
+
legTmexToUser: "tmex \u2192 Browser",
|
|
23431
|
+
cancel: "Cancel",
|
|
23432
|
+
canceled: "Canceled {{name}}",
|
|
23433
|
+
downloaded: "Downloaded {{name}}",
|
|
23434
|
+
downloadFailed: "Failed to download {{name}}",
|
|
23435
|
+
dragDownloadStarted: "Started downloading {{name}} (handled by browser)",
|
|
23436
|
+
tooLarge: "{{name}} exceeds the size limit ({{max}})"
|
|
23437
|
+
},
|
|
23438
|
+
agentLaunch: {
|
|
23439
|
+
connectFailed: "Failed to connect to the device",
|
|
23440
|
+
windowFailed: "Failed to create a window"
|
|
23441
|
+
},
|
|
23385
23442
|
install: {
|
|
23386
23443
|
button: "Install rsync",
|
|
23387
23444
|
scopeLocal: "server",
|
|
23388
23445
|
scopeRemote: "remote",
|
|
23389
|
-
connectFailed: "Failed to connect to the device",
|
|
23390
|
-
windowFailed: "Failed to create a window",
|
|
23391
23446
|
prompt: "rsync is not installed on {{device}} ({{scope}}). Please install rsync using the appropriate command for this system (e.g. `brew install rsync`, `sudo apt-get install -y rsync`, or `sudo yum install -y rsync`), then reply once it is done."
|
|
23392
23447
|
}
|
|
23393
23448
|
},
|
|
@@ -23487,6 +23542,8 @@ Time: {{time}}`,
|
|
|
23487
23542
|
authKey: "\u79C1\u94A5",
|
|
23488
23543
|
authAgent: "SSH Agent",
|
|
23489
23544
|
authConfigRef: "SSH Config",
|
|
23545
|
+
sshConfigRefPlaceholder: "Host \u522B\u540D\uFF0C\u5982 my-server",
|
|
23546
|
+
sshConfigRefHint: "\u586B ssh config \u91CC\u7684 Host \u522B\u540D\uFF0C\u7ECF `ssh -G` \u89E3\u6790\uFF1B\u4E0D\u662F config \u6587\u4EF6\u8DEF\u5F84\u3002",
|
|
23490
23547
|
password: "\u5BC6\u7801",
|
|
23491
23548
|
privateKey: "\u79C1\u94A5",
|
|
23492
23549
|
privateKeyPlaceholder: "-----BEGIN OPENSSH PRIVATE KEY-----",
|
|
@@ -23514,6 +23571,16 @@ Time: {{time}}`,
|
|
|
23514
23571
|
delete: "\u5220\u9664"
|
|
23515
23572
|
},
|
|
23516
23573
|
terminal: {
|
|
23574
|
+
keyboardBehavior: {
|
|
23575
|
+
title: "\u952E\u76D8\u884C\u4E3A",
|
|
23576
|
+
description: "\u9009\u62E9\u624B\u673A\u952E\u76D8\u5F39\u51FA\u65F6\u9875\u9762\u7684\u907F\u8BA9\u65B9\u5F0F",
|
|
23577
|
+
modeLift: "\u9875\u9762\u5E73\u79FB",
|
|
23578
|
+
modeLiftDesc: "\u952E\u76D8\u5F39\u51FA\u65F6\u6574\u9875\u4E0A\u79FB\uFF0C\u7EC8\u7AEF\u5927\u5C0F\u4E0D\u53D8",
|
|
23579
|
+
modeResize: "\u7EC8\u7AEF\u7F29\u653E",
|
|
23580
|
+
modeResizeDesc: "\u7F29\u5C0F\u7EC8\u7AEF\u94FA\u6EE1\u952E\u76D8\u4E0A\u65B9\u533A\u57DF\uFF08\u4F1A\u8C03\u6574\u8FDC\u7AEF\u7A97\u53E3\u884C\u6570\uFF09",
|
|
23581
|
+
modeFollow: "\u5149\u6807\u5BF9\u9F50",
|
|
23582
|
+
modeFollowDesc: "\u6309\u5149\u6807\u4F4D\u7F6E\u4E0A\u79FB\uFF0C\u5149\u6807\u59CB\u7EC8\u5728\u952E\u76D8\u6B63\u4E0A\u65B9\uFF0C\u7EC8\u7AEF\u5927\u5C0F\u4E0D\u53D8"
|
|
23583
|
+
},
|
|
23517
23584
|
initializing: "\u521D\u59CB\u5316\u7EC8\u7AEF...",
|
|
23518
23585
|
connecting: "\u8FDE\u63A5\u8BBE\u5907...",
|
|
23519
23586
|
initFailed: "\u7EC8\u7AEF\u521D\u59CB\u5316\u5931\u8D25",
|
|
@@ -23574,10 +23641,20 @@ Time: {{time}}`,
|
|
|
23574
23641
|
siteTab: "\u7AD9\u70B9",
|
|
23575
23642
|
notificationsTab: "\u901A\u77E5",
|
|
23576
23643
|
tabGroup: {
|
|
23577
|
-
general: "\u901A\u7528
|
|
23644
|
+
general: "\u901A\u7528",
|
|
23578
23645
|
devicesAndFiles: "\u8BBE\u5907\u4E0E\u6587\u4EF6",
|
|
23579
|
-
notifications: "\u901A\u77E5
|
|
23580
|
-
ai: "AI
|
|
23646
|
+
notifications: "\u901A\u77E5",
|
|
23647
|
+
ai: "AI",
|
|
23648
|
+
terminal: "\u7EC8\u7AEF"
|
|
23649
|
+
},
|
|
23650
|
+
terminal: {
|
|
23651
|
+
title: "\u7EC8\u7AEF\u8BBE\u7F6E",
|
|
23652
|
+
description: "\u8C03\u6574\u7EC8\u7AEF\u7684\u5B57\u53F7\u3001\u884C\u9AD8\u3001\u5B57\u4F53\u4E0E\u624B\u673A\u952E\u76D8\u907F\u8BA9\u884C\u4E3A\u3002\u5B57\u4F53\u9009\u62E9\u5BF9\u5168\u5E94\u7528\u6240\u6709\u7B49\u5BBD\u6587\u672C\u751F\u6548\u3002",
|
|
23653
|
+
fontSize: "\u5B57\u53F7",
|
|
23654
|
+
lineHeight: "\u884C\u9AD8",
|
|
23655
|
+
fontFamily: "\u5B57\u4F53",
|
|
23656
|
+
preview: "\u9884\u89C8",
|
|
23657
|
+
savedInBrowser: "\u8FD9\u4E9B\u8BBE\u7F6E\u4EC5\u4FDD\u5B58\u5728\u5F53\u524D\u6D4F\u89C8\u5668\u4E2D\u3002"
|
|
23581
23658
|
},
|
|
23582
23659
|
deviceManagement: {
|
|
23583
23660
|
title: "\u8BBE\u5907\u7BA1\u7406",
|
|
@@ -24210,12 +24287,45 @@ Bot\uFF1A{{botName}}
|
|
|
24210
24287
|
unknown: "\u52A0\u8F7D\u5931\u8D25"
|
|
24211
24288
|
},
|
|
24212
24289
|
retry: "\u91CD\u8BD5\uFF08\u6536\u8D77\u540E\u518D\u5C55\u5F00\uFF09",
|
|
24290
|
+
menu: {
|
|
24291
|
+
copyAbsolute: "\u590D\u5236\u7EDD\u5BF9\u4F4D\u7F6E",
|
|
24292
|
+
copyRelative: "\u590D\u5236\u76F8\u5BF9\u4F4D\u7F6E",
|
|
24293
|
+
sendToAgent: "\u53D1\u9001\u5230 Agent",
|
|
24294
|
+
expand: "\u5C55\u5F00",
|
|
24295
|
+
collapse: "\u6536\u8D77",
|
|
24296
|
+
upload: "\u4E0A\u4F20\u6587\u4EF6\u5230\u8FD9\u4E2A\u6587\u4EF6\u5939",
|
|
24297
|
+
open: "\u6253\u5F00"
|
|
24298
|
+
},
|
|
24299
|
+
copied: "\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F",
|
|
24300
|
+
copyFailed: "\u590D\u5236\u5931\u8D25",
|
|
24301
|
+
sendToAgent: {
|
|
24302
|
+
prompt: "\u8BF7\u5904\u7406\u8FD9\u4E2A\u8DEF\u5F84\uFF1A`{{path}}`"
|
|
24303
|
+
},
|
|
24304
|
+
upload: {
|
|
24305
|
+
uploading: "\u6B63\u5728\u4E0A\u4F20 {{name}}\u2026",
|
|
24306
|
+
success: "\u5DF2\u4E0A\u4F20 {{name}}",
|
|
24307
|
+
fail: "\u4E0A\u4F20 {{name}} \u5931\u8D25"
|
|
24308
|
+
},
|
|
24309
|
+
transfer: {
|
|
24310
|
+
legUserToTmex: "\u7528\u6237 \u2192 tmex",
|
|
24311
|
+
legTmexToServer: "tmex \u2192 \u670D\u52A1\u5668",
|
|
24312
|
+
legServerToTmex: "\u670D\u52A1\u5668 \u2192 tmex",
|
|
24313
|
+
legTmexToUser: "tmex \u2192 \u7528\u6237",
|
|
24314
|
+
cancel: "\u53D6\u6D88",
|
|
24315
|
+
canceled: "\u5DF2\u53D6\u6D88 {{name}}",
|
|
24316
|
+
downloaded: "\u5DF2\u4E0B\u8F7D {{name}}",
|
|
24317
|
+
downloadFailed: "\u4E0B\u8F7D {{name}} \u5931\u8D25",
|
|
24318
|
+
dragDownloadStarted: "\u5DF2\u5F00\u59CB\u4E0B\u8F7D {{name}}\uFF08\u7531\u6D4F\u89C8\u5668\u63A5\u7BA1\uFF09",
|
|
24319
|
+
tooLarge: "{{name}} \u8D85\u8FC7\u5927\u5C0F\u4E0A\u9650\uFF08{{max}}\uFF09"
|
|
24320
|
+
},
|
|
24321
|
+
agentLaunch: {
|
|
24322
|
+
connectFailed: "\u8FDE\u63A5\u8BBE\u5907\u5931\u8D25",
|
|
24323
|
+
windowFailed: "\u521B\u5EFA\u7A97\u53E3\u5931\u8D25"
|
|
24324
|
+
},
|
|
24213
24325
|
install: {
|
|
24214
24326
|
button: "\u5B89\u88C5 rsync",
|
|
24215
24327
|
scopeLocal: "\u670D\u52A1\u5668",
|
|
24216
24328
|
scopeRemote: "\u8FDC\u7A0B",
|
|
24217
|
-
connectFailed: "\u8FDE\u63A5\u8BBE\u5907\u5931\u8D25",
|
|
24218
|
-
windowFailed: "\u521B\u5EFA\u7A97\u53E3\u5931\u8D25",
|
|
24219
24329
|
prompt: "\u8BBE\u5907 {{device}}\uFF08{{scope}}\uFF09\u4E0A\u672A\u68C0\u6D4B\u5230 rsync\u3002\u8BF7\u6839\u636E\u7CFB\u7EDF\u9009\u62E9\u5408\u9002\u7684\u547D\u4EE4\u5B89\u88C5 rsync\uFF08\u4F8B\u5982 `brew install rsync`\u3001`sudo apt-get install -y rsync` \u6216 `sudo yum install -y rsync`\uFF09\uFF0C\u5B89\u88C5\u5B8C\u6210\u540E\u56DE\u590D\u6211\u3002"
|
|
24220
24330
|
}
|
|
24221
24331
|
},
|
|
@@ -24315,6 +24425,8 @@ Bot\uFF1A{{botName}}
|
|
|
24315
24425
|
authKey: "\u79D8\u5BC6\u9375",
|
|
24316
24426
|
authAgent: "SSH Agent",
|
|
24317
24427
|
authConfigRef: "SSH Config",
|
|
24428
|
+
sshConfigRefPlaceholder: "Host \u30A8\u30A4\u30EA\u30A2\u30B9\uFF08\u4F8B: my-server\uFF09",
|
|
24429
|
+
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",
|
|
24318
24430
|
password: "\u30D1\u30B9\u30EF\u30FC\u30C9",
|
|
24319
24431
|
privateKey: "\u79D8\u5BC6\u9375",
|
|
24320
24432
|
privateKeyPlaceholder: "-----BEGIN OPENSSH PRIVATE KEY-----",
|
|
@@ -24342,6 +24454,16 @@ Bot\uFF1A{{botName}}
|
|
|
24342
24454
|
delete: "\u524A\u9664"
|
|
24343
24455
|
},
|
|
24344
24456
|
terminal: {
|
|
24457
|
+
keyboardBehavior: {
|
|
24458
|
+
title: "\u30AD\u30FC\u30DC\u30FC\u30C9\u52D5\u4F5C",
|
|
24459
|
+
description: "\u30BD\u30D5\u30C8\u30A6\u30A7\u30A2\u30AD\u30FC\u30DC\u30FC\u30C9\u8868\u793A\u6642\u306E\u30DA\u30FC\u30B8\u306E\u56DE\u907F\u65B9\u6CD5",
|
|
24460
|
+
modeLift: "\u30DA\u30FC\u30B8\u79FB\u52D5",
|
|
24461
|
+
modeLiftDesc: "\u30AD\u30FC\u30DC\u30FC\u30C9\u8868\u793A\u6642\u306B\u30DA\u30FC\u30B8\u5168\u4F53\u3092\u4E0A\u306B\u79FB\u52D5\uFF08\u7AEF\u672B\u30B5\u30A4\u30BA\u306F\u4E0D\u5909\uFF09",
|
|
24462
|
+
modeResize: "\u7AEF\u672B\u30EA\u30B5\u30A4\u30BA",
|
|
24463
|
+
modeResizeDesc: "\u30AD\u30FC\u30DC\u30FC\u30C9\u4E0A\u306E\u9818\u57DF\u306B\u5408\u308F\u305B\u3066\u7AEF\u672B\u3092\u7E2E\u5C0F\uFF08\u30EA\u30E2\u30FC\u30C8\u306E\u884C\u6570\u304C\u5909\u308F\u308A\u307E\u3059\uFF09",
|
|
24464
|
+
modeFollow: "\u30AB\u30FC\u30BD\u30EB\u8FFD\u5F93",
|
|
24465
|
+
modeFollowDesc: "\u30AB\u30FC\u30BD\u30EB\u304C\u30AD\u30FC\u30DC\u30FC\u30C9\u306E\u771F\u4E0A\u306B\u6765\u308B\u3088\u3046\u79FB\u52D5\uFF08\u7AEF\u672B\u30B5\u30A4\u30BA\u306F\u4E0D\u5909\uFF09"
|
|
24466
|
+
},
|
|
24345
24467
|
initializing: "\u30BF\u30FC\u30DF\u30CA\u30EB\u3092\u521D\u671F\u5316\u4E2D...",
|
|
24346
24468
|
connecting: "\u30C7\u30D0\u30A4\u30B9\u306B\u63A5\u7D9A\u4E2D...",
|
|
24347
24469
|
initFailed: "\u30BF\u30FC\u30DF\u30CA\u30EB\u306E\u521D\u671F\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
@@ -24402,10 +24524,20 @@ Bot\uFF1A{{botName}}
|
|
|
24402
24524
|
siteTab: "\u30B5\u30A4\u30C8",
|
|
24403
24525
|
notificationsTab: "\u901A\u77E5",
|
|
24404
24526
|
tabGroup: {
|
|
24405
|
-
general: "\u4E00\u822C
|
|
24527
|
+
general: "\u4E00\u822C",
|
|
24406
24528
|
devicesAndFiles: "\u30C7\u30D0\u30A4\u30B9\u3068\u30D5\u30A1\u30A4\u30EB",
|
|
24407
|
-
notifications: "\u901A\u77E5
|
|
24408
|
-
ai: "AI
|
|
24529
|
+
notifications: "\u901A\u77E5",
|
|
24530
|
+
ai: "AI",
|
|
24531
|
+
terminal: "\u30BF\u30FC\u30DF\u30CA\u30EB"
|
|
24532
|
+
},
|
|
24533
|
+
terminal: {
|
|
24534
|
+
title: "\u30BF\u30FC\u30DF\u30CA\u30EB\u8A2D\u5B9A",
|
|
24535
|
+
description: "\u30BF\u30FC\u30DF\u30CA\u30EB\u306E\u30D5\u30A9\u30F3\u30C8\u30B5\u30A4\u30BA\u30FB\u884C\u306E\u9AD8\u3055\u30FB\u30D5\u30A9\u30F3\u30C8\u3001\u30E2\u30D0\u30A4\u30EB\u306E\u30AD\u30FC\u30DC\u30FC\u30C9\u56DE\u907F\u3092\u8ABF\u6574\u3057\u307E\u3059\u3002\u30D5\u30A9\u30F3\u30C8\u306E\u9078\u629E\u306F\u30A2\u30D7\u30EA\u5168\u4F53\u306E\u7B49\u5E45\u30C6\u30AD\u30B9\u30C8\u306B\u9069\u7528\u3055\u308C\u307E\u3059\u3002",
|
|
24536
|
+
fontSize: "\u30D5\u30A9\u30F3\u30C8\u30B5\u30A4\u30BA",
|
|
24537
|
+
lineHeight: "\u884C\u306E\u9AD8\u3055",
|
|
24538
|
+
fontFamily: "\u30D5\u30A9\u30F3\u30C8",
|
|
24539
|
+
preview: "\u30D7\u30EC\u30D3\u30E5\u30FC",
|
|
24540
|
+
savedInBrowser: "\u3053\u308C\u3089\u306E\u8A2D\u5B9A\u306F\u3053\u306E\u30D6\u30E9\u30A6\u30B6\u306B\u306E\u307F\u4FDD\u5B58\u3055\u308C\u307E\u3059\u3002"
|
|
24409
24541
|
},
|
|
24410
24542
|
deviceManagement: {
|
|
24411
24543
|
title: "\u30C7\u30D0\u30A4\u30B9\u7BA1\u7406",
|
|
@@ -25038,12 +25170,45 @@ Bot\uFF1A{{botName}}
|
|
|
25038
25170
|
unknown: "\u8AAD\u307F\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
|
|
25039
25171
|
},
|
|
25040
25172
|
retry: "\u518D\u8A66\u884C\uFF08\u6298\u308A\u305F\u305F\u3093\u3067\u518D\u5C55\u958B\uFF09",
|
|
25173
|
+
menu: {
|
|
25174
|
+
copyAbsolute: "\u7D76\u5BFE\u30D1\u30B9\u3092\u30B3\u30D4\u30FC",
|
|
25175
|
+
copyRelative: "\u76F8\u5BFE\u30D1\u30B9\u3092\u30B3\u30D4\u30FC",
|
|
25176
|
+
sendToAgent: "Agent \u306B\u9001\u308B",
|
|
25177
|
+
expand: "\u5C55\u958B",
|
|
25178
|
+
collapse: "\u6298\u308A\u305F\u305F\u3080",
|
|
25179
|
+
upload: "\u3053\u306E\u30D5\u30A9\u30EB\u30C0\u306B\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9",
|
|
25180
|
+
open: "\u958B\u304F"
|
|
25181
|
+
},
|
|
25182
|
+
copied: "\u30AF\u30EA\u30C3\u30D7\u30DC\u30FC\u30C9\u306B\u30B3\u30D4\u30FC\u3057\u307E\u3057\u305F",
|
|
25183
|
+
copyFailed: "\u30B3\u30D4\u30FC\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25184
|
+
sendToAgent: {
|
|
25185
|
+
prompt: "\u3053\u306E\u30D1\u30B9\u3092\u51E6\u7406\u3057\u3066\u304F\u3060\u3055\u3044\uFF1A`{{path}}`"
|
|
25186
|
+
},
|
|
25187
|
+
upload: {
|
|
25188
|
+
uploading: "{{name}} \u3092\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u4E2D\u2026",
|
|
25189
|
+
success: "{{name}} \u3092\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u3057\u307E\u3057\u305F",
|
|
25190
|
+
fail: "{{name}} \u306E\u30A2\u30C3\u30D7\u30ED\u30FC\u30C9\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
|
|
25191
|
+
},
|
|
25192
|
+
transfer: {
|
|
25193
|
+
legUserToTmex: "\u30D6\u30E9\u30A6\u30B6 \u2192 tmex",
|
|
25194
|
+
legTmexToServer: "tmex \u2192 \u30B5\u30FC\u30D0\u30FC",
|
|
25195
|
+
legServerToTmex: "\u30B5\u30FC\u30D0\u30FC \u2192 tmex",
|
|
25196
|
+
legTmexToUser: "tmex \u2192 \u30D6\u30E9\u30A6\u30B6",
|
|
25197
|
+
cancel: "\u30AD\u30E3\u30F3\u30BB\u30EB",
|
|
25198
|
+
canceled: "{{name}} \u3092\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u307E\u3057\u305F",
|
|
25199
|
+
downloaded: "{{name}} \u3092\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u3057\u307E\u3057\u305F",
|
|
25200
|
+
downloadFailed: "{{name}} \u306E\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25201
|
+
dragDownloadStarted: "{{name}} \u306E\u30C0\u30A6\u30F3\u30ED\u30FC\u30C9\u3092\u958B\u59CB\uFF08\u30D6\u30E9\u30A6\u30B6\u304C\u51E6\u7406\uFF09",
|
|
25202
|
+
tooLarge: "{{name}} \u306F\u30B5\u30A4\u30BA\u4E0A\u9650\uFF08{{max}}\uFF09\u3092\u8D85\u3048\u3066\u3044\u307E\u3059"
|
|
25203
|
+
},
|
|
25204
|
+
agentLaunch: {
|
|
25205
|
+
connectFailed: "\u30C7\u30D0\u30A4\u30B9\u3078\u306E\u63A5\u7D9A\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25206
|
+
windowFailed: "\u30A6\u30A3\u30F3\u30C9\u30A6\u306E\u4F5C\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
|
|
25207
|
+
},
|
|
25041
25208
|
install: {
|
|
25042
25209
|
button: "rsync \u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB",
|
|
25043
25210
|
scopeLocal: "\u30B5\u30FC\u30D0\u30FC",
|
|
25044
25211
|
scopeRemote: "\u30EA\u30E2\u30FC\u30C8",
|
|
25045
|
-
connectFailed: "\u30C7\u30D0\u30A4\u30B9\u3078\u306E\u63A5\u7D9A\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25046
|
-
windowFailed: "\u30A6\u30A3\u30F3\u30C9\u30A6\u306E\u4F5C\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25047
25212
|
prompt: "{{device}}\uFF08{{scope}}\uFF09\u306B rsync \u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u30B7\u30B9\u30C6\u30E0\u306B\u5408\u308F\u305B\u305F\u65B9\u6CD5\u3067 rsync \u3092\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB\u3057\uFF08\u4F8B: `brew install rsync`\u3001`sudo apt-get install -y rsync`\u3001`sudo yum install -y rsync`\uFF09\u3001\u5B8C\u4E86\u3057\u305F\u3089\u8FD4\u4FE1\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
25048
25213
|
}
|
|
25049
25214
|
},
|
|
@@ -27583,6 +27748,7 @@ var config = {
|
|
|
27583
27748
|
baseUrl: getEnv("TMEX_BASE_URL", "http://127.0.0.1:8085"),
|
|
27584
27749
|
siteNameDefault: getEnv("TMEX_SITE_NAME", "tmex"),
|
|
27585
27750
|
databaseUrl: getEnv("DATABASE_URL", "./tmex.db"),
|
|
27751
|
+
transferMaxBytes: Number.parseInt(getEnv("TMEX_TRANSFER_MAX_BYTES", "2147483648"), 10),
|
|
27586
27752
|
bellThrottleSecondsDefault: Number.parseInt(getEnv("TMEX_BELL_THROTTLE_SECONDS", "6"), 10),
|
|
27587
27753
|
notificationThrottleSecondsDefault: Number.parseInt(getEnv("TMEX_NOTIFICATION_THROTTLE_SECONDS", "3"), 10),
|
|
27588
27754
|
tmuxAllowPassthrough: getBooleanEnv("TMEX_TMUX_ALLOW_PASSTHROUGH", false),
|
|
@@ -86753,9 +86919,6 @@ function normalizeHttpUrl(input) {
|
|
|
86753
86919
|
return null;
|
|
86754
86920
|
}
|
|
86755
86921
|
}
|
|
86756
|
-
function encodePercentForTelegramUrl(url2) {
|
|
86757
|
-
return url2.replace(/%/g, "%25");
|
|
86758
|
-
}
|
|
86759
86922
|
|
|
86760
86923
|
class EventNotifier {
|
|
86761
86924
|
webhooks = [];
|
|
@@ -86899,8 +87062,7 @@ class EventNotifier {
|
|
|
86899
87062
|
lines.push(...this.buildPaneMetaLines(event));
|
|
86900
87063
|
const paneUrl = normalizeHttpUrl(buildPaneUrl(event));
|
|
86901
87064
|
if (paneUrl) {
|
|
86902
|
-
|
|
86903
|
-
lines.push("", `<a href="${escapeTelegramHtmlAttribute(tgSafePaneUrl)}">${escapeTelegramHtmlText(t2("notification.telegramBell.viewLink"))}</a>`);
|
|
87065
|
+
lines.push("", `<a href="${escapeTelegramHtmlAttribute(paneUrl)}">${escapeTelegramHtmlText(t2("notification.telegramBell.viewLink"))}</a>`);
|
|
86904
87066
|
}
|
|
86905
87067
|
return lines.join(`
|
|
86906
87068
|
`);
|
|
@@ -86920,8 +87082,7 @@ class EventNotifier {
|
|
|
86920
87082
|
const topbarLabel = this.buildTerminalTopbarLabel(event);
|
|
86921
87083
|
const footer = `from ${event.site.name}: ${topbarLabel}`;
|
|
86922
87084
|
if (paneUrl) {
|
|
86923
|
-
|
|
86924
|
-
lines.push("", `<a href="${escapeTelegramHtmlAttribute(tgSafePaneUrl)}">${escapeTelegramHtmlText(footer)}</a>`);
|
|
87085
|
+
lines.push("", `<a href="${escapeTelegramHtmlAttribute(paneUrl)}">${escapeTelegramHtmlText(footer)}</a>`);
|
|
86925
87086
|
} else {
|
|
86926
87087
|
lines.push("", escapeTelegramHtmlText(footer));
|
|
86927
87088
|
}
|
|
@@ -86961,8 +87122,7 @@ class EventNotifier {
|
|
|
86961
87122
|
}
|
|
86962
87123
|
const paneUrl = normalizeHttpUrl(buildPaneUrl(event));
|
|
86963
87124
|
if (paneUrl) {
|
|
86964
|
-
|
|
86965
|
-
lines.push("", `<a href="${escapeTelegramHtmlAttribute(tgSafePaneUrl)}">${esc2(t2("notification.directLink"))}</a>`);
|
|
87125
|
+
lines.push("", `<a href="${escapeTelegramHtmlAttribute(paneUrl)}">${esc2(t2("notification.directLink"))}</a>`);
|
|
86966
87126
|
}
|
|
86967
87127
|
return lines.join(`
|
|
86968
87128
|
`);
|
|
@@ -95883,10 +96043,12 @@ class ConnectionAlertNotifier {
|
|
|
95883
96043
|
const classified = classifySshError(errObj);
|
|
95884
96044
|
const friendlyMessage = t2(classified.messageKey, { ...classified.messageParams });
|
|
95885
96045
|
const rawMessage = errObj.message;
|
|
96046
|
+
const persistedMessage = rawMessage && !friendlyMessage.includes(rawMessage) ? `${friendlyMessage}
|
|
96047
|
+
${rawMessage}` : friendlyMessage;
|
|
95886
96048
|
console.error(`[conn-alert] device ${device.id} (${device.name}) source=${source} type=${classified.type}: ${rawMessage}`);
|
|
95887
96049
|
if (persist) {
|
|
95888
96050
|
try {
|
|
95889
|
-
this.persister(device.id,
|
|
96051
|
+
this.persister(device.id, persistedMessage, classified.type);
|
|
95890
96052
|
} catch (dbErr) {
|
|
95891
96053
|
console.error("[conn-alert] failed to persist runtime status:", dbErr);
|
|
95892
96054
|
}
|
|
@@ -98251,7 +98413,7 @@ function resolveSshConfigRef(device, deps) {
|
|
|
98251
98413
|
return parseSshConfigOutput(result.stdout, deps.env);
|
|
98252
98414
|
}
|
|
98253
98415
|
function resolveImplicitIdentityFilesForAgentAuth(device, host, port, username, deps) {
|
|
98254
|
-
if (device.authMode !== "agent"
|
|
98416
|
+
if (device.authMode !== "agent") {
|
|
98255
98417
|
return [];
|
|
98256
98418
|
}
|
|
98257
98419
|
const target = username ? `${username}@${host}` : host;
|
|
@@ -98273,7 +98435,7 @@ async function resolveSshConnectConfig(device, decrypt2, inputDeps = {}) {
|
|
|
98273
98435
|
readTextFile: inputDeps.readTextFile ?? ((path) => readFileSync2(path, "utf8"))
|
|
98274
98436
|
};
|
|
98275
98437
|
const sshEnv = toSshAuthEnv(deps.env);
|
|
98276
|
-
const resolvedConfig = resolveSshConfigRef(device, deps);
|
|
98438
|
+
const resolvedConfig = device.authMode === "configRef" ? resolveSshConfigRef(device, deps) : null;
|
|
98277
98439
|
const host = resolvedConfig?.host ?? device.host;
|
|
98278
98440
|
const port = resolvedConfig?.port ?? device.port ?? 22;
|
|
98279
98441
|
const username = resolvedConfig?.username ?? resolveSshUsername(device.username, device.authMode, sshEnv);
|
|
@@ -102871,7 +103033,7 @@ function deleteFileRoot(id) {
|
|
|
102871
103033
|
}
|
|
102872
103034
|
|
|
102873
103035
|
// ../../apps/gateway/src/files/device-storage.ts
|
|
102874
|
-
import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync3, realpathSync, rmSync as rmSync2 } from "fs";
|
|
103036
|
+
import { mkdtempSync as mkdtempSync2, readFileSync as readFileSync3, realpathSync, rmSync as rmSync2, statSync } from "fs";
|
|
102875
103037
|
import { tmpdir as tmpdir2 } from "os";
|
|
102876
103038
|
import { join as join5 } from "path";
|
|
102877
103039
|
|
|
@@ -103106,6 +103268,18 @@ function enqueueDeviceJob(deviceId, job) {
|
|
|
103106
103268
|
}
|
|
103107
103269
|
|
|
103108
103270
|
// ../../apps/gateway/src/files/rsync.ts
|
|
103271
|
+
var PROGRESS_RE = /^\s*([\d,]+)\s+(\d+)%\s+([\d.]+[KMGT]?B\/s)/;
|
|
103272
|
+
function parseRsyncProgress(line) {
|
|
103273
|
+
const m = PROGRESS_RE.exec(line);
|
|
103274
|
+
if (!m)
|
|
103275
|
+
return null;
|
|
103276
|
+
const transferred = Number.parseInt(m[1].replace(/,/g, ""), 10);
|
|
103277
|
+
const pct = Number.parseInt(m[2], 10);
|
|
103278
|
+
if (Number.isNaN(transferred) || Number.isNaN(pct))
|
|
103279
|
+
return null;
|
|
103280
|
+
return { transferred, pct, rate: m[3] };
|
|
103281
|
+
}
|
|
103282
|
+
|
|
103109
103283
|
class RsyncMissingLocalError extends Error {
|
|
103110
103284
|
constructor(message) {
|
|
103111
103285
|
super(message);
|
|
@@ -103127,8 +103301,36 @@ function baseSubprocessEnv() {
|
|
|
103127
103301
|
out.LC_ALL = "C";
|
|
103128
103302
|
return out;
|
|
103129
103303
|
}
|
|
103304
|
+
async function readStdoutWithProgress(stream, onProgress, resetIdle) {
|
|
103305
|
+
const reader = stream.getReader();
|
|
103306
|
+
const decoder4 = new TextDecoder;
|
|
103307
|
+
let full = "";
|
|
103308
|
+
let buf = "";
|
|
103309
|
+
for (;; ) {
|
|
103310
|
+
const { done, value } = await reader.read();
|
|
103311
|
+
if (done)
|
|
103312
|
+
break;
|
|
103313
|
+
resetIdle();
|
|
103314
|
+
const text3 = decoder4.decode(value, { stream: true });
|
|
103315
|
+
full += text3;
|
|
103316
|
+
buf += text3;
|
|
103317
|
+
const parts = buf.split(/[\r\n]/);
|
|
103318
|
+
buf = parts.pop() ?? "";
|
|
103319
|
+
for (const line of parts) {
|
|
103320
|
+
const p = parseRsyncProgress(line);
|
|
103321
|
+
if (p)
|
|
103322
|
+
onProgress(p);
|
|
103323
|
+
}
|
|
103324
|
+
}
|
|
103325
|
+
const tail = parseRsyncProgress(buf);
|
|
103326
|
+
if (tail)
|
|
103327
|
+
onProgress(tail);
|
|
103328
|
+
return full;
|
|
103329
|
+
}
|
|
103130
103330
|
async function runRsync(argv, opts = {}) {
|
|
103331
|
+
const streaming = typeof opts.onProgress === "function";
|
|
103131
103332
|
const timeoutMs = opts.timeoutMs ?? 20000;
|
|
103333
|
+
const idleTimeoutMs = opts.idleTimeoutMs ?? 120000;
|
|
103132
103334
|
let proc;
|
|
103133
103335
|
try {
|
|
103134
103336
|
proc = Bun.spawn(["rsync", ...argv], {
|
|
@@ -103141,21 +103343,36 @@ async function runRsync(argv, opts = {}) {
|
|
|
103141
103343
|
throw new RsyncMissingLocalError(error51 instanceof Error ? error51.message : String(error51));
|
|
103142
103344
|
}
|
|
103143
103345
|
let timedOut = false;
|
|
103144
|
-
const
|
|
103145
|
-
timedOut = true;
|
|
103146
|
-
try {
|
|
103147
|
-
proc.kill();
|
|
103148
|
-
} catch {}
|
|
103149
|
-
}, timeoutMs);
|
|
103150
|
-
const onAbort = () => {
|
|
103346
|
+
const killProc = () => {
|
|
103151
103347
|
try {
|
|
103152
103348
|
proc.kill();
|
|
103153
103349
|
} catch {}
|
|
103154
103350
|
};
|
|
103351
|
+
let killTimer;
|
|
103352
|
+
const armFixed = () => {
|
|
103353
|
+
killTimer = setTimeout(() => {
|
|
103354
|
+
timedOut = true;
|
|
103355
|
+
killProc();
|
|
103356
|
+
}, timeoutMs);
|
|
103357
|
+
};
|
|
103358
|
+
const resetIdle = () => {
|
|
103359
|
+
if (killTimer)
|
|
103360
|
+
clearTimeout(killTimer);
|
|
103361
|
+
killTimer = setTimeout(() => {
|
|
103362
|
+
timedOut = true;
|
|
103363
|
+
killProc();
|
|
103364
|
+
}, idleTimeoutMs);
|
|
103365
|
+
};
|
|
103366
|
+
if (streaming)
|
|
103367
|
+
resetIdle();
|
|
103368
|
+
else
|
|
103369
|
+
armFixed();
|
|
103370
|
+
const onAbort = () => killProc();
|
|
103155
103371
|
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
103156
103372
|
try {
|
|
103373
|
+
const stdoutPromise = streaming ? readStdoutWithProgress(proc.stdout, opts.onProgress, resetIdle) : new Response(proc.stdout).text();
|
|
103157
103374
|
const [stdout, stderr, exitCode] = await Promise.all([
|
|
103158
|
-
|
|
103375
|
+
stdoutPromise,
|
|
103159
103376
|
new Response(proc.stderr).text(),
|
|
103160
103377
|
proc.exited
|
|
103161
103378
|
]);
|
|
@@ -103165,7 +103382,8 @@ async function runRsync(argv, opts = {}) {
|
|
|
103165
103382
|
}
|
|
103166
103383
|
return { stdout, stderr, exitCode };
|
|
103167
103384
|
} finally {
|
|
103168
|
-
|
|
103385
|
+
if (killTimer)
|
|
103386
|
+
clearTimeout(killTimer);
|
|
103169
103387
|
opts.signal?.removeEventListener("abort", onAbort);
|
|
103170
103388
|
}
|
|
103171
103389
|
}
|
|
@@ -103347,17 +103565,25 @@ function rsyncListArgs(spec, remotePath) {
|
|
|
103347
103565
|
return args;
|
|
103348
103566
|
}
|
|
103349
103567
|
function rsyncCopyArgs(spec, remotePath, dest) {
|
|
103350
|
-
const args = ["-L"];
|
|
103568
|
+
const args = ["-L", "--progress"];
|
|
103351
103569
|
if (spec.rsh)
|
|
103352
103570
|
args.push("-e", spec.rsh);
|
|
103353
103571
|
args.push(rsyncTargetArg(spec, remotePath), dest);
|
|
103354
103572
|
return args;
|
|
103355
103573
|
}
|
|
103574
|
+
function rsyncUploadArgs(spec, localSource, remoteDest) {
|
|
103575
|
+
const args = ["--progress"];
|
|
103576
|
+
if (spec.rsh)
|
|
103577
|
+
args.push("-e", spec.rsh);
|
|
103578
|
+
args.push(localSource, rsyncTargetArg(spec, remoteDest));
|
|
103579
|
+
return args;
|
|
103580
|
+
}
|
|
103356
103581
|
|
|
103357
103582
|
// ../../apps/gateway/src/files/device-storage.ts
|
|
103358
103583
|
var RAW_MAX_BYTES = 50 * 1024 * 1024;
|
|
103359
103584
|
var LIST_TIMEOUT_MS = 20000;
|
|
103360
103585
|
var COPY_TIMEOUT_MS = 60000;
|
|
103586
|
+
var TRANSFER_IDLE_TIMEOUT_MS = 120000;
|
|
103361
103587
|
function ok(data) {
|
|
103362
103588
|
return { ok: true, data };
|
|
103363
103589
|
}
|
|
@@ -103667,8 +103893,241 @@ async function readRawFile(rootId, inputPath) {
|
|
|
103667
103893
|
}
|
|
103668
103894
|
});
|
|
103669
103895
|
}
|
|
103896
|
+
function sanitizeUploadName(raw) {
|
|
103897
|
+
const base = raw.split("/").pop() ?? "";
|
|
103898
|
+
if (base === "" || base === "." || base === "..")
|
|
103899
|
+
return null;
|
|
103900
|
+
if (base.includes("/") || base.includes("\\") || base.includes("\x00"))
|
|
103901
|
+
return null;
|
|
103902
|
+
return base;
|
|
103903
|
+
}
|
|
103904
|
+
async function pushFileToDevice(rootId, destDir, srcPath, name24, opts = {}) {
|
|
103905
|
+
const safeName = sanitizeUploadName(name24);
|
|
103906
|
+
if (!safeName)
|
|
103907
|
+
return fail("invalid");
|
|
103908
|
+
const r = resolveContext(rootId);
|
|
103909
|
+
if (!r.ok)
|
|
103910
|
+
return fail(r.code);
|
|
103911
|
+
const { root, device } = r.ctx;
|
|
103912
|
+
const norm = checkAndNormalize(device, root.path, destDir);
|
|
103913
|
+
if (!norm.ok)
|
|
103914
|
+
return fail(norm.code);
|
|
103915
|
+
return enqueueDeviceJob(device.id, async () => {
|
|
103916
|
+
let spec;
|
|
103917
|
+
try {
|
|
103918
|
+
spec = await buildRsyncDeviceSpec(device);
|
|
103919
|
+
} catch (error51) {
|
|
103920
|
+
if (error51 instanceof RsyncAuthError)
|
|
103921
|
+
return fail(error51.code, error51.message);
|
|
103922
|
+
throw error51;
|
|
103923
|
+
}
|
|
103924
|
+
try {
|
|
103925
|
+
const destStat = await statViaRsync(spec, norm.path);
|
|
103926
|
+
if (!destStat.ok)
|
|
103927
|
+
return destStat;
|
|
103928
|
+
if (destStat.data.type !== "dir")
|
|
103929
|
+
return fail("not_a_directory");
|
|
103930
|
+
const remoteDest = posixJoin(norm.path, safeName);
|
|
103931
|
+
let res;
|
|
103932
|
+
try {
|
|
103933
|
+
res = await runRsync(rsyncUploadArgs(spec, srcPath, remoteDest), {
|
|
103934
|
+
env: spec.env,
|
|
103935
|
+
onProgress: opts.onProgress,
|
|
103936
|
+
idleTimeoutMs: TRANSFER_IDLE_TIMEOUT_MS,
|
|
103937
|
+
signal: opts.signal
|
|
103938
|
+
});
|
|
103939
|
+
} catch (error51) {
|
|
103940
|
+
if (error51 instanceof RsyncMissingLocalError)
|
|
103941
|
+
return fail("rsync_missing_local");
|
|
103942
|
+
throw error51;
|
|
103943
|
+
}
|
|
103944
|
+
if (res.exitCode !== 0)
|
|
103945
|
+
return fail(classifyRsyncFailure(res.exitCode, res.stderr), res.stderr);
|
|
103946
|
+
return ok({ uploaded: safeName });
|
|
103947
|
+
} finally {
|
|
103948
|
+
spec.cleanup();
|
|
103949
|
+
}
|
|
103950
|
+
});
|
|
103951
|
+
}
|
|
103952
|
+
async function pullFileFromDevice(rootId, inputPath, opts = {}) {
|
|
103953
|
+
const r = resolveContext(rootId);
|
|
103954
|
+
if (!r.ok)
|
|
103955
|
+
return fail(r.code);
|
|
103956
|
+
const { root, device } = r.ctx;
|
|
103957
|
+
const norm = checkAndNormalize(device, root.path, inputPath);
|
|
103958
|
+
if (!norm.ok)
|
|
103959
|
+
return fail(norm.code);
|
|
103960
|
+
return enqueueDeviceJob(device.id, async () => {
|
|
103961
|
+
let spec;
|
|
103962
|
+
try {
|
|
103963
|
+
spec = await buildRsyncDeviceSpec(device);
|
|
103964
|
+
} catch (error51) {
|
|
103965
|
+
if (error51 instanceof RsyncAuthError)
|
|
103966
|
+
return fail(error51.code, error51.message);
|
|
103967
|
+
throw error51;
|
|
103968
|
+
}
|
|
103969
|
+
try {
|
|
103970
|
+
const st = await statViaRsync(spec, norm.path);
|
|
103971
|
+
if (!st.ok)
|
|
103972
|
+
return st;
|
|
103973
|
+
if (st.data.type === "dir")
|
|
103974
|
+
return fail("is_directory");
|
|
103975
|
+
if (st.data.size != null && st.data.size > config.transferMaxBytes)
|
|
103976
|
+
return fail("too_large");
|
|
103977
|
+
const dir2 = mkdtempSync2(join5(tmpdir2(), "tmex-dl-"));
|
|
103978
|
+
const dest = join5(dir2, "f");
|
|
103979
|
+
const cleanup = () => {
|
|
103980
|
+
try {
|
|
103981
|
+
rmSync2(dir2, { recursive: true, force: true });
|
|
103982
|
+
} catch {}
|
|
103983
|
+
};
|
|
103984
|
+
let res;
|
|
103985
|
+
try {
|
|
103986
|
+
res = await runRsync(rsyncCopyArgs(spec, norm.path, dest), {
|
|
103987
|
+
env: spec.env,
|
|
103988
|
+
onProgress: opts.onProgress,
|
|
103989
|
+
idleTimeoutMs: TRANSFER_IDLE_TIMEOUT_MS,
|
|
103990
|
+
signal: opts.signal
|
|
103991
|
+
});
|
|
103992
|
+
} catch (error51) {
|
|
103993
|
+
cleanup();
|
|
103994
|
+
if (error51 instanceof RsyncMissingLocalError)
|
|
103995
|
+
return fail("rsync_missing_local");
|
|
103996
|
+
throw error51;
|
|
103997
|
+
}
|
|
103998
|
+
if (res.exitCode !== 0) {
|
|
103999
|
+
cleanup();
|
|
104000
|
+
return fail(classifyRsyncFailure(res.exitCode, res.stderr), res.stderr);
|
|
104001
|
+
}
|
|
104002
|
+
const name24 = posixBasename(norm.path);
|
|
104003
|
+
let size = st.data.size ?? 0;
|
|
104004
|
+
try {
|
|
104005
|
+
size = statSync(dest).size;
|
|
104006
|
+
} catch {}
|
|
104007
|
+
if (size > config.transferMaxBytes) {
|
|
104008
|
+
cleanup();
|
|
104009
|
+
return fail("too_large");
|
|
104010
|
+
}
|
|
104011
|
+
return ok({ tmpPath: dest, size, name: name24, mime: mimeOf(name24), cleanup });
|
|
104012
|
+
} finally {
|
|
104013
|
+
spec.cleanup();
|
|
104014
|
+
}
|
|
104015
|
+
});
|
|
104016
|
+
}
|
|
104017
|
+
|
|
104018
|
+
// ../../apps/gateway/src/files/transfer-session.ts
|
|
104019
|
+
import { appendFileSync, mkdtempSync as mkdtempSync3, readdirSync, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
104020
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
104021
|
+
import { join as join6 } from "path";
|
|
104022
|
+
var sessions = new Map;
|
|
104023
|
+
var SESSION_TTL_MS = 30 * 60000;
|
|
104024
|
+
var downloads = new Map;
|
|
104025
|
+
function sweepStale(now2) {
|
|
104026
|
+
for (const [id, s] of sessions) {
|
|
104027
|
+
if (!s.committing && now2 - s.createdAt > SESSION_TTL_MS) {
|
|
104028
|
+
removeUploadSession(id);
|
|
104029
|
+
}
|
|
104030
|
+
}
|
|
104031
|
+
for (const [id, d] of downloads) {
|
|
104032
|
+
if (now2 - d.createdAt > SESSION_TTL_MS) {
|
|
104033
|
+
removeDownloadSession(id);
|
|
104034
|
+
}
|
|
104035
|
+
}
|
|
104036
|
+
}
|
|
104037
|
+
function createDownloadSession(data) {
|
|
104038
|
+
sweepStale(Date.now());
|
|
104039
|
+
const session = { id: crypto.randomUUID(), createdAt: Date.now(), ...data };
|
|
104040
|
+
downloads.set(session.id, session);
|
|
104041
|
+
return session;
|
|
104042
|
+
}
|
|
104043
|
+
function getDownloadSession(id) {
|
|
104044
|
+
return downloads.get(id);
|
|
104045
|
+
}
|
|
104046
|
+
function removeDownloadSession(id) {
|
|
104047
|
+
const d = downloads.get(id);
|
|
104048
|
+
if (!d)
|
|
104049
|
+
return;
|
|
104050
|
+
downloads.delete(id);
|
|
104051
|
+
try {
|
|
104052
|
+
d.cleanup();
|
|
104053
|
+
} catch {}
|
|
104054
|
+
}
|
|
104055
|
+
function createUploadSession(args) {
|
|
104056
|
+
const now2 = Date.now();
|
|
104057
|
+
sweepStale(now2);
|
|
104058
|
+
const tmpDir = mkdtempSync3(join6(tmpdir3(), "tmex-up-"));
|
|
104059
|
+
const tmpPath = join6(tmpDir, "f");
|
|
104060
|
+
writeFileSync2(tmpPath, new Uint8Array(0));
|
|
104061
|
+
const session = {
|
|
104062
|
+
id: crypto.randomUUID(),
|
|
104063
|
+
rootId: args.rootId,
|
|
104064
|
+
destDir: args.destDir,
|
|
104065
|
+
name: args.name,
|
|
104066
|
+
size: args.size,
|
|
104067
|
+
received: 0,
|
|
104068
|
+
tmpDir,
|
|
104069
|
+
tmpPath,
|
|
104070
|
+
abort: new AbortController,
|
|
104071
|
+
createdAt: now2,
|
|
104072
|
+
committing: false
|
|
104073
|
+
};
|
|
104074
|
+
sessions.set(session.id, session);
|
|
104075
|
+
return session;
|
|
104076
|
+
}
|
|
104077
|
+
function getUploadSession(id) {
|
|
104078
|
+
return sessions.get(id);
|
|
104079
|
+
}
|
|
104080
|
+
function appendUploadChunk(id, offset, bytes) {
|
|
104081
|
+
const s = sessions.get(id);
|
|
104082
|
+
if (!s)
|
|
104083
|
+
return { ok: false, reason: "not_found" };
|
|
104084
|
+
if (offset !== s.received)
|
|
104085
|
+
return { ok: false, reason: "bad_offset" };
|
|
104086
|
+
if (s.received + bytes.byteLength > s.size)
|
|
104087
|
+
return { ok: false, reason: "too_large" };
|
|
104088
|
+
appendFileSync(s.tmpPath, bytes);
|
|
104089
|
+
s.received += bytes.byteLength;
|
|
104090
|
+
return { ok: true, received: s.received };
|
|
104091
|
+
}
|
|
104092
|
+
function removeUploadSession(id) {
|
|
104093
|
+
const s = sessions.get(id);
|
|
104094
|
+
if (!s)
|
|
104095
|
+
return;
|
|
104096
|
+
sessions.delete(id);
|
|
104097
|
+
try {
|
|
104098
|
+
s.abort.abort();
|
|
104099
|
+
} catch {}
|
|
104100
|
+
try {
|
|
104101
|
+
rmSync3(s.tmpDir, { recursive: true, force: true });
|
|
104102
|
+
} catch {}
|
|
104103
|
+
}
|
|
104104
|
+
var transferGcTimer = setInterval(() => sweepStale(Date.now()), 5 * 60000);
|
|
104105
|
+
transferGcTimer.unref?.();
|
|
104106
|
+
var ORPHAN_PREFIXES = ["tmex-up-", "tmex-dl-"];
|
|
104107
|
+
var ORPHAN_MAX_AGE_MS = 60 * 60000;
|
|
104108
|
+
function sweepOrphanTransferTemps() {
|
|
104109
|
+
const base = tmpdir3();
|
|
104110
|
+
let entries;
|
|
104111
|
+
try {
|
|
104112
|
+
entries = readdirSync(base);
|
|
104113
|
+
} catch {
|
|
104114
|
+
return;
|
|
104115
|
+
}
|
|
104116
|
+
const now2 = Date.now();
|
|
104117
|
+
for (const name24 of entries) {
|
|
104118
|
+
if (!ORPHAN_PREFIXES.some((p) => name24.startsWith(p)))
|
|
104119
|
+
continue;
|
|
104120
|
+
const full = join6(base, name24);
|
|
104121
|
+
try {
|
|
104122
|
+
if (now2 - statSync2(full).mtimeMs > ORPHAN_MAX_AGE_MS) {
|
|
104123
|
+
rmSync3(full, { recursive: true, force: true });
|
|
104124
|
+
}
|
|
104125
|
+
} catch {}
|
|
104126
|
+
}
|
|
104127
|
+
}
|
|
103670
104128
|
|
|
103671
104129
|
// ../../apps/gateway/src/api/files.ts
|
|
104130
|
+
var UPLOAD_CHUNK_SIZE = 8 * 1024 * 1024;
|
|
103672
104131
|
function json4(data, status = 200, headers = {}) {
|
|
103673
104132
|
return new Response(JSON.stringify(data), {
|
|
103674
104133
|
status,
|
|
@@ -103832,6 +104291,223 @@ async function handleRaw(url2) {
|
|
|
103832
104291
|
}
|
|
103833
104292
|
return new Response(result.data.data, { status: 200, headers });
|
|
103834
104293
|
}
|
|
104294
|
+
async function handleUploadInit(req) {
|
|
104295
|
+
let body;
|
|
104296
|
+
try {
|
|
104297
|
+
body = await req.json();
|
|
104298
|
+
} catch {
|
|
104299
|
+
return json4({ error: t2("apiError.invalidRequest") }, 400);
|
|
104300
|
+
}
|
|
104301
|
+
const rootId = typeof body.rootId === "string" ? body.rootId : "";
|
|
104302
|
+
const destDir = typeof body.path === "string" ? body.path : "";
|
|
104303
|
+
const rawName = typeof body.name === "string" ? body.name : "";
|
|
104304
|
+
const size = typeof body.size === "number" && Number.isFinite(body.size) ? body.size : -1;
|
|
104305
|
+
if (!rootId || !destDir || !rawName || size < 0) {
|
|
104306
|
+
return json4({ error: t2("apiError.invalidRequest") }, 400);
|
|
104307
|
+
}
|
|
104308
|
+
const name24 = sanitizeUploadName(rawName);
|
|
104309
|
+
if (!name24)
|
|
104310
|
+
return codeError("invalid");
|
|
104311
|
+
if (size > config.transferMaxBytes)
|
|
104312
|
+
return codeError("too_large");
|
|
104313
|
+
const stat = await statFile(rootId, destDir);
|
|
104314
|
+
if (!stat.ok)
|
|
104315
|
+
return codeError(stat.code, stat.detail);
|
|
104316
|
+
if (stat.data.type !== "dir")
|
|
104317
|
+
return codeError("not_a_directory");
|
|
104318
|
+
const session = createUploadSession({ rootId, destDir, name: name24, size });
|
|
104319
|
+
return json4({ uploadId: session.id, chunkSize: UPLOAD_CHUNK_SIZE });
|
|
104320
|
+
}
|
|
104321
|
+
async function handleUploadChunk(req, id, url2) {
|
|
104322
|
+
const offset = Number.parseInt(url2.searchParams.get("offset") ?? "", 10);
|
|
104323
|
+
if (Number.isNaN(offset) || offset < 0)
|
|
104324
|
+
return json4({ error: t2("apiError.invalidRequest") }, 400);
|
|
104325
|
+
const bytes = new Uint8Array(await req.arrayBuffer());
|
|
104326
|
+
const res = appendUploadChunk(id, offset, bytes);
|
|
104327
|
+
if (!res.ok) {
|
|
104328
|
+
if (res.reason === "not_found")
|
|
104329
|
+
return codeError("not_found");
|
|
104330
|
+
if (res.reason === "too_large")
|
|
104331
|
+
return codeError("too_large");
|
|
104332
|
+
return json4({ error: t2("apiError.invalidRequest") }, 409);
|
|
104333
|
+
}
|
|
104334
|
+
return json4({ received: res.received });
|
|
104335
|
+
}
|
|
104336
|
+
function handleUploadCommit(id) {
|
|
104337
|
+
const session = getUploadSession(id);
|
|
104338
|
+
if (!session)
|
|
104339
|
+
return codeError("not_found");
|
|
104340
|
+
if (session.received !== session.size)
|
|
104341
|
+
return codeError("invalid", "incomplete upload");
|
|
104342
|
+
session.committing = true;
|
|
104343
|
+
const encoder3 = new TextEncoder;
|
|
104344
|
+
const stream = new ReadableStream({
|
|
104345
|
+
start(controller) {
|
|
104346
|
+
const emit = (obj) => {
|
|
104347
|
+
try {
|
|
104348
|
+
controller.enqueue(encoder3.encode(`${JSON.stringify(obj)}
|
|
104349
|
+
`));
|
|
104350
|
+
} catch {}
|
|
104351
|
+
};
|
|
104352
|
+
pushFileToDevice(session.rootId, session.destDir, session.tmpPath, session.name, {
|
|
104353
|
+
signal: session.abort.signal,
|
|
104354
|
+
onProgress: (p) => emit({ type: "progress", ...p })
|
|
104355
|
+
}).then((res) => {
|
|
104356
|
+
if (res.ok)
|
|
104357
|
+
emit({ type: "done", uploaded: res.data.uploaded });
|
|
104358
|
+
else
|
|
104359
|
+
emit({ type: "error", code: res.code, detail: res.detail });
|
|
104360
|
+
}).catch((e) => emit({ type: "error", code: "unknown", detail: String(e) })).finally(() => {
|
|
104361
|
+
try {
|
|
104362
|
+
controller.close();
|
|
104363
|
+
} catch {}
|
|
104364
|
+
removeUploadSession(id);
|
|
104365
|
+
});
|
|
104366
|
+
},
|
|
104367
|
+
cancel() {
|
|
104368
|
+
removeUploadSession(id);
|
|
104369
|
+
}
|
|
104370
|
+
});
|
|
104371
|
+
return new Response(stream, {
|
|
104372
|
+
status: 200,
|
|
104373
|
+
headers: { "Content-Type": "application/x-ndjson; charset=utf-8", "Cache-Control": "no-store" }
|
|
104374
|
+
});
|
|
104375
|
+
}
|
|
104376
|
+
function handleUploadCancel(id) {
|
|
104377
|
+
removeUploadSession(id);
|
|
104378
|
+
return json4({ success: true });
|
|
104379
|
+
}
|
|
104380
|
+
function attachmentHeaders(name24, mime, size) {
|
|
104381
|
+
const encoded = encodeURIComponent(name24);
|
|
104382
|
+
const ascii = name24.replace(/["\\\r\n]/g, "_");
|
|
104383
|
+
return {
|
|
104384
|
+
"Content-Type": mime ?? "application/octet-stream",
|
|
104385
|
+
"Content-Length": String(size),
|
|
104386
|
+
"Content-Disposition": `attachment; filename="${ascii}"; filename*=UTF-8''${encoded}`,
|
|
104387
|
+
"Cache-Control": "no-store"
|
|
104388
|
+
};
|
|
104389
|
+
}
|
|
104390
|
+
function streamTempFile(tmpPath, cleanupAfter) {
|
|
104391
|
+
let reader;
|
|
104392
|
+
try {
|
|
104393
|
+
reader = Bun.file(tmpPath).stream().getReader();
|
|
104394
|
+
} catch {
|
|
104395
|
+
cleanupAfter();
|
|
104396
|
+
return null;
|
|
104397
|
+
}
|
|
104398
|
+
return new ReadableStream({
|
|
104399
|
+
async pull(controller) {
|
|
104400
|
+
try {
|
|
104401
|
+
const { done, value } = await reader.read();
|
|
104402
|
+
if (done) {
|
|
104403
|
+
controller.close();
|
|
104404
|
+
cleanupAfter();
|
|
104405
|
+
return;
|
|
104406
|
+
}
|
|
104407
|
+
controller.enqueue(value);
|
|
104408
|
+
} catch (e) {
|
|
104409
|
+
controller.error(e);
|
|
104410
|
+
cleanupAfter();
|
|
104411
|
+
}
|
|
104412
|
+
},
|
|
104413
|
+
cancel() {
|
|
104414
|
+
reader.cancel();
|
|
104415
|
+
cleanupAfter();
|
|
104416
|
+
}
|
|
104417
|
+
});
|
|
104418
|
+
}
|
|
104419
|
+
function handleDownloadPrepare(req) {
|
|
104420
|
+
let abort = null;
|
|
104421
|
+
const encoder3 = new TextEncoder;
|
|
104422
|
+
const stream = new ReadableStream({
|
|
104423
|
+
async start(controller) {
|
|
104424
|
+
const emit = (obj) => {
|
|
104425
|
+
try {
|
|
104426
|
+
controller.enqueue(encoder3.encode(`${JSON.stringify(obj)}
|
|
104427
|
+
`));
|
|
104428
|
+
} catch {}
|
|
104429
|
+
};
|
|
104430
|
+
const close = () => {
|
|
104431
|
+
try {
|
|
104432
|
+
controller.close();
|
|
104433
|
+
} catch {}
|
|
104434
|
+
};
|
|
104435
|
+
let body;
|
|
104436
|
+
try {
|
|
104437
|
+
body = await req.json();
|
|
104438
|
+
} catch {
|
|
104439
|
+
emit({ type: "error", code: "invalid" });
|
|
104440
|
+
close();
|
|
104441
|
+
return;
|
|
104442
|
+
}
|
|
104443
|
+
const rootId = typeof body.rootId === "string" ? body.rootId : "";
|
|
104444
|
+
const path = typeof body.path === "string" ? body.path : "";
|
|
104445
|
+
if (!rootId || !path) {
|
|
104446
|
+
emit({ type: "error", code: "invalid" });
|
|
104447
|
+
close();
|
|
104448
|
+
return;
|
|
104449
|
+
}
|
|
104450
|
+
abort = new AbortController;
|
|
104451
|
+
const result = await pullFileFromDevice(rootId, path, {
|
|
104452
|
+
signal: abort.signal,
|
|
104453
|
+
onProgress: (p) => emit({ type: "progress", ...p })
|
|
104454
|
+
});
|
|
104455
|
+
if (result.ok) {
|
|
104456
|
+
const s = createDownloadSession({
|
|
104457
|
+
tmpPath: result.data.tmpPath,
|
|
104458
|
+
size: result.data.size,
|
|
104459
|
+
name: result.data.name,
|
|
104460
|
+
mime: result.data.mime,
|
|
104461
|
+
cleanup: result.data.cleanup
|
|
104462
|
+
});
|
|
104463
|
+
emit({ type: "done", downloadId: s.id, size: s.size, name: s.name });
|
|
104464
|
+
} else {
|
|
104465
|
+
emit({ type: "error", code: result.code, detail: result.detail });
|
|
104466
|
+
}
|
|
104467
|
+
close();
|
|
104468
|
+
},
|
|
104469
|
+
cancel() {
|
|
104470
|
+
abort?.abort();
|
|
104471
|
+
}
|
|
104472
|
+
});
|
|
104473
|
+
return new Response(stream, {
|
|
104474
|
+
status: 200,
|
|
104475
|
+
headers: { "Content-Type": "application/x-ndjson; charset=utf-8", "Cache-Control": "no-store" }
|
|
104476
|
+
});
|
|
104477
|
+
}
|
|
104478
|
+
function handleDownloadContent(id) {
|
|
104479
|
+
const session = getDownloadSession(id);
|
|
104480
|
+
if (!session)
|
|
104481
|
+
return codeError("not_found");
|
|
104482
|
+
const body = streamTempFile(session.tmpPath, () => removeDownloadSession(id));
|
|
104483
|
+
if (!body)
|
|
104484
|
+
return codeError("unknown");
|
|
104485
|
+
return new Response(body, {
|
|
104486
|
+
status: 200,
|
|
104487
|
+
headers: attachmentHeaders(session.name, session.mime, session.size)
|
|
104488
|
+
});
|
|
104489
|
+
}
|
|
104490
|
+
function handleDownloadCancel(id) {
|
|
104491
|
+
removeDownloadSession(id);
|
|
104492
|
+
return json4({ success: true });
|
|
104493
|
+
}
|
|
104494
|
+
async function handleDownload(req, url2) {
|
|
104495
|
+
const rootId = url2.searchParams.get("rootId");
|
|
104496
|
+
const path = url2.searchParams.get("path");
|
|
104497
|
+
if (!rootId || !path)
|
|
104498
|
+
return json4({ error: t2("apiError.invalidRequest") }, 400);
|
|
104499
|
+
const result = await pullFileFromDevice(rootId, path, {
|
|
104500
|
+
signal: req.signal,
|
|
104501
|
+
onProgress: () => {}
|
|
104502
|
+
});
|
|
104503
|
+
if (!result.ok)
|
|
104504
|
+
return codeError(result.code, result.detail);
|
|
104505
|
+
const { tmpPath, size, name: name24, mime, cleanup } = result.data;
|
|
104506
|
+
const body = streamTempFile(tmpPath, cleanup);
|
|
104507
|
+
if (!body)
|
|
104508
|
+
return codeError("unknown");
|
|
104509
|
+
return new Response(body, { status: 200, headers: attachmentHeaders(name24, mime, size) });
|
|
104510
|
+
}
|
|
103835
104511
|
function handleFilesApiRequest(req, path) {
|
|
103836
104512
|
const url2 = new URL(req.url);
|
|
103837
104513
|
if (path === "/api/files/roots" && req.method === "GET")
|
|
@@ -103852,6 +104528,32 @@ function handleFilesApiRequest(req, path) {
|
|
|
103852
104528
|
return handleStat(url2);
|
|
103853
104529
|
if (path === "/api/files/raw" && req.method === "GET")
|
|
103854
104530
|
return handleRaw(url2);
|
|
104531
|
+
if (path === "/api/files/download" && req.method === "GET")
|
|
104532
|
+
return handleDownload(req, url2);
|
|
104533
|
+
if (path === "/api/files/download/prepare" && req.method === "POST") {
|
|
104534
|
+
return handleDownloadPrepare(req);
|
|
104535
|
+
}
|
|
104536
|
+
const dlContentMatch = path.match(/^\/api\/files\/download\/([^/]+)\/content$/);
|
|
104537
|
+
if (dlContentMatch && req.method === "GET") {
|
|
104538
|
+
return handleDownloadContent(decodeURIComponent(dlContentMatch[1]));
|
|
104539
|
+
}
|
|
104540
|
+
const dlCancelMatch = path.match(/^\/api\/files\/download\/([^/]+)$/);
|
|
104541
|
+
if (dlCancelMatch && req.method === "DELETE") {
|
|
104542
|
+
return handleDownloadCancel(decodeURIComponent(dlCancelMatch[1]));
|
|
104543
|
+
}
|
|
104544
|
+
if (path === "/api/files/upload/init" && req.method === "POST")
|
|
104545
|
+
return handleUploadInit(req);
|
|
104546
|
+
const commitMatch = path.match(/^\/api\/files\/upload\/([^/]+)\/commit$/);
|
|
104547
|
+
if (commitMatch && req.method === "POST") {
|
|
104548
|
+
return handleUploadCommit(decodeURIComponent(commitMatch[1]));
|
|
104549
|
+
}
|
|
104550
|
+
const uploadMatch = path.match(/^\/api\/files\/upload\/([^/]+)$/);
|
|
104551
|
+
if (uploadMatch && req.method === "PUT") {
|
|
104552
|
+
return handleUploadChunk(req, decodeURIComponent(uploadMatch[1]), url2);
|
|
104553
|
+
}
|
|
104554
|
+
if (uploadMatch && req.method === "DELETE") {
|
|
104555
|
+
return handleUploadCancel(decodeURIComponent(uploadMatch[1]));
|
|
104556
|
+
}
|
|
103855
104557
|
return;
|
|
103856
104558
|
}
|
|
103857
104559
|
|
|
@@ -104208,8 +104910,8 @@ function getBaseVersion() {
|
|
|
104208
104910
|
if (cachedBase !== undefined)
|
|
104209
104911
|
return cachedBase;
|
|
104210
104912
|
let base = null;
|
|
104211
|
-
if ("0.
|
|
104212
|
-
base = "0.
|
|
104913
|
+
if ("0.12.2") {
|
|
104914
|
+
base = "0.12.2";
|
|
104213
104915
|
}
|
|
104214
104916
|
if (!base && config.isProd) {
|
|
104215
104917
|
base = readInstallMeta()?.cliVersion ?? null;
|
|
@@ -104300,8 +105002,8 @@ async function fetchChangelog(version3) {
|
|
|
104300
105002
|
import { spawn } from "child_process";
|
|
104301
105003
|
import { closeSync, existsSync as existsSync6, openSync } from "fs";
|
|
104302
105004
|
import { mkdtemp, rm, writeFile } from "fs/promises";
|
|
104303
|
-
import { tmpdir as
|
|
104304
|
-
import { join as
|
|
105005
|
+
import { tmpdir as tmpdir4 } from "os";
|
|
105006
|
+
import { join as join7 } from "path";
|
|
104305
105007
|
class UpgradeController {
|
|
104306
105008
|
state = "idle";
|
|
104307
105009
|
targetVersion = null;
|
|
@@ -104337,12 +105039,12 @@ class UpgradeController {
|
|
|
104337
105039
|
if (!installDir) {
|
|
104338
105040
|
throw new Error("install directory could not be resolved");
|
|
104339
105041
|
}
|
|
104340
|
-
stageDir = await mkdtemp(
|
|
104341
|
-
cacheDir = await mkdtemp(
|
|
104342
|
-
await writeFile(
|
|
105042
|
+
stageDir = await mkdtemp(join7(tmpdir4(), "tmex-upg-"));
|
|
105043
|
+
cacheDir = await mkdtemp(join7(tmpdir4(), "tmex-upg-cache-"));
|
|
105044
|
+
await writeFile(join7(stageDir, "package.json"), `${JSON.stringify({ name: "tmex-upgrade-stage", private: true })}
|
|
104343
105045
|
`);
|
|
104344
105046
|
await this.runBunAdd(stageDir, cacheDir, version3);
|
|
104345
|
-
const binPath =
|
|
105047
|
+
const binPath = join7(stageDir, "node_modules", "tmex-cli", "bin", "tmex.js");
|
|
104346
105048
|
if (!existsSync6(binPath)) {
|
|
104347
105049
|
throw new Error(`downloaded tmex-cli binary not found at ${binPath}`);
|
|
104348
105050
|
}
|
|
@@ -104377,7 +105079,7 @@ class UpgradeController {
|
|
|
104377
105079
|
spawnUpgrade(binPath, installDir, version3) {
|
|
104378
105080
|
let logFd = null;
|
|
104379
105081
|
try {
|
|
104380
|
-
logFd = openSync(
|
|
105082
|
+
logFd = openSync(join7(installDir, "upgrade.log"), "a");
|
|
104381
105083
|
} catch {
|
|
104382
105084
|
logFd = null;
|
|
104383
105085
|
}
|
|
@@ -104413,7 +105115,8 @@ function getSystemInfo() {
|
|
|
104413
105115
|
installedViaCli: install.installedViaCli,
|
|
104414
105116
|
deployment: install.deployment,
|
|
104415
105117
|
canSelfUpdate,
|
|
104416
|
-
serviceName: install.serviceName
|
|
105118
|
+
serviceName: install.serviceName,
|
|
105119
|
+
transferMaxBytes: config.transferMaxBytes
|
|
104417
105120
|
};
|
|
104418
105121
|
}
|
|
104419
105122
|
|
|
@@ -107949,6 +108652,7 @@ async function createGatewayRuntime(options = {}) {
|
|
|
107949
108652
|
}
|
|
107950
108653
|
runtimeController.reset();
|
|
107951
108654
|
primeLocalShellPath();
|
|
108655
|
+
sweepOrphanTransferTemps();
|
|
107952
108656
|
const wsServer = new WebSocketServer;
|
|
107953
108657
|
connectionAlertNotifier.setBroadcaster((deviceId, payload) => {
|
|
107954
108658
|
wsServer.broadcastDeviceError(deviceId, payload);
|
|
@@ -108274,7 +108978,7 @@ async function serveFrontend(req, staticRoot) {
|
|
|
108274
108978
|
if (!existsSync8(requestedPath) && extname2(url2.pathname) !== "") {
|
|
108275
108979
|
return new Response(t3("runtime.notFound"), { status: 404 });
|
|
108276
108980
|
}
|
|
108277
|
-
const indexPath =
|
|
108981
|
+
const indexPath = join8(staticRoot, "index.html");
|
|
108278
108982
|
const targetPath = existsSync8(requestedPath) ? requestedPath : indexPath;
|
|
108279
108983
|
if (!existsSync8(targetPath)) {
|
|
108280
108984
|
return new Response(t3("runtime.frontendMissing"), { status: 500 });
|