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.
Files changed (82) hide show
  1. package/CHANGELOG.md +22 -12
  2. package/dist/runtime/server.js +752 -48
  3. package/package.json +1 -1
  4. package/resources/fe-dist/assets/DevicePage-B0fQ6yWB.js +25 -0
  5. package/resources/fe-dist/assets/DevicesPage-C7THDzTX.js +2 -0
  6. package/resources/fe-dist/assets/{FilePage-YWma96aj.js → FilePage-D8V8P2IA.js} +8 -8
  7. package/resources/fe-dist/assets/{SettingsPage-LhUD03Ln.js → SettingsPage-BOTyBhHH.js} +8 -8
  8. package/resources/fe-dist/assets/{arc-DLVa298O.js → arc-DcujnLz7.js} +2 -2
  9. package/resources/fe-dist/assets/architectureDiagram-3BPJPVTR-i8499Wfc.js +37 -0
  10. package/resources/fe-dist/assets/{blockDiagram-GPEHLZMM-BTwwkzZh.js → blockDiagram-GPEHLZMM-BFh4xZY2.js} +2 -2
  11. package/resources/fe-dist/assets/{c4Diagram-AAUBKEIU-xvTqXxl9.js → c4Diagram-AAUBKEIU-nUyGK7Ke.js} +2 -2
  12. package/resources/fe-dist/assets/{card-cbVDy0Xp.js → card-BkRqR9zz.js} +2 -2
  13. package/resources/fe-dist/assets/channel-DqmfsuLY.js +2 -0
  14. package/resources/fe-dist/assets/{chunk-2J33WTMH-Blr6l1Nk.js → chunk-2J33WTMH-z2oMKYMn.js} +2 -2
  15. package/resources/fe-dist/assets/{chunk-4BX2VUAB-D_Y2TeLg.js → chunk-4BX2VUAB-CCD_pyAh.js} +2 -2
  16. package/resources/fe-dist/assets/{chunk-55IACEB6-CimNoYDH.js → chunk-55IACEB6-Dx2_9d-z.js} +2 -2
  17. package/resources/fe-dist/assets/{chunk-727SXJPM-JPYDN0Cb.js → chunk-727SXJPM-DA36poir.js} +2 -2
  18. package/resources/fe-dist/assets/{chunk-AQP2D5EJ-Cb3NZBib.js → chunk-AQP2D5EJ-CQoXkVd4.js} +2 -2
  19. package/resources/fe-dist/assets/{chunk-FMBD7UC4-DyZ7Ot2F.js → chunk-FMBD7UC4-A6Yn7A44.js} +2 -2
  20. package/resources/fe-dist/assets/{chunk-ND2GUHAM-SU6Ea3wF.js → chunk-ND2GUHAM-BsNl2EMT.js} +2 -2
  21. package/resources/fe-dist/assets/{chunk-QZHKN3VN-B6qXX6sU.js → chunk-QZHKN3VN-BM_QNxf6.js} +2 -2
  22. package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-D3Z9BeOE.js +2 -0
  23. package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-D3Z9BeOE.js +2 -0
  24. package/resources/fe-dist/assets/cose-bilkent-S5V4N54A-1lMO_Dod.js +2 -0
  25. package/resources/fe-dist/assets/{dagre-BM42HDAG-Ckrx9BXZ.js → dagre-BM42HDAG-C9Up3ckQ.js} +2 -2
  26. package/resources/fe-dist/assets/{diagram-2AECGRRQ-DTnZtf2k.js → diagram-2AECGRRQ-CtuDndmY.js} +2 -2
  27. package/resources/fe-dist/assets/{diagram-5GNKFQAL-DBpgZj4a.js → diagram-5GNKFQAL-BQfskK8S.js} +2 -2
  28. package/resources/fe-dist/assets/{diagram-KO2AKTUF-CfijYCeY.js → diagram-KO2AKTUF-CGJi8q81.js} +2 -2
  29. package/resources/fe-dist/assets/{diagram-LMA3HP47-D7nBmQ-E.js → diagram-LMA3HP47-DAMQIoLQ.js} +2 -2
  30. package/resources/fe-dist/assets/{diagram-OG6HWLK6-Bi8sHV0O.js → diagram-OG6HWLK6-D2s__dFc.js} +2 -2
  31. package/resources/fe-dist/assets/{erDiagram-TEJ5UH35-DcoDesO2.js → erDiagram-TEJ5UH35-_UJYLIQN.js} +2 -2
  32. package/resources/fe-dist/assets/{flowDiagram-I6XJVG4X-Bbcl9kxS.js → flowDiagram-I6XJVG4X-DN-qktDh.js} +2 -2
  33. package/resources/fe-dist/assets/ganttDiagram-6RSMTGT7-CNuuuhXX.js +293 -0
  34. package/resources/fe-dist/assets/{gitGraphDiagram-PVQCEYII-x3wpNQcW.js → gitGraphDiagram-PVQCEYII-Qar2sVRo.js} +2 -2
  35. package/resources/fe-dist/assets/index-BhU3LRKb.css +1 -0
  36. package/resources/fe-dist/assets/index-Ra6pj0rt.js +488 -0
  37. package/resources/fe-dist/assets/{infoDiagram-5YYISTIA-Bl6cgwVb.js → infoDiagram-5YYISTIA-2e6tU_v9.js} +2 -2
  38. package/resources/fe-dist/assets/{ishikawaDiagram-YF4QCWOH-BfQ7aZxN.js → ishikawaDiagram-YF4QCWOH-BghKvoM4.js} +2 -2
  39. package/resources/fe-dist/assets/{journeyDiagram-JHISSGLW-DSwQzmdL.js → journeyDiagram-JHISSGLW-D-cHSGb_.js} +2 -2
  40. package/resources/fe-dist/assets/{kanban-definition-UN3LZRKU-B1aWDl4E.js → kanban-definition-UN3LZRKU-BE6Cb-OK.js} +2 -2
  41. package/resources/fe-dist/assets/{linear-CfhRC4iX.js → linear-CwDBnUH0.js} +2 -2
  42. package/resources/fe-dist/assets/{markdown-preview-CfBRMViB.js → markdown-preview-BlQiC8ND.js} +4 -4
  43. package/resources/fe-dist/assets/{mermaid.core-M-o4g4TO.js → mermaid.core-DDMkLXrp.js} +6 -6
  44. package/resources/fe-dist/assets/{mindmap-definition-RKZ34NQL-D976sxOA.js → mindmap-definition-RKZ34NQL-C9xVR7Ut.js} +2 -2
  45. package/resources/fe-dist/assets/{pieDiagram-4H26LBE5-PVQ3-EB6.js → pieDiagram-4H26LBE5-DW_4RcBv.js} +2 -2
  46. package/resources/fe-dist/assets/{quadrantDiagram-W4KKPZXB-BNEFhYNq.js → quadrantDiagram-W4KKPZXB-DoIrZmk2.js} +2 -2
  47. package/resources/fe-dist/assets/{requirementDiagram-4Y6WPE33-B7Zd2lpN.js → requirementDiagram-4Y6WPE33-CYcJSuPe.js} +2 -2
  48. package/resources/fe-dist/assets/{sankeyDiagram-5OEKKPKP-D_Td4j-B.js → sankeyDiagram-5OEKKPKP-CrIkKM6h.js} +2 -2
  49. package/resources/fe-dist/assets/{sequenceDiagram-3UESZ5HK-DQSVX5mL.js → sequenceDiagram-3UESZ5HK-DgzGHhGk.js} +2 -2
  50. package/resources/fe-dist/assets/{stateDiagram-AJRCARHV-C0PNCTfs.js → stateDiagram-AJRCARHV-C6Dhr3fY.js} +2 -2
  51. package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-BiSh2azE.js +2 -0
  52. package/resources/fe-dist/assets/terminal-settings-panel-CpnmWQtf.js +9 -0
  53. package/resources/fe-dist/assets/{timeline-definition-PNZ67QCA-BlMj8Fl1.js → timeline-definition-PNZ67QCA-jJ1iauQ0.js} +2 -2
  54. package/resources/fe-dist/assets/{vennDiagram-CIIHVFJN-DuGxwZiq.js → vennDiagram-CIIHVFJN-DLRoaoJ9.js} +2 -2
  55. package/resources/fe-dist/assets/{wardley-L42UT6IY-4VhWbg10.js → wardley-L42UT6IY-CykeHWp8.js} +2 -2
  56. package/resources/fe-dist/assets/{wardleyDiagram-YWT4CUSO-Bu4k6xIx.js → wardleyDiagram-YWT4CUSO-U22jzD8a.js} +2 -2
  57. package/resources/fe-dist/assets/{xychartDiagram-2RQKCTM6-Cv88IDWh.js → xychartDiagram-2RQKCTM6-CiXS9PJ2.js} +2 -2
  58. package/resources/fe-dist/fonts/generated/blex-mono/blex-mono-bold.woff2 +0 -0
  59. package/resources/fe-dist/fonts/generated/blex-mono/blex-mono-regular.woff2 +0 -0
  60. package/resources/fe-dist/fonts/generated/fira-code/fira-code-bold.woff2 +0 -0
  61. package/resources/fe-dist/fonts/generated/fira-code/fira-code-regular.woff2 +0 -0
  62. package/resources/fe-dist/fonts/generated/jetbrains-mono/jetbrains-mono-bold.woff2 +0 -0
  63. package/resources/fe-dist/fonts/generated/jetbrains-mono/jetbrains-mono-regular.woff2 +0 -0
  64. package/resources/fe-dist/fonts/generated/noto-sans-mono/noto-sans-mono-bold.woff2 +0 -0
  65. package/resources/fe-dist/fonts/generated/noto-sans-mono/noto-sans-mono-regular.woff2 +0 -0
  66. package/resources/fe-dist/fonts/generated/victor-mono/victor-mono-bold.woff2 +0 -0
  67. package/resources/fe-dist/fonts/generated/victor-mono/victor-mono-regular.woff2 +0 -0
  68. package/resources/fe-dist/fonts/generated/zed-mono/zed-mono-bold.woff2 +0 -0
  69. package/resources/fe-dist/fonts/generated/zed-mono/zed-mono-regular.woff2 +0 -0
  70. package/resources/fe-dist/index.html +2 -2
  71. package/resources/fe-dist/assets/DevicePage-fGI-do8S.js +0 -26
  72. package/resources/fe-dist/assets/DevicesPage-kGrzwhGj.js +0 -2
  73. package/resources/fe-dist/assets/architectureDiagram-3BPJPVTR-EOUpuRby.js +0 -37
  74. package/resources/fe-dist/assets/channel-DbG6qNF6.js +0 -2
  75. package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-rB1DgxOZ.js +0 -2
  76. package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-rB1DgxOZ.js +0 -2
  77. package/resources/fe-dist/assets/cose-bilkent-S5V4N54A-BAyMopHS.js +0 -2
  78. package/resources/fe-dist/assets/ganttDiagram-6RSMTGT7-BM8Y7jwp.js +0 -293
  79. package/resources/fe-dist/assets/index-cfY8KuW7.js +0 -466
  80. package/resources/fe-dist/assets/index-vcS0VPIW.css +0 -1
  81. package/resources/fe-dist/assets/refresh-cw-cfX5JiIH.js +0 -7
  82. package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-CB5OePKf.js +0 -2
@@ -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 join7, normalize, resolve as resolve6, sep } from "path";
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\u8BBE\u7F6E",
23644
+ general: "\u901A\u7528",
23578
23645
  devicesAndFiles: "\u8BBE\u5907\u4E0E\u6587\u4EF6",
23579
- notifications: "\u901A\u77E5\u8BBE\u7F6E",
23580
- ai: "AI \u8BBE\u7F6E"
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\u8A2D\u5B9A",
24527
+ general: "\u4E00\u822C",
24406
24528
  devicesAndFiles: "\u30C7\u30D0\u30A4\u30B9\u3068\u30D5\u30A1\u30A4\u30EB",
24407
- notifications: "\u901A\u77E5\u8A2D\u5B9A",
24408
- ai: "AI \u8A2D\u5B9A"
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
- const tgSafePaneUrl = encodePercentForTelegramUrl(paneUrl);
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
- const tgSafePaneUrl = encodePercentForTelegramUrl(paneUrl);
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
- const tgSafePaneUrl = encodePercentForTelegramUrl(paneUrl);
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, friendlyMessage, classified.type);
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" || device.sshConfigRef?.trim()) {
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 killTimer = setTimeout(() => {
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
- new Response(proc.stdout).text(),
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
- clearTimeout(killTimer);
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.11.0") {
104212
- base = "0.11.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 tmpdir3 } from "os";
104304
- import { join as join6 } from "path";
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(join6(tmpdir3(), "tmex-upg-"));
104341
- cacheDir = await mkdtemp(join6(tmpdir3(), "tmex-upg-cache-"));
104342
- await writeFile(join6(stageDir, "package.json"), `${JSON.stringify({ name: "tmex-upgrade-stage", private: true })}
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 = join6(stageDir, "node_modules", "tmex-cli", "bin", "tmex.js");
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(join6(installDir, "upgrade.log"), "a");
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 = join7(staticRoot, "index.html");
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 });