tmex-cli 0.12.4 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -6
- package/dist/cli-node.js +492 -87
- package/dist/runtime/server.js +1912 -132
- package/package.json +1 -1
- package/resources/fe-dist/assets/DevicePage-Ctq31eWb.js +19 -0
- package/resources/fe-dist/assets/DevicesPage-DGo44NW0.js +1 -0
- package/resources/fe-dist/assets/{FilePage-D1t8_zlX.js → FilePage-BpubOqjN.js} +3 -4
- package/resources/fe-dist/assets/SettingsPage-QT6fy6XA.js +39 -0
- package/resources/fe-dist/assets/agent-tab-A2FeMPwL.js +38 -0
- package/resources/fe-dist/assets/api-CGfOwFKm.js +10 -0
- package/resources/fe-dist/assets/{arc-Cv0H1Phu.js → arc-B2dphCVi.js} +1 -2
- package/resources/fe-dist/assets/{architectureDiagram-3BPJPVTR-DG7npZeK.js → architectureDiagram-3BPJPVTR-BpvEhd8M.js} +1 -2
- package/resources/fe-dist/assets/{blockDiagram-GPEHLZMM-weB7PKUK.js → blockDiagram-GPEHLZMM-CI_NnIJz.js} +1 -2
- package/resources/fe-dist/assets/{c4Diagram-AAUBKEIU-C0sEabtp.js → c4Diagram-AAUBKEIU-Y6RrOc7Y.js} +1 -2
- package/resources/fe-dist/assets/{card-Spfb5xRZ.js → card-CydUB3N3.js} +1 -2
- package/resources/fe-dist/assets/channel-Ce3VMo9B.js +1 -0
- package/resources/fe-dist/assets/{chunk-2J33WTMH-BxOp2sIC.js → chunk-2J33WTMH-BQN_fYaa.js} +1 -2
- package/resources/fe-dist/assets/{chunk-4BX2VUAB-bhg4hxVT.js → chunk-4BX2VUAB-BExarxjk.js} +1 -2
- package/resources/fe-dist/assets/{chunk-55IACEB6-v6jayOak.js → chunk-55IACEB6-DeOEgcn_.js} +1 -2
- package/resources/fe-dist/assets/{chunk-727SXJPM-IY0KVws8.js → chunk-727SXJPM-CwkxCtPA.js} +1 -2
- package/resources/fe-dist/assets/{chunk-AQP2D5EJ-CgwymoWa.js → chunk-AQP2D5EJ-_IoTtvOb.js} +1 -2
- package/resources/fe-dist/assets/{chunk-FMBD7UC4-DN35690f.js → chunk-FMBD7UC4-6pvmQ3Lj.js} +1 -2
- package/resources/fe-dist/assets/{chunk-ND2GUHAM-lJmtxdhS.js → chunk-ND2GUHAM-CxpGf5zM.js} +1 -2
- package/resources/fe-dist/assets/{chunk-QZHKN3VN-DFeaB8rn.js → chunk-QZHKN3VN-DOMH0v_e.js} +1 -2
- package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-BjK-bnh-.js +1 -0
- package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-BjK-bnh-.js +1 -0
- package/resources/fe-dist/assets/copy-DnlW35bA.js +6 -0
- package/resources/fe-dist/assets/{cose-bilkent-S5V4N54A-Dw2yYQz4.js → cose-bilkent-S5V4N54A-D03NElzH.js} +1 -2
- package/resources/fe-dist/assets/cytoscape.esm-DTSO7Bv0.js +0 -1
- package/resources/fe-dist/assets/{dagre-BM42HDAG-BeMjJ7ln.js → dagre-BM42HDAG-CVOxLhKQ.js} +1 -2
- package/resources/fe-dist/assets/defaultLocale-DX6XiGOO.js +0 -1
- package/resources/fe-dist/assets/{diagram-2AECGRRQ-Dur8_CuN.js → diagram-2AECGRRQ-p564GAo-.js} +1 -2
- package/resources/fe-dist/assets/{diagram-5GNKFQAL-GJc7gVa_.js → diagram-5GNKFQAL-C_pgnQLt.js} +1 -2
- package/resources/fe-dist/assets/{diagram-KO2AKTUF-pxckXfjw.js → diagram-KO2AKTUF-B36jioML.js} +1 -2
- package/resources/fe-dist/assets/{diagram-LMA3HP47-B9E8-X7C.js → diagram-LMA3HP47-B8P4WWeq.js} +1 -2
- package/resources/fe-dist/assets/{diagram-OG6HWLK6-DrhHw5wy.js → diagram-OG6HWLK6-CTL4zJlz.js} +1 -2
- package/resources/fe-dist/assets/en_US-DRPd4vPi.js +1 -0
- package/resources/fe-dist/assets/{erDiagram-TEJ5UH35-DY9Wg1A1.js → erDiagram-TEJ5UH35-DbKPL3Fa.js} +1 -2
- package/resources/fe-dist/assets/files-tab-CeUM00x8.js +70 -0
- package/resources/fe-dist/assets/{flowDiagram-I6XJVG4X-eq3WE9d6.js → flowDiagram-I6XJVG4X-DvOU6wtN.js} +1 -2
- package/resources/fe-dist/assets/{ganttDiagram-6RSMTGT7-BlXISXe1.js → ganttDiagram-6RSMTGT7-CwgNky8L.js} +1 -2
- package/resources/fe-dist/assets/{gitGraphDiagram-PVQCEYII-Bg-xHeIY.js → gitGraphDiagram-PVQCEYII-B3U-hNGe.js} +1 -2
- package/resources/fe-dist/assets/graph-CAnANduQ.js +0 -1
- package/resources/fe-dist/assets/index-D-q7dOhH.css +1 -0
- package/resources/fe-dist/assets/index-DAElYydw.js +299 -0
- package/resources/fe-dist/assets/index-DBHh6Xu1.js +29 -0
- package/resources/fe-dist/assets/{infoDiagram-5YYISTIA-CW2drAHG.js → infoDiagram-5YYISTIA-BmcWF7v1.js} +1 -2
- package/resources/fe-dist/assets/init-Gi6I4Gst.js +0 -1
- package/resources/fe-dist/assets/{ishikawaDiagram-YF4QCWOH-Qchz0nqR.js → ishikawaDiagram-YF4QCWOH-Dr8C0MtS.js} +1 -2
- package/resources/fe-dist/assets/ja_JP-DHREHMZZ.js +1 -0
- package/resources/fe-dist/assets/{journeyDiagram-JHISSGLW-Dk-UsexE.js → journeyDiagram-JHISSGLW-DzlvXb4K.js} +1 -2
- package/resources/fe-dist/assets/{kanban-definition-UN3LZRKU-DKeGp7b5.js → kanban-definition-UN3LZRKU-4aBWZlcm.js} +1 -2
- package/resources/fe-dist/assets/layout-DGIYPm2g.js +0 -1
- package/resources/fe-dist/assets/{linear-Bt4f_qDW.js → linear-_wXLepjd.js} +1 -2
- package/resources/fe-dist/assets/{markdown-preview-DXv9GzaA.js → markdown-preview-BX0o83rG.js} +6 -7
- package/resources/fe-dist/assets/{mermaid.core-B-wSkmMd.js → mermaid.core-DoE3dx_X.js} +5 -6
- package/resources/fe-dist/assets/{mindmap-definition-RKZ34NQL-DXBwCMXu.js → mindmap-definition-RKZ34NQL-CKpq2s43.js} +1 -2
- package/resources/fe-dist/assets/ordinal-BENe2yWM.js +0 -1
- package/resources/fe-dist/assets/{pieDiagram-4H26LBE5--KxRgbWG.js → pieDiagram-4H26LBE5-DlW1TLza.js} +1 -2
- package/resources/fe-dist/assets/{quadrantDiagram-W4KKPZXB-DqXsCI9E.js → quadrantDiagram-W4KKPZXB-NEwxpMVe.js} +1 -2
- package/resources/fe-dist/assets/{requirementDiagram-4Y6WPE33-C2KeTWqB.js → requirementDiagram-4Y6WPE33-CGysr6uH.js} +1 -2
- package/resources/fe-dist/assets/{sankeyDiagram-5OEKKPKP-CC5tcr7d.js → sankeyDiagram-5OEKKPKP-j8yXOgys.js} +1 -2
- package/resources/fe-dist/assets/selection-clipboard-Dq6Zemfd.js +1 -0
- package/resources/fe-dist/assets/send-BtOPYsPT.js +11 -0
- package/resources/fe-dist/assets/{sequenceDiagram-3UESZ5HK-CqlMDtOG.js → sequenceDiagram-3UESZ5HK-BRPWK9ZU.js} +1 -2
- package/resources/fe-dist/assets/{stateDiagram-AJRCARHV-CVcrlBiw.js → stateDiagram-AJRCARHV-C8ckQlA7.js} +1 -2
- package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-Cm0vyyJy.js +1 -0
- package/resources/fe-dist/assets/{terminal-settings-panel-CYV9svzz.js → terminal-settings-panel-ZnL_cBER.js} +6 -7
- package/resources/fe-dist/assets/{timeline-definition-PNZ67QCA-CEBskfkL.js → timeline-definition-PNZ67QCA-BXMFPa99.js} +1 -2
- package/resources/fe-dist/assets/transfer-toast-CUkPc73G.js +6 -0
- package/resources/fe-dist/assets/triangle-alert-DyM9tU7d.js +6 -0
- package/resources/fe-dist/assets/{vennDiagram-CIIHVFJN-CrxyVTtu.js → vennDiagram-CIIHVFJN-Bk8oPY9E.js} +1 -2
- package/resources/fe-dist/assets/{wardley-L42UT6IY-CSmsRb8z.js → wardley-L42UT6IY-Bi_GTP2_.js} +1 -2
- package/resources/fe-dist/assets/{wardleyDiagram-YWT4CUSO-DoDzNeOA.js → wardleyDiagram-YWT4CUSO-CaPDmxFM.js} +1 -2
- package/resources/fe-dist/assets/{xychartDiagram-2RQKCTM6-Dbq_OKsv.js → xychartDiagram-2RQKCTM6-EHogasXy.js} +1 -2
- package/resources/fe-dist/assets/zap-CBgYyGBV.js +6 -0
- package/resources/fe-dist/assets/zh_CN-DMEKXuFd.js +1 -0
- package/resources/fe-dist/index.html +2 -2
- package/resources/gateway-drizzle/0010_lucky_kabuki.sql +32 -0
- package/resources/gateway-drizzle/0011_stormy_sauron.sql +1 -0
- package/resources/gateway-drizzle/meta/_journal.json +14 -0
- package/resources/fe-dist/assets/DevicePage-C4JS6baR.js +0 -15
- package/resources/fe-dist/assets/DevicesPage-p0CfWP1T.js +0 -2
- package/resources/fe-dist/assets/SettingsPage-Cq6n8Xjc.js +0 -27
- package/resources/fe-dist/assets/channel-DDiDKfY0.js +0 -2
- package/resources/fe-dist/assets/classDiagram-4FO5ZUOK-CXFjdzka.js +0 -2
- package/resources/fe-dist/assets/classDiagram-v2-Q7XG4LA2-CXFjdzka.js +0 -2
- package/resources/fe-dist/assets/index-CXIj7HZi.css +0 -1
- package/resources/fe-dist/assets/index-CYmVLCAD.js +0 -488
- package/resources/fe-dist/assets/stateDiagram-v2-BHNVJYJU-B0BDuZBM.js +0 -2
package/dist/runtime/server.js
CHANGED
|
@@ -22654,6 +22654,8 @@ var I18N_RESOURCES = {
|
|
|
22654
22654
|
session: "Tmux Session Name",
|
|
22655
22655
|
sessionPlaceholder: "tmex",
|
|
22656
22656
|
sessionHint: 'Leave empty to use default "tmex"',
|
|
22657
|
+
defaultWorkingDir: "Default Working Directory",
|
|
22658
|
+
defaultWorkingDirPlaceholder: "Leave empty for user home directory",
|
|
22657
22659
|
authMode: "Authentication Mode",
|
|
22658
22660
|
authPassword: "Password",
|
|
22659
22661
|
authKey: "Private Key",
|
|
@@ -22708,9 +22710,12 @@ var I18N_RESOURCES = {
|
|
|
22708
22710
|
noDeviceSelected: "No device selected",
|
|
22709
22711
|
windowClosed: "Current window has been closed, please select a window from the sidebar.",
|
|
22710
22712
|
paneClosed: "Current pane has been closed, please select a pane from the sidebar.",
|
|
22711
|
-
bellNotification: "Terminal Bell
|
|
22712
|
-
|
|
22713
|
+
bellNotification: "Terminal Bell",
|
|
22714
|
+
bellDescriptionWithTitle: "Window {{window}} \xB7 {{paneLabel}}",
|
|
22713
22715
|
bellFallback: "Received tmux bell",
|
|
22716
|
+
notificationFallbackTitle: "Terminal Notification",
|
|
22717
|
+
notificationSourceLabel: "From {{source}}",
|
|
22718
|
+
notificationFallbackDetail: "Terminal notification",
|
|
22714
22719
|
paneTitle: "Pane {{index}}",
|
|
22715
22720
|
activePane: "Current Pane",
|
|
22716
22721
|
activeWindow: "Current Window",
|
|
@@ -22745,6 +22750,8 @@ var I18N_RESOURCES = {
|
|
|
22745
22750
|
enableBrowserNotificationToast: "Enable Browser Notification Toast",
|
|
22746
22751
|
enableTelegramBellPush: "Enable Telegram Bell Push",
|
|
22747
22752
|
enableTelegramNotificationPush: "Enable Telegram Notification Push",
|
|
22753
|
+
enableWeixinBellPush: "Enable WeChat Bell Push",
|
|
22754
|
+
enableWeixinNotificationPush: "Enable WeChat Notification Push",
|
|
22748
22755
|
sshReconnectRetries: "SSH Reconnect Retries",
|
|
22749
22756
|
sshReconnectDelay: "SSH Reconnect Delay (seconds)",
|
|
22750
22757
|
language: "Language",
|
|
@@ -22984,6 +22991,81 @@ Time: {{time}}`,
|
|
|
22984
22991
|
botNotFound: "Bot not found or unavailable",
|
|
22985
22992
|
botNotRunning: "Bot is not running or unavailable"
|
|
22986
22993
|
},
|
|
22994
|
+
weixin: {
|
|
22995
|
+
title: "WeChat (ClawBot) Management",
|
|
22996
|
+
subtitle: "Push tmex alerts to your personal WeChat via the iLink bot protocol.",
|
|
22997
|
+
replyOnlyNotice: "iLink can only reply inside an active conversation window \u2014 message the bot first to activate; alerts then reuse that session. If it goes quiet too long the session expires; message the bot again to re-activate.",
|
|
22998
|
+
accountName: "Account Name",
|
|
22999
|
+
accountNamePlaceholder: "e.g., my-wechat",
|
|
23000
|
+
addAccount: "Add Account",
|
|
23001
|
+
editAccount: "Edit Account",
|
|
23002
|
+
enableAccount: "Enable Account",
|
|
23003
|
+
allowAuthRequests: "Allow Authorization Requests",
|
|
23004
|
+
scanToLogin: "Scan to Log In",
|
|
23005
|
+
relogin: "Re-login (Re-scan)",
|
|
23006
|
+
loggedIn: "Logged in",
|
|
23007
|
+
notLoggedIn: "Not logged in",
|
|
23008
|
+
scanQrcodeHint: "Open WeChat, scan the QR code, and confirm on your phone.",
|
|
23009
|
+
loginPending: "Waiting for scan\u2026",
|
|
23010
|
+
loginConfirmed: "Login confirmed.",
|
|
23011
|
+
loginExpired: "QR code expired. Please retry.",
|
|
23012
|
+
loginError: "Login failed: {{message}}",
|
|
23013
|
+
loginFailed: "Login failed",
|
|
23014
|
+
refreshQrcode: "Refresh QR Code",
|
|
23015
|
+
closeLogin: "Close",
|
|
23016
|
+
pendingUsers: "Pending Authorization",
|
|
23017
|
+
authorizedUsers: "Authorized Users",
|
|
23018
|
+
noPendingUsers: "No pending users",
|
|
23019
|
+
noAuthorizedUsers: "No authorized users",
|
|
23020
|
+
needsReactivation: "Session expired",
|
|
23021
|
+
reactivationHint: "Ask this user to send the bot any message to re-activate alerts.",
|
|
23022
|
+
approve: "Approve",
|
|
23023
|
+
revokeAuth: "Revoke",
|
|
23024
|
+
removeUser: "Remove",
|
|
23025
|
+
testMessage: "Test Message",
|
|
23026
|
+
sendTestMessage: "Send Test Message",
|
|
23027
|
+
deleteAccount: "Delete Account",
|
|
23028
|
+
accountCreated: "Account created",
|
|
23029
|
+
accountUpdated: "Account updated",
|
|
23030
|
+
accountDeleted: "Account deleted",
|
|
23031
|
+
authApproved: "Authorization approved",
|
|
23032
|
+
userRemoved: "User removed",
|
|
23033
|
+
testMessageSent: "Test message sent",
|
|
23034
|
+
createFailed: "Failed to create account",
|
|
23035
|
+
updateFailed: "Failed to update account",
|
|
23036
|
+
deleteFailed: "Failed to delete account",
|
|
23037
|
+
approveFailed: "Failed to approve",
|
|
23038
|
+
removeFailed: "Failed to remove user",
|
|
23039
|
+
testMessageFailed: "Failed to send test message",
|
|
23040
|
+
loadAccountsFailed: "Failed to load accounts",
|
|
23041
|
+
loadUsersFailed: "Failed to load users",
|
|
23042
|
+
noAccounts: "No accounts yet, add one first.",
|
|
23043
|
+
userCount: "{{authorized}} authorized / {{pending}} pending (max 16)",
|
|
23044
|
+
userId: "WeChat User ID",
|
|
23045
|
+
applyTime: "First Contact",
|
|
23046
|
+
expand: "Expand",
|
|
23047
|
+
collapse: "Collapse",
|
|
23048
|
+
accountNameRequired: "Account name is required",
|
|
23049
|
+
accountNotFound: "Account not found or unavailable",
|
|
23050
|
+
accountNotRunning: "Account is not logged in or not running",
|
|
23051
|
+
userNotFound: "User not found",
|
|
23052
|
+
authSuccess: "\u2705 Authorized. You will now receive notifications.",
|
|
23053
|
+
authPending: "\u23F3 Authorization request received. Please approve in tmex settings.",
|
|
23054
|
+
testMessageTemplate: `\uD83E\uDDEA Test Message
|
|
23055
|
+
Site: {{siteName}}
|
|
23056
|
+
Time: {{time}}`,
|
|
23057
|
+
approveMessageTemplate: `\u2705 Authorized via tmex.
|
|
23058
|
+
Account: {{accountName}}
|
|
23059
|
+
Time: {{time}}`,
|
|
23060
|
+
keepalivePrompt: "\u3010tmex\u3011To keep notifications flowing, just reply with anything to keep the session alive \uD83D\uDE4F",
|
|
23061
|
+
scanConfirmedSendHint: "Scan confirmed! Now send any message to this bot in WeChat to finish binding.",
|
|
23062
|
+
bindingInProgress: "Message received, finishing binding\u2026",
|
|
23063
|
+
bindSuccess: "WeChat bound. You will now receive notifications.",
|
|
23064
|
+
bound: "Bound",
|
|
23065
|
+
unbound: "Not bound",
|
|
23066
|
+
bindAction: "Bind (scan)",
|
|
23067
|
+
gatewayOnline: "\uD83D\uDFE2 tmex online @ {{siteName}}"
|
|
23068
|
+
},
|
|
22987
23069
|
webhook: {
|
|
22988
23070
|
title: "Webhooks",
|
|
22989
23071
|
url: "Webhook URL",
|
|
@@ -23568,6 +23650,8 @@ Time: {{time}}`,
|
|
|
23568
23650
|
session: "Tmux \u4F1A\u8BDD\u540D\u79F0",
|
|
23569
23651
|
sessionPlaceholder: "tmex",
|
|
23570
23652
|
sessionHint: '\u7559\u7A7A\u5C06\u4F7F\u7528\u9ED8\u8BA4\u503C "tmex"',
|
|
23653
|
+
defaultWorkingDir: "\u9ED8\u8BA4\u5DE5\u4F5C\u76EE\u5F55",
|
|
23654
|
+
defaultWorkingDirPlaceholder: "\u7559\u7A7A\u4F7F\u7528\u7528\u6237 home \u76EE\u5F55",
|
|
23571
23655
|
authMode: "\u8BA4\u8BC1\u65B9\u5F0F",
|
|
23572
23656
|
authPassword: "\u5BC6\u7801",
|
|
23573
23657
|
authKey: "\u79C1\u94A5",
|
|
@@ -23622,9 +23706,12 @@ Time: {{time}}`,
|
|
|
23622
23706
|
noDeviceSelected: "\u672A\u9009\u62E9\u8BBE\u5907",
|
|
23623
23707
|
windowClosed: "\u5F53\u524D\u7A97\u53E3\u5DF2\u5173\u95ED\uFF0C\u8BF7\u5728\u4FA7\u8FB9\u680F\u91CD\u65B0\u9009\u62E9\u7A97\u53E3\u3002",
|
|
23624
23708
|
paneClosed: "\u5F53\u524D Pane \u5DF2\u5173\u95ED\uFF0C\u8BF7\u5728\u4FA7\u8FB9\u680F\u91CD\u65B0\u9009\u62E9 Pane\u3002",
|
|
23625
|
-
bellNotification: "\u7EC8\u7AEF Bell
|
|
23626
|
-
|
|
23709
|
+
bellNotification: "\u7EC8\u7AEF Bell",
|
|
23710
|
+
bellDescriptionWithTitle: "\u7A97\u53E3 {{window}} \xB7 {{paneLabel}}",
|
|
23627
23711
|
bellFallback: "\u6536\u5230 tmux bell",
|
|
23712
|
+
notificationFallbackTitle: "\u7EC8\u7AEF\u901A\u77E5",
|
|
23713
|
+
notificationSourceLabel: "\u6765\u81EA {{source}}",
|
|
23714
|
+
notificationFallbackDetail: "\u7EC8\u7AEF\u901A\u77E5",
|
|
23628
23715
|
paneTitle: "Pane {{index}}",
|
|
23629
23716
|
activePane: "\u5F53\u524D pane",
|
|
23630
23717
|
activeWindow: "\u5F53\u524D\u7A97\u53E3",
|
|
@@ -23659,6 +23746,8 @@ Time: {{time}}`,
|
|
|
23659
23746
|
enableBrowserNotificationToast: "\u5F00\u542F\u6D4F\u89C8\u5668\u901A\u77E5 Toast",
|
|
23660
23747
|
enableTelegramBellPush: "\u5F00\u542F Telegram Bell \u63A8\u9001",
|
|
23661
23748
|
enableTelegramNotificationPush: "\u5F00\u542F Telegram \u901A\u77E5\u63A8\u9001",
|
|
23749
|
+
enableWeixinBellPush: "\u542F\u7528\u5FAE\u4FE1\u54CD\u94C3\u63A8\u9001",
|
|
23750
|
+
enableWeixinNotificationPush: "\u542F\u7528\u5FAE\u4FE1\u901A\u77E5\u63A8\u9001",
|
|
23662
23751
|
sshReconnectRetries: "SSH \u91CD\u8FDE\u6B21\u6570",
|
|
23663
23752
|
sshReconnectDelay: "SSH \u91CD\u8FDE\u7B49\u5F85\uFF08\u79D2\uFF09",
|
|
23664
23753
|
language: "\u8BED\u8A00",
|
|
@@ -23898,6 +23987,81 @@ Bot\uFF1A{{botName}}
|
|
|
23898
23987
|
botNotFound: "Bot \u672A\u542F\u52A8\u6216\u4E0D\u53EF\u7528",
|
|
23899
23988
|
botNotRunning: "Bot \u672A\u542F\u52A8\u6216\u4E0D\u53EF\u7528"
|
|
23900
23989
|
},
|
|
23990
|
+
weixin: {
|
|
23991
|
+
title: "\u5FAE\u4FE1 (ClawBot) \u7BA1\u7406",
|
|
23992
|
+
subtitle: "\u901A\u8FC7 iLink bot \u534F\u8BAE\u628A tmex \u544A\u8B66\u63A8\u9001\u5230\u4F60\u7684\u4E2A\u4EBA\u5FAE\u4FE1\u3002",
|
|
23993
|
+
replyOnlyNotice: "iLink \u53EA\u80FD\u5728\u4F1A\u8BDD\u6FC0\u6D3B\u7A97\u53E3\u5185\u56DE\u590D\u2014\u2014\u5148\u7ED9 bot \u53D1\u6761\u6D88\u606F\u6FC0\u6D3B\uFF0C\u544A\u8B66\u968F\u540E\u590D\u7528\u8BE5\u4F1A\u8BDD\uFF1B\u95F2\u7F6E\u8FC7\u4E45\u4F1A\u8BDD\u4F1A\u8FC7\u671F\uFF0C\u9700\u518D\u7ED9 bot \u53D1\u6761\u6D88\u606F\u91CD\u65B0\u6FC0\u6D3B\u3002",
|
|
23994
|
+
accountName: "\u8D26\u53F7\u540D\u79F0",
|
|
23995
|
+
accountNamePlaceholder: "\u5982\uFF1A\u6211\u7684\u5FAE\u4FE1",
|
|
23996
|
+
addAccount: "\u6DFB\u52A0\u8D26\u53F7",
|
|
23997
|
+
editAccount: "\u7F16\u8F91\u8D26\u53F7",
|
|
23998
|
+
enableAccount: "\u542F\u7528\u8D26\u53F7",
|
|
23999
|
+
allowAuthRequests: "\u5141\u8BB8\u6388\u6743\u7533\u8BF7",
|
|
24000
|
+
scanToLogin: "\u626B\u7801\u767B\u5F55",
|
|
24001
|
+
relogin: "\u91CD\u65B0\u767B\u5F55\uFF08\u91CD\u65B0\u626B\u7801\uFF09",
|
|
24002
|
+
loggedIn: "\u5DF2\u767B\u5F55",
|
|
24003
|
+
notLoggedIn: "\u672A\u767B\u5F55",
|
|
24004
|
+
scanQrcodeHint: "\u6253\u5F00\u5FAE\u4FE1\u626B\u63CF\u4E8C\u7EF4\u7801\uFF0C\u5E76\u5728\u624B\u673A\u4E0A\u786E\u8BA4\u3002",
|
|
24005
|
+
loginPending: "\u7B49\u5F85\u626B\u7801\u2026",
|
|
24006
|
+
loginConfirmed: "\u767B\u5F55\u5DF2\u786E\u8BA4\u3002",
|
|
24007
|
+
loginExpired: "\u4E8C\u7EF4\u7801\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u8BD5\u3002",
|
|
24008
|
+
loginError: "\u767B\u5F55\u5931\u8D25\uFF1A{{message}}",
|
|
24009
|
+
loginFailed: "\u767B\u5F55\u5931\u8D25",
|
|
24010
|
+
refreshQrcode: "\u5237\u65B0\u4E8C\u7EF4\u7801",
|
|
24011
|
+
closeLogin: "\u5173\u95ED",
|
|
24012
|
+
pendingUsers: "\u5F85\u6388\u6743",
|
|
24013
|
+
authorizedUsers: "\u5DF2\u6388\u6743\u7528\u6237",
|
|
24014
|
+
noPendingUsers: "\u6682\u65E0\u5F85\u6388\u6743\u7528\u6237",
|
|
24015
|
+
noAuthorizedUsers: "\u6682\u65E0\u5DF2\u6388\u6743\u7528\u6237",
|
|
24016
|
+
needsReactivation: "\u4F1A\u8BDD\u5DF2\u8FC7\u671F",
|
|
24017
|
+
reactivationHint: "\u8BF7\u8BE5\u7528\u6237\u7ED9 bot \u53D1\u6761\u6D88\u606F\u4EE5\u91CD\u65B0\u6FC0\u6D3B\u544A\u8B66\u3002",
|
|
24018
|
+
approve: "\u6279\u51C6",
|
|
24019
|
+
revokeAuth: "\u64A4\u9500\u6388\u6743",
|
|
24020
|
+
removeUser: "\u79FB\u9664",
|
|
24021
|
+
testMessage: "\u6D4B\u8BD5\u6D88\u606F",
|
|
24022
|
+
sendTestMessage: "\u53D1\u9001\u6D4B\u8BD5\u6D88\u606F",
|
|
24023
|
+
deleteAccount: "\u5220\u9664\u8D26\u53F7",
|
|
24024
|
+
accountCreated: "\u8D26\u53F7\u5DF2\u521B\u5EFA",
|
|
24025
|
+
accountUpdated: "\u8D26\u53F7\u5DF2\u66F4\u65B0",
|
|
24026
|
+
accountDeleted: "\u8D26\u53F7\u5DF2\u5220\u9664",
|
|
24027
|
+
authApproved: "\u6388\u6743\u5DF2\u6279\u51C6",
|
|
24028
|
+
userRemoved: "\u7528\u6237\u5DF2\u79FB\u9664",
|
|
24029
|
+
testMessageSent: "\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001",
|
|
24030
|
+
createFailed: "\u521B\u5EFA\u8D26\u53F7\u5931\u8D25",
|
|
24031
|
+
updateFailed: "\u66F4\u65B0\u8D26\u53F7\u5931\u8D25",
|
|
24032
|
+
deleteFailed: "\u5220\u9664\u8D26\u53F7\u5931\u8D25",
|
|
24033
|
+
approveFailed: "\u6279\u51C6\u5931\u8D25",
|
|
24034
|
+
removeFailed: "\u79FB\u9664\u7528\u6237\u5931\u8D25",
|
|
24035
|
+
testMessageFailed: "\u53D1\u9001\u6D4B\u8BD5\u6D88\u606F\u5931\u8D25",
|
|
24036
|
+
loadAccountsFailed: "\u52A0\u8F7D\u8D26\u53F7\u5217\u8868\u5931\u8D25",
|
|
24037
|
+
loadUsersFailed: "\u52A0\u8F7D\u7528\u6237\u5217\u8868\u5931\u8D25",
|
|
24038
|
+
noAccounts: "\u8FD8\u6CA1\u6709\u8D26\u53F7\uFF0C\u5148\u6DFB\u52A0\u4E00\u4E2A\u3002",
|
|
24039
|
+
userCount: "{{authorized}} \u5DF2\u6388\u6743 / {{pending}} \u5F85\u6388\u6743\uFF08\u4E0A\u9650 16\uFF09",
|
|
24040
|
+
userId: "\u5FAE\u4FE1\u7528\u6237 ID",
|
|
24041
|
+
applyTime: "\u9996\u6B21\u8054\u7CFB\u65F6\u95F4",
|
|
24042
|
+
expand: "\u5C55\u5F00",
|
|
24043
|
+
collapse: "\u6536\u8D77",
|
|
24044
|
+
accountNameRequired: "\u8D26\u53F7\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A",
|
|
24045
|
+
accountNotFound: "\u8D26\u53F7\u4E0D\u5B58\u5728\u6216\u4E0D\u53EF\u7528",
|
|
24046
|
+
accountNotRunning: "\u8D26\u53F7\u672A\u767B\u5F55\u6216\u672A\u8FD0\u884C",
|
|
24047
|
+
userNotFound: "\u7528\u6237\u4E0D\u5B58\u5728",
|
|
24048
|
+
authSuccess: "\u2705 \u5DF2\u6388\u6743\uFF0C\u4F60\u5C06\u5F00\u59CB\u6536\u5230\u901A\u77E5\u3002",
|
|
24049
|
+
authPending: "\u23F3 \u5DF2\u6536\u5230\u6388\u6743\u7533\u8BF7\uFF0C\u8BF7\u5728 tmex \u8BBE\u7F6E\u4E2D\u6279\u51C6\u3002",
|
|
24050
|
+
testMessageTemplate: `\uD83E\uDDEA \u6D4B\u8BD5\u6D88\u606F
|
|
24051
|
+
\u7AD9\u70B9\uFF1A{{siteName}}
|
|
24052
|
+
\u65F6\u95F4\uFF1A{{time}}`,
|
|
24053
|
+
approveMessageTemplate: `\u2705 \u5DF2\u901A\u8FC7 tmex \u6388\u6743\u3002
|
|
24054
|
+
\u8D26\u53F7\uFF1A{{accountName}}
|
|
24055
|
+
\u65F6\u95F4\uFF1A{{time}}`,
|
|
24056
|
+
keepalivePrompt: "\u3010tmex\u3011\u4E3A\u786E\u4FDD\u4F60\u80FD\u6B63\u5E38\u6536\u5230\u901A\u77E5\uFF0C\u8BF7\u56DE\u590D\u4EFB\u610F\u5185\u5BB9\u5373\u53EF\u4FDD\u6301\u8FDE\u63A5 \uD83D\uDE4F",
|
|
24057
|
+
scanConfirmedSendHint: "\u626B\u7801\u6210\u529F\uFF01\u8BF7\u5728\u5FAE\u4FE1\u91CC\u7ED9\u8BE5 bot \u53D1\u9001\u4EFB\u610F\u4E00\u6761\u6D88\u606F\u4EE5\u5B8C\u6210\u7ED1\u5B9A\u3002",
|
|
24058
|
+
bindingInProgress: "\u5DF2\u6536\u5230\u6D88\u606F\uFF0C\u6B63\u5728\u5B8C\u6210\u7ED1\u5B9A\u2026",
|
|
24059
|
+
bindSuccess: "\u5FAE\u4FE1\u5DF2\u7ED1\u5B9A\uFF0C\u5F00\u59CB\u63A5\u6536\u901A\u77E5\u3002",
|
|
24060
|
+
bound: "\u5DF2\u7ED1\u5B9A",
|
|
24061
|
+
unbound: "\u672A\u7ED1\u5B9A",
|
|
24062
|
+
bindAction: "\u626B\u7801\u7ED1\u5B9A",
|
|
24063
|
+
gatewayOnline: "\uD83D\uDFE2 tmex \u5DF2\u4E0A\u7EBF @ {{siteName}}"
|
|
24064
|
+
},
|
|
23901
24065
|
webhook: {
|
|
23902
24066
|
title: "Webhooks",
|
|
23903
24067
|
url: "Webhook \u5730\u5740",
|
|
@@ -24482,6 +24646,8 @@ Bot\uFF1A{{botName}}
|
|
|
24482
24646
|
session: "Tmux \u30BB\u30C3\u30B7\u30E7\u30F3\u540D",
|
|
24483
24647
|
sessionPlaceholder: "tmex",
|
|
24484
24648
|
sessionHint: "\u7A7A\u306E\u5834\u5408\u3001\u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u300Ctmex\u300D\u304C\u4F7F\u7528\u3055\u308C\u307E\u3059",
|
|
24649
|
+
defaultWorkingDir: "\u30C7\u30D5\u30A9\u30EB\u30C8\u4F5C\u696D\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA",
|
|
24650
|
+
defaultWorkingDirPlaceholder: "\u7A7A\u306E\u5834\u5408\u3001\u30E6\u30FC\u30B6\u30FC\u306E\u30DB\u30FC\u30E0\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u4F7F\u7528",
|
|
24485
24651
|
authMode: "\u8A8D\u8A3C\u30E2\u30FC\u30C9",
|
|
24486
24652
|
authPassword: "\u30D1\u30B9\u30EF\u30FC\u30C9",
|
|
24487
24653
|
authKey: "\u79D8\u5BC6\u9375",
|
|
@@ -24536,9 +24702,12 @@ Bot\uFF1A{{botName}}
|
|
|
24536
24702
|
noDeviceSelected: "\u30C7\u30D0\u30A4\u30B9\u304C\u9078\u629E\u3055\u308C\u3066\u3044\u307E\u305B\u3093",
|
|
24537
24703
|
windowClosed: "\u73FE\u5728\u306E\u30A6\u30A3\u30F3\u30C9\u30A6\u306F\u9589\u3058\u3089\u308C\u307E\u3057\u305F\u3002\u30B5\u30A4\u30C9\u30D0\u30FC\u304B\u3089\u30A6\u30A3\u30F3\u30C9\u30A6\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
24538
24704
|
paneClosed: "\u73FE\u5728\u306E\u30DA\u30A4\u30F3\u306F\u9589\u3058\u3089\u308C\u307E\u3057\u305F\u3002\u30B5\u30A4\u30C9\u30D0\u30FC\u304B\u3089\u30DA\u30A4\u30F3\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
24539
|
-
bellNotification: "\u30BF\u30FC\u30DF\u30CA\u30EB\u30D9\u30EB
|
|
24540
|
-
|
|
24705
|
+
bellNotification: "\u30BF\u30FC\u30DF\u30CA\u30EB\u30D9\u30EB",
|
|
24706
|
+
bellDescriptionWithTitle: "\u30A6\u30A3\u30F3\u30C9\u30A6 {{window}} \xB7 {{paneLabel}}",
|
|
24541
24707
|
bellFallback: "tmux \u30D9\u30EB\u3092\u53D7\u4FE1",
|
|
24708
|
+
notificationFallbackTitle: "\u30BF\u30FC\u30DF\u30CA\u30EB\u901A\u77E5",
|
|
24709
|
+
notificationSourceLabel: "{{source}} \u304B\u3089",
|
|
24710
|
+
notificationFallbackDetail: "\u30BF\u30FC\u30DF\u30CA\u30EB\u901A\u77E5",
|
|
24542
24711
|
paneTitle: "\u30DA\u30A4\u30F3 {{index}}",
|
|
24543
24712
|
activePane: "\u73FE\u5728\u306E\u30DA\u30A4\u30F3",
|
|
24544
24713
|
activeWindow: "\u73FE\u5728\u306E\u30A6\u30A3\u30F3\u30C9\u30A6",
|
|
@@ -24573,6 +24742,8 @@ Bot\uFF1A{{botName}}
|
|
|
24573
24742
|
enableBrowserNotificationToast: "\u30D6\u30E9\u30A6\u30B6\u901A\u77E5 Toast \u3092\u6709\u52B9\u306B\u3059\u308B",
|
|
24574
24743
|
enableTelegramBellPush: "Telegram \u30D9\u30EB\u30D7\u30C3\u30B7\u30E5\u3092\u6709\u52B9\u306B\u3059\u308B",
|
|
24575
24744
|
enableTelegramNotificationPush: "Telegram \u901A\u77E5\u30D7\u30C3\u30B7\u30E5\u3092\u6709\u52B9\u306B\u3059\u308B",
|
|
24745
|
+
enableWeixinBellPush: "WeChat \u30D9\u30EB\u901A\u77E5\u3092\u6709\u52B9\u5316",
|
|
24746
|
+
enableWeixinNotificationPush: "WeChat \u901A\u77E5\u30D7\u30C3\u30B7\u30E5\u3092\u6709\u52B9\u5316",
|
|
24576
24747
|
sshReconnectRetries: "SSH \u518D\u63A5\u7D9A\u8A66\u884C\u56DE\u6570",
|
|
24577
24748
|
sshReconnectDelay: "SSH \u518D\u63A5\u7D9A\u5F85\u6A5F\uFF08\u79D2\uFF09",
|
|
24578
24749
|
language: "\u8A00\u8A9E",
|
|
@@ -24812,6 +24983,81 @@ Bot\uFF1A{{botName}}
|
|
|
24812
24983
|
botNotFound: "Bot \u304C\u898B\u3064\u304B\u3089\u306A\u3044\u304B\u5229\u7528\u3067\u304D\u307E\u305B\u3093",
|
|
24813
24984
|
botNotRunning: "Bot \u304C\u8D77\u52D5\u3057\u3066\u3044\u306A\u3044\u304B\u5229\u7528\u3067\u304D\u307E\u305B\u3093"
|
|
24814
24985
|
},
|
|
24986
|
+
weixin: {
|
|
24987
|
+
title: "WeChat (ClawBot) \u7BA1\u7406",
|
|
24988
|
+
subtitle: "iLink bot \u30D7\u30ED\u30C8\u30B3\u30EB\u3067 tmex \u306E\u30A2\u30E9\u30FC\u30C8\u3092\u500B\u4EBA WeChat \u3078\u9001\u4FE1\u3057\u307E\u3059\u3002",
|
|
24989
|
+
replyOnlyNotice: "iLink \u306F\u30A2\u30AF\u30C6\u30A3\u30D6\u306A\u4F1A\u8A71\u30A6\u30A3\u30F3\u30C9\u30A6\u5185\u3067\u306E\u307F\u8FD4\u4FE1\u3067\u304D\u307E\u3059\u3002\u307E\u305A bot \u306B\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u3063\u3066\u6709\u52B9\u5316\u3059\u308B\u3068\u3001\u4EE5\u964D\u306E\u30A2\u30E9\u30FC\u30C8\u306F\u305D\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3092\u518D\u5229\u7528\u3057\u307E\u3059\u3002\u4E00\u5B9A\u6642\u9593\u64CD\u4F5C\u304C\u306A\u3044\u3068\u30BB\u30C3\u30B7\u30E7\u30F3\u306F\u5931\u52B9\u3059\u308B\u305F\u3081\u3001\u518D\u5EA6 bot \u306B\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u3063\u3066\u518D\u6709\u52B9\u5316\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
24990
|
+
accountName: "\u30A2\u30AB\u30A6\u30F3\u30C8\u540D",
|
|
24991
|
+
accountNamePlaceholder: "\u4F8B\uFF1Amy-wechat",
|
|
24992
|
+
addAccount: "\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u8FFD\u52A0",
|
|
24993
|
+
editAccount: "\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u7DE8\u96C6",
|
|
24994
|
+
enableAccount: "\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u6709\u52B9\u5316",
|
|
24995
|
+
allowAuthRequests: "\u8A8D\u8A3C\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u8A31\u53EF",
|
|
24996
|
+
scanToLogin: "\u30B9\u30AD\u30E3\u30F3\u3057\u3066\u30ED\u30B0\u30A4\u30F3",
|
|
24997
|
+
relogin: "\u518D\u30ED\u30B0\u30A4\u30F3\uFF08\u518D\u30B9\u30AD\u30E3\u30F3\uFF09",
|
|
24998
|
+
loggedIn: "\u30ED\u30B0\u30A4\u30F3\u6E08\u307F",
|
|
24999
|
+
notLoggedIn: "\u672A\u30ED\u30B0\u30A4\u30F3",
|
|
25000
|
+
scanQrcodeHint: "WeChat \u3092\u958B\u3044\u3066 QR \u30B3\u30FC\u30C9\u3092\u30B9\u30AD\u30E3\u30F3\u3057\u3001\u30B9\u30DE\u30DB\u3067\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
25001
|
+
loginPending: "\u30B9\u30AD\u30E3\u30F3\u5F85\u3061\u2026",
|
|
25002
|
+
loginConfirmed: "\u30ED\u30B0\u30A4\u30F3\u304C\u78BA\u8A8D\u3055\u308C\u307E\u3057\u305F\u3002",
|
|
25003
|
+
loginExpired: "QR \u30B3\u30FC\u30C9\u306E\u6709\u52B9\u671F\u9650\u304C\u5207\u308C\u307E\u3057\u305F\u3002\u518D\u8A66\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
25004
|
+
loginError: "\u30ED\u30B0\u30A4\u30F3\u5931\u6557\uFF1A{{message}}",
|
|
25005
|
+
loginFailed: "\u30ED\u30B0\u30A4\u30F3\u5931\u6557",
|
|
25006
|
+
refreshQrcode: "QR \u30B3\u30FC\u30C9\u3092\u66F4\u65B0",
|
|
25007
|
+
closeLogin: "\u9589\u3058\u308B",
|
|
25008
|
+
pendingUsers: "\u627F\u8A8D\u5F85\u3061",
|
|
25009
|
+
authorizedUsers: "\u627F\u8A8D\u6E08\u307F\u30E6\u30FC\u30B6\u30FC",
|
|
25010
|
+
noPendingUsers: "\u627F\u8A8D\u5F85\u3061\u306E\u30E6\u30FC\u30B6\u30FC\u306F\u3044\u307E\u305B\u3093",
|
|
25011
|
+
noAuthorizedUsers: "\u627F\u8A8D\u6E08\u307F\u306E\u30E6\u30FC\u30B6\u30FC\u306F\u3044\u307E\u305B\u3093",
|
|
25012
|
+
needsReactivation: "\u30BB\u30C3\u30B7\u30E7\u30F3\u5931\u52B9",
|
|
25013
|
+
reactivationHint: "\u3053\u306E\u30E6\u30FC\u30B6\u30FC\u306B bot \u3078\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u3063\u3066\u3082\u3089\u3044\u3001\u30A2\u30E9\u30FC\u30C8\u3092\u518D\u6709\u52B9\u5316\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
25014
|
+
approve: "\u627F\u8A8D",
|
|
25015
|
+
revokeAuth: "\u627F\u8A8D\u3092\u53D6\u308A\u6D88\u3059",
|
|
25016
|
+
removeUser: "\u524A\u9664",
|
|
25017
|
+
testMessage: "\u30C6\u30B9\u30C8\u30E1\u30C3\u30BB\u30FC\u30B8",
|
|
25018
|
+
sendTestMessage: "\u30C6\u30B9\u30C8\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u4FE1",
|
|
25019
|
+
deleteAccount: "\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u524A\u9664",
|
|
25020
|
+
accountCreated: "\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u4F5C\u6210\u3057\u307E\u3057\u305F",
|
|
25021
|
+
accountUpdated: "\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u66F4\u65B0\u3057\u307E\u3057\u305F",
|
|
25022
|
+
accountDeleted: "\u30A2\u30AB\u30A6\u30F3\u30C8\u3092\u524A\u9664\u3057\u307E\u3057\u305F",
|
|
25023
|
+
authApproved: "\u8A8D\u8A3C\u3092\u627F\u8A8D\u3057\u307E\u3057\u305F",
|
|
25024
|
+
userRemoved: "\u30E6\u30FC\u30B6\u30FC\u3092\u524A\u9664\u3057\u307E\u3057\u305F",
|
|
25025
|
+
testMessageSent: "\u30C6\u30B9\u30C8\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u4FE1\u3057\u307E\u3057\u305F",
|
|
25026
|
+
createFailed: "\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u4F5C\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25027
|
+
updateFailed: "\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u66F4\u65B0\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25028
|
+
deleteFailed: "\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u524A\u9664\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25029
|
+
approveFailed: "\u627F\u8A8D\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25030
|
+
removeFailed: "\u30E6\u30FC\u30B6\u30FC\u306E\u524A\u9664\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25031
|
+
testMessageFailed: "\u30C6\u30B9\u30C8\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u9001\u4FE1\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25032
|
+
loadAccountsFailed: "\u30A2\u30AB\u30A6\u30F3\u30C8\u4E00\u89A7\u306E\u8AAD\u307F\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25033
|
+
loadUsersFailed: "\u30E6\u30FC\u30B6\u30FC\u4E00\u89A7\u306E\u8AAD\u307F\u8FBC\u307F\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
25034
|
+
noAccounts: "\u30A2\u30AB\u30A6\u30F3\u30C8\u304C\u307E\u3060\u3042\u308A\u307E\u305B\u3093\u3002\u307E\u305A\u8FFD\u52A0\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
25035
|
+
userCount: "{{authorized}} \u627F\u8A8D\u6E08\u307F / {{pending}} \u627F\u8A8D\u5F85\u3061\uFF08\u6700\u5927 16\uFF09",
|
|
25036
|
+
userId: "WeChat \u30E6\u30FC\u30B6\u30FC ID",
|
|
25037
|
+
applyTime: "\u521D\u56DE\u9023\u7D61",
|
|
25038
|
+
expand: "\u5C55\u958B",
|
|
25039
|
+
collapse: "\u6298\u308A\u305F\u305F\u3080",
|
|
25040
|
+
accountNameRequired: "\u30A2\u30AB\u30A6\u30F3\u30C8\u540D\u306F\u5FC5\u9808\u3067\u3059",
|
|
25041
|
+
accountNotFound: "\u30A2\u30AB\u30A6\u30F3\u30C8\u304C\u898B\u3064\u304B\u3089\u306A\u3044\u304B\u5229\u7528\u3067\u304D\u307E\u305B\u3093",
|
|
25042
|
+
accountNotRunning: "\u30A2\u30AB\u30A6\u30F3\u30C8\u304C\u672A\u30ED\u30B0\u30A4\u30F3\u307E\u305F\u306F\u672A\u8D77\u52D5\u3067\u3059",
|
|
25043
|
+
userNotFound: "\u30E6\u30FC\u30B6\u30FC\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093",
|
|
25044
|
+
authSuccess: "\u2705 \u8A8D\u8A3C\u3055\u308C\u307E\u3057\u305F\u3002\u901A\u77E5\u3092\u53D7\u3051\u53D6\u308C\u307E\u3059\u3002",
|
|
25045
|
+
authPending: "\u23F3 \u8A8D\u8A3C\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u53D7\u3051\u53D6\u308A\u307E\u3057\u305F\u3002tmex \u306E\u8A2D\u5B9A\u3067\u627F\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
25046
|
+
testMessageTemplate: `\uD83E\uDDEA \u30C6\u30B9\u30C8\u30E1\u30C3\u30BB\u30FC\u30B8
|
|
25047
|
+
\u30B5\u30A4\u30C8\uFF1A{{siteName}}
|
|
25048
|
+
\u6642\u523B\uFF1A{{time}}`,
|
|
25049
|
+
approveMessageTemplate: `\u2705 tmex \u7D4C\u7531\u3067\u8A8D\u8A3C\u3057\u307E\u3057\u305F\u3002
|
|
25050
|
+
\u30A2\u30AB\u30A6\u30F3\u30C8\uFF1A{{accountName}}
|
|
25051
|
+
\u6642\u523B\uFF1A{{time}}`,
|
|
25052
|
+
keepalivePrompt: "\u3010tmex\u3011\u901A\u77E5\u3092\u78BA\u5B9F\u306B\u53D7\u3051\u53D6\u308B\u305F\u3081\u3001\u4EFB\u610F\u306E\u5185\u5BB9\u3092\u8FD4\u4FE1\u3057\u3066\u63A5\u7D9A\u3092\u7DAD\u6301\u3057\u3066\u304F\u3060\u3055\u3044 \uD83D\uDE4F",
|
|
25053
|
+
scanConfirmedSendHint: "\u30B9\u30AD\u30E3\u30F3\u5B8C\u4E86\uFF01WeChat \u3067\u3053\u306E bot \u306B\u4EFB\u610F\u306E\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u9001\u4FE1\u3057\u3066\u7D10\u4ED8\u3051\u3092\u5B8C\u4E86\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
25054
|
+
bindingInProgress: "\u30E1\u30C3\u30BB\u30FC\u30B8\u3092\u53D7\u4FE1\u3001\u7D10\u4ED8\u3051\u3092\u5B8C\u4E86\u3057\u3066\u3044\u307E\u3059\u2026",
|
|
25055
|
+
bindSuccess: "WeChat \u3092\u7D10\u4ED8\u3051\u307E\u3057\u305F\u3002\u901A\u77E5\u3092\u53D7\u3051\u53D6\u308C\u307E\u3059\u3002",
|
|
25056
|
+
bound: "\u7D10\u4ED8\u3051\u6E08\u307F",
|
|
25057
|
+
unbound: "\u672A\u7D10\u4ED8\u3051",
|
|
25058
|
+
bindAction: "\u30B9\u30AD\u30E3\u30F3\u3057\u3066\u7D10\u4ED8\u3051",
|
|
25059
|
+
gatewayOnline: "\uD83D\uDFE2 tmex \u30AA\u30F3\u30E9\u30A4\u30F3 @ {{siteName}}"
|
|
25060
|
+
},
|
|
24815
25061
|
webhook: {
|
|
24816
25062
|
title: "Webhooks",
|
|
24817
25063
|
url: "Webhook URL",
|
|
@@ -25396,6 +25642,7 @@ __export(exports_ws_borsh, {
|
|
|
25396
25642
|
KIND_DEVICE_DISCONNECT: () => KIND_DEVICE_DISCONNECT,
|
|
25397
25643
|
KIND_DEVICE_CONNECTED: () => KIND_DEVICE_CONNECTED,
|
|
25398
25644
|
KIND_DEVICE_CONNECT: () => KIND_DEVICE_CONNECT,
|
|
25645
|
+
KIND_CLIPBOARD_WRITE: () => KIND_CLIPBOARD_WRITE,
|
|
25399
25646
|
KIND_CHUNK: () => KIND_CHUNK,
|
|
25400
25647
|
KIND_AGENT_UNSUBSCRIBE: () => KIND_AGENT_UNSUBSCRIBE,
|
|
25401
25648
|
KIND_AGENT_SUBSCRIBE: () => KIND_AGENT_SUBSCRIBE,
|
|
@@ -25467,6 +25714,7 @@ var KIND_TERM_RESIZE = 771;
|
|
|
25467
25714
|
var KIND_TERM_SYNC_SIZE = 772;
|
|
25468
25715
|
var KIND_TERM_OUTPUT = 773;
|
|
25469
25716
|
var KIND_TERM_HISTORY = 774;
|
|
25717
|
+
var KIND_CLIPBOARD_WRITE = 775;
|
|
25470
25718
|
var KIND_SWITCH_ACK = 1025;
|
|
25471
25719
|
var KIND_LIVE_RESUME = 1026;
|
|
25472
25720
|
var KIND_CHUNK = 1281;
|
|
@@ -25503,6 +25751,7 @@ var VALID_KINDS = new Set([
|
|
|
25503
25751
|
KIND_TERM_SYNC_SIZE,
|
|
25504
25752
|
KIND_TERM_OUTPUT,
|
|
25505
25753
|
KIND_TERM_HISTORY,
|
|
25754
|
+
KIND_CLIPBOARD_WRITE,
|
|
25506
25755
|
KIND_SWITCH_ACK,
|
|
25507
25756
|
KIND_LIVE_RESUME,
|
|
25508
25757
|
KIND_CHUNK,
|
|
@@ -25544,6 +25793,7 @@ function kindToString(kind) {
|
|
|
25544
25793
|
[KIND_TERM_SYNC_SIZE]: "TERM_SYNC_SIZE",
|
|
25545
25794
|
[KIND_TERM_OUTPUT]: "TERM_OUTPUT",
|
|
25546
25795
|
[KIND_TERM_HISTORY]: "TERM_HISTORY",
|
|
25796
|
+
[KIND_CLIPBOARD_WRITE]: "CLIPBOARD_WRITE",
|
|
25547
25797
|
[KIND_SWITCH_ACK]: "SWITCH_ACK",
|
|
25548
25798
|
[KIND_LIVE_RESUME]: "LIVE_RESUME",
|
|
25549
25799
|
[KIND_CHUNK]: "CHUNK",
|
|
@@ -25661,6 +25911,7 @@ __export(exports_schema, {
|
|
|
25661
25911
|
DeviceDisconnectSchema: () => DeviceDisconnectSchema,
|
|
25662
25912
|
DeviceConnectedSchema: () => DeviceConnectedSchema,
|
|
25663
25913
|
DeviceConnectSchema: () => DeviceConnectSchema,
|
|
25914
|
+
ClipboardWriteSchema: () => ClipboardWriteSchema,
|
|
25664
25915
|
ChunkSchema: () => ChunkSchema,
|
|
25665
25916
|
BellEventSchema: () => BellEventSchema,
|
|
25666
25917
|
AgentUnsubscribeSchema: () => AgentUnsubscribeSchema,
|
|
@@ -25806,6 +26057,11 @@ var TermHistorySchema = import_zorsh.b.struct({
|
|
|
25806
26057
|
alternateScreen: import_zorsh.b.bool(),
|
|
25807
26058
|
data: import_zorsh.b.bytes()
|
|
25808
26059
|
});
|
|
26060
|
+
var ClipboardWriteSchema = import_zorsh.b.struct({
|
|
26061
|
+
deviceId: import_zorsh.b.string(),
|
|
26062
|
+
paneId: import_zorsh.b.string(),
|
|
26063
|
+
text: import_zorsh.b.string()
|
|
26064
|
+
});
|
|
25809
26065
|
var SwitchAckSchema = import_zorsh.b.struct({
|
|
25810
26066
|
deviceId: import_zorsh.b.string(),
|
|
25811
26067
|
windowId: import_zorsh.b.string(),
|
|
@@ -25910,7 +26166,9 @@ var BellEventSchema = import_zorsh.b.struct({
|
|
|
25910
26166
|
paneId: OptionStringSchema,
|
|
25911
26167
|
windowIndex: OptionU16Schema,
|
|
25912
26168
|
paneIndex: OptionU16Schema,
|
|
25913
|
-
paneUrl: OptionStringSchema
|
|
26169
|
+
paneUrl: OptionStringSchema,
|
|
26170
|
+
paneTitle: OptionStringSchema,
|
|
26171
|
+
paneCurrentCommand: OptionStringSchema
|
|
25914
26172
|
});
|
|
25915
26173
|
var NotificationEventSchema = import_zorsh.b.struct({
|
|
25916
26174
|
source: import_zorsh.b.u8(),
|
|
@@ -25920,7 +26178,9 @@ var NotificationEventSchema = import_zorsh.b.struct({
|
|
|
25920
26178
|
paneId: OptionStringSchema,
|
|
25921
26179
|
windowIndex: OptionU16Schema,
|
|
25922
26180
|
paneIndex: OptionU16Schema,
|
|
25923
|
-
paneUrl: OptionStringSchema
|
|
26181
|
+
paneUrl: OptionStringSchema,
|
|
26182
|
+
paneTitle: OptionStringSchema,
|
|
26183
|
+
paneCurrentCommand: OptionStringSchema
|
|
25924
26184
|
});
|
|
25925
26185
|
// ../shared/src/ws-borsh/codec.ts
|
|
25926
26186
|
var MAGIC = new Uint8Array([84, 88]);
|
|
@@ -26237,7 +26497,9 @@ function encodeEventData(type, data) {
|
|
|
26237
26497
|
paneId: d.paneId ?? null,
|
|
26238
26498
|
windowIndex: d.windowIndex ?? null,
|
|
26239
26499
|
paneIndex: d.paneIndex ?? null,
|
|
26240
|
-
paneUrl: d.paneUrl ?? null
|
|
26500
|
+
paneUrl: d.paneUrl ?? null,
|
|
26501
|
+
paneTitle: d.paneTitle ?? null,
|
|
26502
|
+
paneCurrentCommand: d.paneCurrentCommand ?? null
|
|
26241
26503
|
});
|
|
26242
26504
|
}
|
|
26243
26505
|
case "output":
|
|
@@ -26252,7 +26514,9 @@ function encodeEventData(type, data) {
|
|
|
26252
26514
|
paneId: d.paneId ?? null,
|
|
26253
26515
|
windowIndex: d.windowIndex ?? null,
|
|
26254
26516
|
paneIndex: d.paneIndex ?? null,
|
|
26255
|
-
paneUrl: d.paneUrl ?? null
|
|
26517
|
+
paneUrl: d.paneUrl ?? null,
|
|
26518
|
+
paneTitle: d.paneTitle ?? null,
|
|
26519
|
+
paneCurrentCommand: d.paneCurrentCommand ?? null
|
|
26256
26520
|
});
|
|
26257
26521
|
}
|
|
26258
26522
|
default:
|
|
@@ -26363,7 +26627,9 @@ function decodeEventData(type, data) {
|
|
|
26363
26627
|
paneId: bell.paneId ?? undefined,
|
|
26364
26628
|
windowIndex: bell.windowIndex ?? undefined,
|
|
26365
26629
|
paneIndex: bell.paneIndex ?? undefined,
|
|
26366
|
-
paneUrl: bell.paneUrl ?? undefined
|
|
26630
|
+
paneUrl: bell.paneUrl ?? undefined,
|
|
26631
|
+
paneTitle: bell.paneTitle ?? undefined,
|
|
26632
|
+
paneCurrentCommand: bell.paneCurrentCommand ?? undefined
|
|
26367
26633
|
};
|
|
26368
26634
|
}
|
|
26369
26635
|
case "notification": {
|
|
@@ -26376,7 +26642,9 @@ function decodeEventData(type, data) {
|
|
|
26376
26642
|
paneId: notification.paneId ?? undefined,
|
|
26377
26643
|
windowIndex: notification.windowIndex ?? undefined,
|
|
26378
26644
|
paneIndex: notification.paneIndex ?? undefined,
|
|
26379
|
-
paneUrl: notification.paneUrl ?? undefined
|
|
26645
|
+
paneUrl: notification.paneUrl ?? undefined,
|
|
26646
|
+
paneTitle: notification.paneTitle ?? undefined,
|
|
26647
|
+
paneCurrentCommand: notification.paneCurrentCommand ?? undefined
|
|
26380
26648
|
};
|
|
26381
26649
|
}
|
|
26382
26650
|
default:
|
|
@@ -32855,6 +33123,8 @@ function drizzle(...params) {
|
|
|
32855
33123
|
// ../../apps/gateway/src/db/schema.ts
|
|
32856
33124
|
var exports_schema2 = {};
|
|
32857
33125
|
__export(exports_schema2, {
|
|
33126
|
+
weixinAccounts: () => weixinAccounts,
|
|
33127
|
+
weixinAccountUsers: () => weixinAccountUsers,
|
|
32858
33128
|
webhookEndpoints: () => webhookEndpoints,
|
|
32859
33129
|
watchRules: () => watchRules,
|
|
32860
33130
|
watchRuleState: () => watchRuleState,
|
|
@@ -32887,6 +33157,10 @@ var siteSettings = sqliteTable("site_settings", {
|
|
|
32887
33157
|
enableTelegramNotificationPush: integer("enable_telegram_notification_push", {
|
|
32888
33158
|
mode: "boolean"
|
|
32889
33159
|
}).notNull().default(true),
|
|
33160
|
+
enableWeixinBellPush: integer("enable_weixin_bell_push", { mode: "boolean" }).notNull().default(false),
|
|
33161
|
+
enableWeixinNotificationPush: integer("enable_weixin_notification_push", {
|
|
33162
|
+
mode: "boolean"
|
|
33163
|
+
}).notNull().default(false),
|
|
32890
33164
|
sshReconnectMaxRetries: integer("ssh_reconnect_max_retries").notNull(),
|
|
32891
33165
|
sshReconnectDelaySeconds: integer("ssh_reconnect_delay_seconds").notNull(),
|
|
32892
33166
|
language: text("language").notNull().default("en_US"),
|
|
@@ -32911,6 +33185,7 @@ var devices = sqliteTable("devices", {
|
|
|
32911
33185
|
passwordEnc: text("password_enc"),
|
|
32912
33186
|
privateKeyEnc: text("private_key_enc"),
|
|
32913
33187
|
privateKeyPassphraseEnc: text("private_key_passphrase_enc"),
|
|
33188
|
+
defaultWorkingDir: text("default_working_dir"),
|
|
32914
33189
|
sortOrder: integer("sort_order").notNull().default(0),
|
|
32915
33190
|
createdAt: text("created_at").notNull(),
|
|
32916
33191
|
updatedAt: text("updated_at").notNull()
|
|
@@ -33091,6 +33366,34 @@ var telegramBotChats = sqliteTable("telegram_bot_chats", {
|
|
|
33091
33366
|
check("telegram_bot_chats_status_check", sql`${table.status} in ('pending', 'authorized')`),
|
|
33092
33367
|
check("telegram_bot_chats_chat_type_check", sql`${table.chatType} in ('private', 'group', 'supergroup', 'channel', 'unknown')`)
|
|
33093
33368
|
]);
|
|
33369
|
+
var weixinAccounts = sqliteTable("weixin_accounts", {
|
|
33370
|
+
id: text("id").primaryKey(),
|
|
33371
|
+
name: text("name").notNull(),
|
|
33372
|
+
enabled: integer("enabled", { mode: "boolean" }).notNull().default(true),
|
|
33373
|
+
allowAuthRequests: integer("allow_auth_requests", { mode: "boolean" }).notNull().default(true),
|
|
33374
|
+
weixinUin: text("weixin_uin"),
|
|
33375
|
+
botTokenEnc: text("bot_token_enc"),
|
|
33376
|
+
baseUrl: text("base_url"),
|
|
33377
|
+
syncBuf: text("sync_buf"),
|
|
33378
|
+
createdAt: text("created_at").notNull(),
|
|
33379
|
+
updatedAt: text("updated_at").notNull()
|
|
33380
|
+
});
|
|
33381
|
+
var weixinAccountUsers = sqliteTable("weixin_account_users", {
|
|
33382
|
+
id: text("id").primaryKey(),
|
|
33383
|
+
accountId: text("account_id").notNull().references(() => weixinAccounts.id, { onDelete: "cascade" }),
|
|
33384
|
+
userId: text("user_id").notNull(),
|
|
33385
|
+
displayName: text("display_name").notNull(),
|
|
33386
|
+
status: text("status").notNull(),
|
|
33387
|
+
lastContextToken: text("last_context_token"),
|
|
33388
|
+
lastInboundAt: text("last_inbound_at"),
|
|
33389
|
+
needsReactivation: integer("needs_reactivation", { mode: "boolean" }).notNull().default(false),
|
|
33390
|
+
appliedAt: text("applied_at").notNull(),
|
|
33391
|
+
authorizedAt: text("authorized_at"),
|
|
33392
|
+
updatedAt: text("updated_at").notNull()
|
|
33393
|
+
}, (table) => [
|
|
33394
|
+
unique("weixin_account_users_account_user_unique").on(table.accountId, table.userId),
|
|
33395
|
+
check("weixin_account_users_status_check", sql`${table.status} in ('pending', 'authorized')`)
|
|
33396
|
+
]);
|
|
33094
33397
|
|
|
33095
33398
|
// ../../apps/gateway/src/db/client.ts
|
|
33096
33399
|
var sqliteClient = null;
|
|
@@ -33136,6 +33439,7 @@ function toDevice(row) {
|
|
|
33136
33439
|
passwordEnc: optional(row.passwordEnc),
|
|
33137
33440
|
privateKeyEnc: optional(row.privateKeyEnc),
|
|
33138
33441
|
privateKeyPassphraseEnc: optional(row.privateKeyPassphraseEnc),
|
|
33442
|
+
defaultWorkingDir: optional(row.defaultWorkingDir),
|
|
33139
33443
|
sortOrder: row.sortOrder,
|
|
33140
33444
|
createdAt: row.createdAt,
|
|
33141
33445
|
updatedAt: row.updatedAt
|
|
@@ -33151,6 +33455,8 @@ function toSiteSettings(row) {
|
|
|
33151
33455
|
enableBrowserNotificationToast: row.enableBrowserNotificationToast,
|
|
33152
33456
|
enableTelegramBellPush: row.enableTelegramBellPush,
|
|
33153
33457
|
enableTelegramNotificationPush: row.enableTelegramNotificationPush,
|
|
33458
|
+
enableWeixinBellPush: row.enableWeixinBellPush,
|
|
33459
|
+
enableWeixinNotificationPush: row.enableWeixinNotificationPush,
|
|
33154
33460
|
sshReconnectMaxRetries: row.sshReconnectMaxRetries,
|
|
33155
33461
|
sshReconnectDelaySeconds: row.sshReconnectDelaySeconds,
|
|
33156
33462
|
language: normalizeLocale(row.language),
|
|
@@ -33182,6 +33488,35 @@ function toTelegramChat(row) {
|
|
|
33182
33488
|
updatedAt: row.updatedAt
|
|
33183
33489
|
};
|
|
33184
33490
|
}
|
|
33491
|
+
function toWeixinAccountRecord(row) {
|
|
33492
|
+
return {
|
|
33493
|
+
id: row.id,
|
|
33494
|
+
name: row.name,
|
|
33495
|
+
enabled: row.enabled,
|
|
33496
|
+
allowAuthRequests: row.allowAuthRequests,
|
|
33497
|
+
loggedIn: row.botTokenEnc != null,
|
|
33498
|
+
weixinUin: row.weixinUin ?? null,
|
|
33499
|
+
botTokenEnc: row.botTokenEnc ?? null,
|
|
33500
|
+
baseUrl: row.baseUrl ?? null,
|
|
33501
|
+
syncBuf: row.syncBuf ?? null,
|
|
33502
|
+
createdAt: row.createdAt,
|
|
33503
|
+
updatedAt: row.updatedAt
|
|
33504
|
+
};
|
|
33505
|
+
}
|
|
33506
|
+
function toWeixinAccountUser(row) {
|
|
33507
|
+
return {
|
|
33508
|
+
id: row.id,
|
|
33509
|
+
accountId: row.accountId,
|
|
33510
|
+
userId: row.userId,
|
|
33511
|
+
displayName: row.displayName,
|
|
33512
|
+
status: row.status,
|
|
33513
|
+
needsReactivation: row.needsReactivation,
|
|
33514
|
+
lastInboundAt: row.lastInboundAt ?? null,
|
|
33515
|
+
appliedAt: row.appliedAt,
|
|
33516
|
+
authorizedAt: row.authorizedAt ?? null,
|
|
33517
|
+
updatedAt: row.updatedAt
|
|
33518
|
+
};
|
|
33519
|
+
}
|
|
33185
33520
|
function toWebhookEndpoint(row) {
|
|
33186
33521
|
return {
|
|
33187
33522
|
id: row.id,
|
|
@@ -33206,6 +33541,8 @@ function ensureSiteSettingsInitialized() {
|
|
|
33206
33541
|
enableBrowserNotificationToast: true,
|
|
33207
33542
|
enableTelegramBellPush: true,
|
|
33208
33543
|
enableTelegramNotificationPush: true,
|
|
33544
|
+
enableWeixinBellPush: false,
|
|
33545
|
+
enableWeixinNotificationPush: false,
|
|
33209
33546
|
sshReconnectMaxRetries: config.sshReconnectMaxRetriesDefault,
|
|
33210
33547
|
sshReconnectDelaySeconds: config.sshReconnectDelaySecondsDefault,
|
|
33211
33548
|
language: normalizeLocale(config.languageDefault),
|
|
@@ -33230,6 +33567,7 @@ function createDevice(device) {
|
|
|
33230
33567
|
passwordEnc: device.passwordEnc ?? null,
|
|
33231
33568
|
privateKeyEnc: device.privateKeyEnc ?? null,
|
|
33232
33569
|
privateKeyPassphraseEnc: device.privateKeyPassphraseEnc ?? null,
|
|
33570
|
+
defaultWorkingDir: device.defaultWorkingDir ?? null,
|
|
33233
33571
|
sortOrder: nextSortOrder,
|
|
33234
33572
|
createdAt: device.createdAt,
|
|
33235
33573
|
updatedAt: device.updatedAt
|
|
@@ -33299,6 +33637,9 @@ function updateDevice(id, updates) {
|
|
|
33299
33637
|
if (updates.privateKeyPassphraseEnc !== undefined) {
|
|
33300
33638
|
setValues.privateKeyPassphraseEnc = updates.privateKeyPassphraseEnc;
|
|
33301
33639
|
}
|
|
33640
|
+
if (updates.defaultWorkingDir !== undefined) {
|
|
33641
|
+
setValues.defaultWorkingDir = updates.defaultWorkingDir || null;
|
|
33642
|
+
}
|
|
33302
33643
|
orm.update(devices).set(setValues).where(eq(devices.id, id)).run();
|
|
33303
33644
|
}
|
|
33304
33645
|
function deleteDevice(id) {
|
|
@@ -33402,6 +33743,8 @@ function updateSiteSettings(updates) {
|
|
|
33402
33743
|
enableBrowserNotificationToast: updates.enableBrowserNotificationToast ?? current.enableBrowserNotificationToast,
|
|
33403
33744
|
enableTelegramBellPush: updates.enableTelegramBellPush ?? current.enableTelegramBellPush,
|
|
33404
33745
|
enableTelegramNotificationPush: updates.enableTelegramNotificationPush ?? current.enableTelegramNotificationPush,
|
|
33746
|
+
enableWeixinBellPush: updates.enableWeixinBellPush ?? current.enableWeixinBellPush,
|
|
33747
|
+
enableWeixinNotificationPush: updates.enableWeixinNotificationPush ?? current.enableWeixinNotificationPush,
|
|
33405
33748
|
sshReconnectMaxRetries: updates.sshReconnectMaxRetries ?? current.sshReconnectMaxRetries,
|
|
33406
33749
|
sshReconnectDelaySeconds: updates.sshReconnectDelaySeconds ?? current.sshReconnectDelaySeconds,
|
|
33407
33750
|
language: updates.language ? normalizeLocale(updates.language) : current.language,
|
|
@@ -33417,6 +33760,8 @@ function updateSiteSettings(updates) {
|
|
|
33417
33760
|
enableBrowserNotificationToast: next.enableBrowserNotificationToast,
|
|
33418
33761
|
enableTelegramBellPush: next.enableTelegramBellPush,
|
|
33419
33762
|
enableTelegramNotificationPush: next.enableTelegramNotificationPush,
|
|
33763
|
+
enableWeixinBellPush: next.enableWeixinBellPush,
|
|
33764
|
+
enableWeixinNotificationPush: next.enableWeixinNotificationPush,
|
|
33420
33765
|
sshReconnectMaxRetries: next.sshReconnectMaxRetries,
|
|
33421
33766
|
sshReconnectDelaySeconds: next.sshReconnectDelaySeconds,
|
|
33422
33767
|
language: next.language,
|
|
@@ -33650,6 +33995,200 @@ function deleteTelegramChat(botId, chatId) {
|
|
|
33650
33995
|
const orm = getDb();
|
|
33651
33996
|
orm.delete(telegramBotChats).where(and(eq(telegramBotChats.botId, botId), eq(telegramBotChats.chatId, chatId))).run();
|
|
33652
33997
|
}
|
|
33998
|
+
var WEIXIN_USER_CAP = 16;
|
|
33999
|
+
function createWeixinAccount(record) {
|
|
34000
|
+
const orm = getDb();
|
|
34001
|
+
orm.insert(weixinAccounts).values({
|
|
34002
|
+
id: record.id,
|
|
34003
|
+
name: record.name,
|
|
34004
|
+
enabled: record.enabled,
|
|
34005
|
+
allowAuthRequests: record.allowAuthRequests,
|
|
34006
|
+
weixinUin: record.weixinUin,
|
|
34007
|
+
botTokenEnc: record.botTokenEnc,
|
|
34008
|
+
baseUrl: record.baseUrl,
|
|
34009
|
+
syncBuf: record.syncBuf,
|
|
34010
|
+
createdAt: record.createdAt,
|
|
34011
|
+
updatedAt: record.updatedAt
|
|
34012
|
+
}).run();
|
|
34013
|
+
}
|
|
34014
|
+
function getWeixinAccountById(accountId) {
|
|
34015
|
+
const orm = getDb();
|
|
34016
|
+
const row = orm.select().from(weixinAccounts).where(eq(weixinAccounts.id, accountId)).get();
|
|
34017
|
+
if (!row) {
|
|
34018
|
+
return null;
|
|
34019
|
+
}
|
|
34020
|
+
return toWeixinAccountRecord(row);
|
|
34021
|
+
}
|
|
34022
|
+
function getAllWeixinAccounts() {
|
|
34023
|
+
const orm = getDb();
|
|
34024
|
+
return orm.select().from(weixinAccounts).orderBy(desc(weixinAccounts.createdAt)).all().map(toWeixinAccountRecord);
|
|
34025
|
+
}
|
|
34026
|
+
function getWeixinAccountsWithStats() {
|
|
34027
|
+
const orm = getDb();
|
|
34028
|
+
const accounts = orm.select().from(weixinAccounts).orderBy(desc(weixinAccounts.createdAt)).all();
|
|
34029
|
+
const counters = new Map;
|
|
34030
|
+
const userRows = orm.select({
|
|
34031
|
+
accountId: weixinAccountUsers.accountId,
|
|
34032
|
+
status: weixinAccountUsers.status,
|
|
34033
|
+
needsReactivation: weixinAccountUsers.needsReactivation
|
|
34034
|
+
}).from(weixinAccountUsers).all();
|
|
34035
|
+
for (const row of userRows) {
|
|
34036
|
+
const current = counters.get(row.accountId) ?? {
|
|
34037
|
+
pending: 0,
|
|
34038
|
+
authorized: 0,
|
|
34039
|
+
needsReactivation: 0
|
|
34040
|
+
};
|
|
34041
|
+
if (row.status === "pending") {
|
|
34042
|
+
current.pending += 1;
|
|
34043
|
+
}
|
|
34044
|
+
if (row.status === "authorized") {
|
|
34045
|
+
current.authorized += 1;
|
|
34046
|
+
if (row.needsReactivation) {
|
|
34047
|
+
current.needsReactivation += 1;
|
|
34048
|
+
}
|
|
34049
|
+
}
|
|
34050
|
+
counters.set(row.accountId, current);
|
|
34051
|
+
}
|
|
34052
|
+
return accounts.map((account) => {
|
|
34053
|
+
const counter = counters.get(account.id) ?? {
|
|
34054
|
+
pending: 0,
|
|
34055
|
+
authorized: 0,
|
|
34056
|
+
needsReactivation: 0
|
|
34057
|
+
};
|
|
34058
|
+
return {
|
|
34059
|
+
id: account.id,
|
|
34060
|
+
name: account.name,
|
|
34061
|
+
enabled: account.enabled,
|
|
34062
|
+
allowAuthRequests: account.allowAuthRequests,
|
|
34063
|
+
loggedIn: account.botTokenEnc != null,
|
|
34064
|
+
createdAt: account.createdAt,
|
|
34065
|
+
updatedAt: account.updatedAt,
|
|
34066
|
+
pendingCount: counter.pending,
|
|
34067
|
+
authorizedCount: counter.authorized,
|
|
34068
|
+
needsReactivationCount: counter.needsReactivation
|
|
34069
|
+
};
|
|
34070
|
+
});
|
|
34071
|
+
}
|
|
34072
|
+
function updateWeixinAccount(accountId, updates) {
|
|
34073
|
+
const orm = getDb();
|
|
34074
|
+
const setValues = {
|
|
34075
|
+
updatedAt: new Date().toISOString()
|
|
34076
|
+
};
|
|
34077
|
+
if (updates.name !== undefined) {
|
|
34078
|
+
setValues.name = updates.name;
|
|
34079
|
+
}
|
|
34080
|
+
if (updates.enabled !== undefined) {
|
|
34081
|
+
setValues.enabled = updates.enabled;
|
|
34082
|
+
}
|
|
34083
|
+
if (updates.allowAuthRequests !== undefined) {
|
|
34084
|
+
setValues.allowAuthRequests = updates.allowAuthRequests;
|
|
34085
|
+
}
|
|
34086
|
+
if (updates.weixinUin !== undefined) {
|
|
34087
|
+
setValues.weixinUin = updates.weixinUin;
|
|
34088
|
+
}
|
|
34089
|
+
if (updates.botTokenEnc !== undefined) {
|
|
34090
|
+
setValues.botTokenEnc = updates.botTokenEnc;
|
|
34091
|
+
}
|
|
34092
|
+
if (updates.baseUrl !== undefined) {
|
|
34093
|
+
setValues.baseUrl = updates.baseUrl;
|
|
34094
|
+
}
|
|
34095
|
+
if (updates.syncBuf !== undefined) {
|
|
34096
|
+
setValues.syncBuf = updates.syncBuf;
|
|
34097
|
+
}
|
|
34098
|
+
orm.update(weixinAccounts).set(setValues).where(eq(weixinAccounts.id, accountId)).run();
|
|
34099
|
+
return getWeixinAccountById(accountId);
|
|
34100
|
+
}
|
|
34101
|
+
function deleteWeixinAccount(accountId) {
|
|
34102
|
+
const orm = getDb();
|
|
34103
|
+
orm.delete(weixinAccounts).where(eq(weixinAccounts.id, accountId)).run();
|
|
34104
|
+
}
|
|
34105
|
+
function getWeixinUserCount(accountId) {
|
|
34106
|
+
const orm = getDb();
|
|
34107
|
+
const row = orm.select({ total: count() }).from(weixinAccountUsers).where(eq(weixinAccountUsers.accountId, accountId)).get();
|
|
34108
|
+
return Number(row?.total ?? 0);
|
|
34109
|
+
}
|
|
34110
|
+
function getWeixinUserByAccountAndUserId(accountId, userId) {
|
|
34111
|
+
const orm = getDb();
|
|
34112
|
+
const row = orm.select().from(weixinAccountUsers).where(and(eq(weixinAccountUsers.accountId, accountId), eq(weixinAccountUsers.userId, userId))).get();
|
|
34113
|
+
if (!row) {
|
|
34114
|
+
return null;
|
|
34115
|
+
}
|
|
34116
|
+
return toWeixinAccountUser(row);
|
|
34117
|
+
}
|
|
34118
|
+
function upsertWeixinUserOnInbound(params) {
|
|
34119
|
+
const existing = getWeixinUserByAccountAndUserId(params.accountId, params.userId);
|
|
34120
|
+
const orm = getDb();
|
|
34121
|
+
if (existing) {
|
|
34122
|
+
const setValues = {
|
|
34123
|
+
displayName: params.displayName,
|
|
34124
|
+
lastInboundAt: params.at,
|
|
34125
|
+
needsReactivation: false,
|
|
34126
|
+
updatedAt: params.at
|
|
34127
|
+
};
|
|
34128
|
+
if (params.contextToken != null) {
|
|
34129
|
+
setValues.lastContextToken = params.contextToken;
|
|
34130
|
+
}
|
|
34131
|
+
orm.update(weixinAccountUsers).set(setValues).where(eq(weixinAccountUsers.id, existing.id)).run();
|
|
34132
|
+
return getWeixinUserByAccountAndUserId(params.accountId, params.userId);
|
|
34133
|
+
}
|
|
34134
|
+
if (!params.allowAuthRequests) {
|
|
34135
|
+
return null;
|
|
34136
|
+
}
|
|
34137
|
+
if (getWeixinUserCount(params.accountId) >= WEIXIN_USER_CAP) {
|
|
34138
|
+
throw new Error(instance.t("apiError.invalidRequest"));
|
|
34139
|
+
}
|
|
34140
|
+
orm.insert(weixinAccountUsers).values({
|
|
34141
|
+
id: crypto.randomUUID(),
|
|
34142
|
+
accountId: params.accountId,
|
|
34143
|
+
userId: params.userId,
|
|
34144
|
+
displayName: params.displayName,
|
|
34145
|
+
status: "pending",
|
|
34146
|
+
lastContextToken: params.contextToken,
|
|
34147
|
+
lastInboundAt: params.at,
|
|
34148
|
+
needsReactivation: false,
|
|
34149
|
+
appliedAt: params.at,
|
|
34150
|
+
authorizedAt: null,
|
|
34151
|
+
updatedAt: params.at
|
|
34152
|
+
}).run();
|
|
34153
|
+
return getWeixinUserByAccountAndUserId(params.accountId, params.userId);
|
|
34154
|
+
}
|
|
34155
|
+
function listWeixinUsersByAccount(accountId) {
|
|
34156
|
+
const orm = getDb();
|
|
34157
|
+
return orm.select().from(weixinAccountUsers).where(eq(weixinAccountUsers.accountId, accountId)).orderBy(desc(weixinAccountUsers.appliedAt)).all().map(toWeixinAccountUser);
|
|
34158
|
+
}
|
|
34159
|
+
function listAuthorizedWeixinUsersByAccount(accountId) {
|
|
34160
|
+
const orm = getDb();
|
|
34161
|
+
return orm.select().from(weixinAccountUsers).where(and(eq(weixinAccountUsers.accountId, accountId), eq(weixinAccountUsers.status, "authorized"))).orderBy(desc(weixinAccountUsers.authorizedAt)).all().map(toWeixinAccountUser);
|
|
34162
|
+
}
|
|
34163
|
+
function getWeixinUserContextTokens(accountId) {
|
|
34164
|
+
const orm = getDb();
|
|
34165
|
+
return orm.select({
|
|
34166
|
+
userId: weixinAccountUsers.userId,
|
|
34167
|
+
contextToken: weixinAccountUsers.lastContextToken
|
|
34168
|
+
}).from(weixinAccountUsers).where(eq(weixinAccountUsers.accountId, accountId)).all().filter((row) => row.contextToken != null);
|
|
34169
|
+
}
|
|
34170
|
+
function approveWeixinUser(accountId, userId) {
|
|
34171
|
+
const existing = getWeixinUserByAccountAndUserId(accountId, userId);
|
|
34172
|
+
if (!existing) {
|
|
34173
|
+
return null;
|
|
34174
|
+
}
|
|
34175
|
+
const now = new Date().toISOString();
|
|
34176
|
+
const orm = getDb();
|
|
34177
|
+
orm.update(weixinAccountUsers).set({
|
|
34178
|
+
status: "authorized",
|
|
34179
|
+
authorizedAt: now,
|
|
34180
|
+
updatedAt: now
|
|
34181
|
+
}).where(eq(weixinAccountUsers.id, existing.id)).run();
|
|
34182
|
+
return getWeixinUserByAccountAndUserId(accountId, userId);
|
|
34183
|
+
}
|
|
34184
|
+
function deleteWeixinUser(accountId, userId) {
|
|
34185
|
+
const orm = getDb();
|
|
34186
|
+
orm.delete(weixinAccountUsers).where(and(eq(weixinAccountUsers.accountId, accountId), eq(weixinAccountUsers.userId, userId))).run();
|
|
34187
|
+
}
|
|
34188
|
+
function setWeixinUserNeedsReactivation(accountId, userId, value) {
|
|
34189
|
+
const orm = getDb();
|
|
34190
|
+
orm.update(weixinAccountUsers).set({ needsReactivation: value, updatedAt: new Date().toISOString() }).where(and(eq(weixinAccountUsers.accountId, accountId), eq(weixinAccountUsers.userId, userId))).run();
|
|
34191
|
+
}
|
|
33653
34192
|
|
|
33654
34193
|
// ../../apps/gateway/src/db/agent.ts
|
|
33655
34194
|
function ensureAgentSettingsInitialized() {
|
|
@@ -87048,13 +87587,7 @@ var _a21;
|
|
|
87048
87587
|
_a21 = symbol21;
|
|
87049
87588
|
var defaultDownload2 = createDownload();
|
|
87050
87589
|
|
|
87051
|
-
// ../../apps/gateway/src/events/
|
|
87052
|
-
function escapeTelegramHtmlText(input) {
|
|
87053
|
-
return input.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
87054
|
-
}
|
|
87055
|
-
function escapeTelegramHtmlAttribute(input) {
|
|
87056
|
-
return escapeTelegramHtmlText(input).replace(/"/g, """);
|
|
87057
|
-
}
|
|
87590
|
+
// ../../apps/gateway/src/events/channels/pane-url.ts
|
|
87058
87591
|
function trimTrailingSlash(url2) {
|
|
87059
87592
|
return url2.endsWith("/") ? url2.slice(0, -1) : url2;
|
|
87060
87593
|
}
|
|
@@ -87083,100 +87616,17 @@ function normalizeHttpUrl(input) {
|
|
|
87083
87616
|
}
|
|
87084
87617
|
}
|
|
87085
87618
|
|
|
87086
|
-
|
|
87087
|
-
|
|
87088
|
-
|
|
87089
|
-
|
|
87090
|
-
|
|
87091
|
-
|
|
87092
|
-
|
|
87093
|
-
|
|
87094
|
-
|
|
87095
|
-
|
|
87096
|
-
this.webhooks = getAllWebhookEndpoints().filter((w) => w.enabled);
|
|
87097
|
-
this.lastRefresh = now2;
|
|
87098
|
-
console.log(`[events] refreshed config: ${this.webhooks.length} webhooks`);
|
|
87099
|
-
}
|
|
87619
|
+
// ../../apps/gateway/src/events/channels/telegram.ts
|
|
87620
|
+
function escapeTelegramHtmlText(input) {
|
|
87621
|
+
return input.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
87622
|
+
}
|
|
87623
|
+
function escapeTelegramHtmlAttribute(input) {
|
|
87624
|
+
return escapeTelegramHtmlText(input).replace(/"/g, """);
|
|
87625
|
+
}
|
|
87626
|
+
|
|
87627
|
+
class TelegramChannel {
|
|
87628
|
+
id = "telegram";
|
|
87100
87629
|
async notify(eventType, event) {
|
|
87101
|
-
this.refreshConfig();
|
|
87102
|
-
const fullEvent = {
|
|
87103
|
-
...event,
|
|
87104
|
-
eventType,
|
|
87105
|
-
timestamp: new Date().toISOString()
|
|
87106
|
-
};
|
|
87107
|
-
if (eventType === "terminal_bell") {
|
|
87108
|
-
if (!this.shouldPassBellThrottle(fullEvent)) {
|
|
87109
|
-
return;
|
|
87110
|
-
}
|
|
87111
|
-
} else if (eventType === "terminal_notification") {
|
|
87112
|
-
if (!this.shouldPassNotificationThrottle(fullEvent)) {
|
|
87113
|
-
return;
|
|
87114
|
-
}
|
|
87115
|
-
}
|
|
87116
|
-
await Promise.all([
|
|
87117
|
-
this.sendWebhooks(eventType, fullEvent),
|
|
87118
|
-
this.sendTelegramNotifications(eventType, fullEvent)
|
|
87119
|
-
]);
|
|
87120
|
-
}
|
|
87121
|
-
shouldPassBellThrottle(event) {
|
|
87122
|
-
const settings = getSiteSettings();
|
|
87123
|
-
const throttleMs = Math.max(0, settings.bellThrottleSeconds) * 1000;
|
|
87124
|
-
if (throttleMs === 0) {
|
|
87125
|
-
return true;
|
|
87126
|
-
}
|
|
87127
|
-
const key = `${event.device.id}:${event.tmux?.paneId ?? "-"}:${event.eventType}`;
|
|
87128
|
-
const now2 = Date.now();
|
|
87129
|
-
const previous = this.bellThrottleMap.get(key) ?? 0;
|
|
87130
|
-
if (now2 - previous < throttleMs) {
|
|
87131
|
-
return false;
|
|
87132
|
-
}
|
|
87133
|
-
this.bellThrottleMap.set(key, now2);
|
|
87134
|
-
return true;
|
|
87135
|
-
}
|
|
87136
|
-
shouldPassNotificationThrottle(event) {
|
|
87137
|
-
const settings = getSiteSettings();
|
|
87138
|
-
const throttleMs = Math.max(0, settings.notificationThrottleSeconds) * 1000;
|
|
87139
|
-
if (throttleMs === 0) {
|
|
87140
|
-
return true;
|
|
87141
|
-
}
|
|
87142
|
-
const source = typeof event.payload?.source === "string" ? event.payload.source : "unknown";
|
|
87143
|
-
const key = `${event.device.id}:${event.tmux?.paneId ?? "-"}:notification:${source}`;
|
|
87144
|
-
const now2 = Date.now();
|
|
87145
|
-
const previous = this.notificationThrottleMap.get(key) ?? 0;
|
|
87146
|
-
if (now2 - previous < throttleMs) {
|
|
87147
|
-
return false;
|
|
87148
|
-
}
|
|
87149
|
-
this.notificationThrottleMap.set(key, now2);
|
|
87150
|
-
return true;
|
|
87151
|
-
}
|
|
87152
|
-
async sendWebhooks(eventType, event) {
|
|
87153
|
-
const targets = this.webhooks.filter((w) => w.eventMask.includes(eventType));
|
|
87154
|
-
await Promise.all(targets.map(async (webhook) => {
|
|
87155
|
-
try {
|
|
87156
|
-
await this.sendWebhook(webhook, event);
|
|
87157
|
-
} catch (err) {
|
|
87158
|
-
console.error(`[webhook] failed to send to ${webhook.url}:`, err);
|
|
87159
|
-
}
|
|
87160
|
-
}));
|
|
87161
|
-
}
|
|
87162
|
-
async sendWebhook(webhook, event) {
|
|
87163
|
-
const body = JSON.stringify(event);
|
|
87164
|
-
const signature = await this.generateHmac(webhook.secret, body);
|
|
87165
|
-
const response = await fetch(webhook.url, {
|
|
87166
|
-
method: "POST",
|
|
87167
|
-
headers: {
|
|
87168
|
-
"Content-Type": "application/json",
|
|
87169
|
-
"X-Tmex-Signature": `sha256=${signature}`,
|
|
87170
|
-
"X-Tmex-Event": event.eventType,
|
|
87171
|
-
"X-Tmex-Timestamp": event.timestamp
|
|
87172
|
-
},
|
|
87173
|
-
body
|
|
87174
|
-
});
|
|
87175
|
-
if (!response.ok) {
|
|
87176
|
-
console.error(`[webhook] ${webhook.url} returned ${response.status}`);
|
|
87177
|
-
}
|
|
87178
|
-
}
|
|
87179
|
-
async sendTelegramNotifications(eventType, event) {
|
|
87180
87630
|
const settings = getSiteSettings();
|
|
87181
87631
|
if (eventType === "terminal_bell") {
|
|
87182
87632
|
if (!settings.enableTelegramBellPush) {
|
|
@@ -87290,6 +87740,51 @@ class EventNotifier {
|
|
|
87290
87740
|
return lines.join(`
|
|
87291
87741
|
`);
|
|
87292
87742
|
}
|
|
87743
|
+
}
|
|
87744
|
+
var telegramChannel = new TelegramChannel;
|
|
87745
|
+
|
|
87746
|
+
// ../../apps/gateway/src/events/channels/webhook.ts
|
|
87747
|
+
class WebhookChannel {
|
|
87748
|
+
id = "webhook";
|
|
87749
|
+
webhooks = [];
|
|
87750
|
+
lastRefresh = 0;
|
|
87751
|
+
REFRESH_INTERVAL = 60000;
|
|
87752
|
+
refreshConfig() {
|
|
87753
|
+
const now2 = Date.now();
|
|
87754
|
+
if (now2 - this.lastRefresh < this.REFRESH_INTERVAL)
|
|
87755
|
+
return;
|
|
87756
|
+
this.webhooks = getAllWebhookEndpoints().filter((w) => w.enabled);
|
|
87757
|
+
this.lastRefresh = now2;
|
|
87758
|
+
console.log(`[events] refreshed config: ${this.webhooks.length} webhooks`);
|
|
87759
|
+
}
|
|
87760
|
+
async notify(eventType, event) {
|
|
87761
|
+
this.refreshConfig();
|
|
87762
|
+
const targets = this.webhooks.filter((w) => w.eventMask.includes(eventType));
|
|
87763
|
+
await Promise.all(targets.map(async (webhook) => {
|
|
87764
|
+
try {
|
|
87765
|
+
await this.sendWebhook(webhook, event);
|
|
87766
|
+
} catch (err) {
|
|
87767
|
+
console.error(`[webhook] failed to send to ${webhook.url}:`, err);
|
|
87768
|
+
}
|
|
87769
|
+
}));
|
|
87770
|
+
}
|
|
87771
|
+
async sendWebhook(webhook, event) {
|
|
87772
|
+
const body = JSON.stringify(event);
|
|
87773
|
+
const signature = await this.generateHmac(webhook.secret, body);
|
|
87774
|
+
const response = await fetch(webhook.url, {
|
|
87775
|
+
method: "POST",
|
|
87776
|
+
headers: {
|
|
87777
|
+
"Content-Type": "application/json",
|
|
87778
|
+
"X-Tmex-Signature": `sha256=${signature}`,
|
|
87779
|
+
"X-Tmex-Event": event.eventType,
|
|
87780
|
+
"X-Tmex-Timestamp": event.timestamp
|
|
87781
|
+
},
|
|
87782
|
+
body
|
|
87783
|
+
});
|
|
87784
|
+
if (!response.ok) {
|
|
87785
|
+
console.error(`[webhook] ${webhook.url} returned ${response.status}`);
|
|
87786
|
+
}
|
|
87787
|
+
}
|
|
87293
87788
|
async generateHmac(secret, message) {
|
|
87294
87789
|
const encoder2 = new TextEncoder;
|
|
87295
87790
|
const key = await crypto.subtle.importKey("raw", encoder2.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
@@ -87297,6 +87792,897 @@ class EventNotifier {
|
|
|
87297
87792
|
return Buffer.from(signature).toString("hex");
|
|
87298
87793
|
}
|
|
87299
87794
|
}
|
|
87795
|
+
var webhookChannel = new WebhookChannel;
|
|
87796
|
+
|
|
87797
|
+
// ../../apps/gateway/src/weixin/ilink/types.ts
|
|
87798
|
+
var ILINK_LOGIN_HOST = "https://ilinkai.weixin.qq.com";
|
|
87799
|
+
var ILINK_BOT_TYPE = 3;
|
|
87800
|
+
var CHANNEL_VERSION = "1.0.3";
|
|
87801
|
+
var CLIENT_ID_PREFIX = "openclaw-weixin-";
|
|
87802
|
+
var MESSAGE_TYPE_BOT = 2;
|
|
87803
|
+
var MESSAGE_STATE_FINISH = 2;
|
|
87804
|
+
var ITEM_TYPE_TEXT = 1;
|
|
87805
|
+
var SESSION_EXPIRED_ERRCODE = -14;
|
|
87806
|
+
|
|
87807
|
+
// ../../apps/gateway/src/weixin/ilink/api.ts
|
|
87808
|
+
function resolveFetch(fetchImpl) {
|
|
87809
|
+
return fetchImpl ?? globalThis.fetch;
|
|
87810
|
+
}
|
|
87811
|
+
function trimTrailingSlash2(url2) {
|
|
87812
|
+
return url2.endsWith("/") ? url2.slice(0, -1) : url2;
|
|
87813
|
+
}
|
|
87814
|
+
function generateWechatUin() {
|
|
87815
|
+
const buf = new Uint32Array(1);
|
|
87816
|
+
crypto.getRandomValues(buf);
|
|
87817
|
+
const decimal = String(buf[0]);
|
|
87818
|
+
return Buffer.from(decimal, "utf8").toString("base64");
|
|
87819
|
+
}
|
|
87820
|
+
function buildAuthHeaders(botToken) {
|
|
87821
|
+
const headers = {
|
|
87822
|
+
"Content-Type": "application/json",
|
|
87823
|
+
AuthorizationType: "ilink_bot_token",
|
|
87824
|
+
"X-WECHAT-UIN": generateWechatUin()
|
|
87825
|
+
};
|
|
87826
|
+
const token = botToken?.trim();
|
|
87827
|
+
if (token) {
|
|
87828
|
+
headers.Authorization = `Bearer ${token}`;
|
|
87829
|
+
}
|
|
87830
|
+
return headers;
|
|
87831
|
+
}
|
|
87832
|
+
function buildBaseInfo() {
|
|
87833
|
+
return { channel_version: CHANNEL_VERSION };
|
|
87834
|
+
}
|
|
87835
|
+
async function readJson(resp) {
|
|
87836
|
+
if (!resp.ok) {
|
|
87837
|
+
const body = await resp.text().catch(() => "");
|
|
87838
|
+
throw new Error(`iLink HTTP ${resp.status} ${resp.statusText}${body ? `: ${body.slice(0, 200)}` : ""}`);
|
|
87839
|
+
}
|
|
87840
|
+
const text3 = await resp.text();
|
|
87841
|
+
if (!text3) {
|
|
87842
|
+
return {};
|
|
87843
|
+
}
|
|
87844
|
+
return JSON.parse(text3);
|
|
87845
|
+
}
|
|
87846
|
+
async function getBotQrcode(opts = {}) {
|
|
87847
|
+
const host = trimTrailingSlash2(opts.host ?? ILINK_LOGIN_HOST);
|
|
87848
|
+
const botType = opts.botType ?? ILINK_BOT_TYPE;
|
|
87849
|
+
const url2 = `${host}/ilink/bot/get_bot_qrcode?bot_type=${botType}`;
|
|
87850
|
+
const resp = await resolveFetch(opts.fetchImpl)(url2, {
|
|
87851
|
+
method: "GET",
|
|
87852
|
+
headers: buildAuthHeaders(),
|
|
87853
|
+
signal: opts.signal
|
|
87854
|
+
});
|
|
87855
|
+
return readJson(resp);
|
|
87856
|
+
}
|
|
87857
|
+
async function getQrcodeStatus(qrcode, opts = {}) {
|
|
87858
|
+
const host = trimTrailingSlash2(opts.host ?? ILINK_LOGIN_HOST);
|
|
87859
|
+
const url2 = `${host}/ilink/bot/get_qrcode_status?qrcode=${encodeURIComponent(qrcode)}`;
|
|
87860
|
+
const resp = await resolveFetch(opts.fetchImpl)(url2, {
|
|
87861
|
+
method: "GET",
|
|
87862
|
+
headers: buildAuthHeaders(),
|
|
87863
|
+
signal: opts.signal
|
|
87864
|
+
});
|
|
87865
|
+
return readJson(resp);
|
|
87866
|
+
}
|
|
87867
|
+
async function getUpdates(opts) {
|
|
87868
|
+
const baseUrl = trimTrailingSlash2(opts.baseUrl);
|
|
87869
|
+
const url2 = `${baseUrl}/ilink/bot/getupdates`;
|
|
87870
|
+
const body = {
|
|
87871
|
+
get_updates_buf: opts.getUpdatesBuf ?? "",
|
|
87872
|
+
base_info: buildBaseInfo()
|
|
87873
|
+
};
|
|
87874
|
+
const resp = await resolveFetch(opts.fetchImpl)(url2, {
|
|
87875
|
+
method: "POST",
|
|
87876
|
+
headers: buildAuthHeaders(opts.botToken),
|
|
87877
|
+
body: JSON.stringify(body),
|
|
87878
|
+
signal: opts.signal
|
|
87879
|
+
});
|
|
87880
|
+
return readJson(resp);
|
|
87881
|
+
}
|
|
87882
|
+
async function sendMessage(opts) {
|
|
87883
|
+
const baseUrl = trimTrailingSlash2(opts.baseUrl);
|
|
87884
|
+
const url2 = `${baseUrl}/ilink/bot/sendmessage`;
|
|
87885
|
+
const itemList = opts.items.map((item) => ({
|
|
87886
|
+
type: ITEM_TYPE_TEXT,
|
|
87887
|
+
text_item: { text: item.text }
|
|
87888
|
+
}));
|
|
87889
|
+
const req = {
|
|
87890
|
+
msg: {
|
|
87891
|
+
to_user_id: opts.toUserId,
|
|
87892
|
+
message_type: MESSAGE_TYPE_BOT,
|
|
87893
|
+
message_state: MESSAGE_STATE_FINISH,
|
|
87894
|
+
context_token: opts.contextToken,
|
|
87895
|
+
client_id: opts.clientId,
|
|
87896
|
+
item_list: itemList
|
|
87897
|
+
},
|
|
87898
|
+
base_info: buildBaseInfo()
|
|
87899
|
+
};
|
|
87900
|
+
const resp = await resolveFetch(opts.fetchImpl)(url2, {
|
|
87901
|
+
method: "POST",
|
|
87902
|
+
headers: buildAuthHeaders(opts.botToken),
|
|
87903
|
+
body: JSON.stringify(req),
|
|
87904
|
+
signal: opts.signal
|
|
87905
|
+
});
|
|
87906
|
+
return readJson(resp);
|
|
87907
|
+
}
|
|
87908
|
+
|
|
87909
|
+
// ../../apps/gateway/src/weixin/ilink/client.ts
|
|
87910
|
+
class WeixinNoContextTokenError extends Error {
|
|
87911
|
+
constructor(toUserId) {
|
|
87912
|
+
super(`No context_token for user ${toUserId}. Receive a message from them first.`);
|
|
87913
|
+
this.name = "WeixinNoContextTokenError";
|
|
87914
|
+
}
|
|
87915
|
+
}
|
|
87916
|
+
|
|
87917
|
+
class WeixinSessionExpiredError extends Error {
|
|
87918
|
+
constructor() {
|
|
87919
|
+
super("iLink bot session expired; re-login required.");
|
|
87920
|
+
this.name = "WeixinSessionExpiredError";
|
|
87921
|
+
}
|
|
87922
|
+
}
|
|
87923
|
+
var DEFAULT_LOGIN_TIMEOUT_MS = 480000;
|
|
87924
|
+
var DEFAULT_QRCODE_POLL_INTERVAL_MS = 1000;
|
|
87925
|
+
var MAX_QRCODE_REFRESH = 3;
|
|
87926
|
+
var RETRY_DELAY_MS = 2000;
|
|
87927
|
+
var BACKOFF_DELAY_MS = 30000;
|
|
87928
|
+
var DEFAULT_LONGPOLL_TIMEOUT_MS = 60000;
|
|
87929
|
+
var LONGPOLL_TIMEOUT_MARGIN_MS = 1e4;
|
|
87930
|
+
function generateClientId() {
|
|
87931
|
+
const bytes = new Uint8Array(16);
|
|
87932
|
+
crypto.getRandomValues(bytes);
|
|
87933
|
+
const hex3 = Array.from(bytes, (b3) => b3.toString(16).padStart(2, "0")).join("");
|
|
87934
|
+
return `${CLIENT_ID_PREFIX}${hex3}`;
|
|
87935
|
+
}
|
|
87936
|
+
|
|
87937
|
+
class AbortError extends Error {
|
|
87938
|
+
constructor() {
|
|
87939
|
+
super("aborted");
|
|
87940
|
+
this.name = "AbortError";
|
|
87941
|
+
}
|
|
87942
|
+
}
|
|
87943
|
+
function sleep3(ms, signal) {
|
|
87944
|
+
return new Promise((resolve3, reject) => {
|
|
87945
|
+
if (signal?.aborted) {
|
|
87946
|
+
reject(new AbortError);
|
|
87947
|
+
return;
|
|
87948
|
+
}
|
|
87949
|
+
const timer = setTimeout(() => {
|
|
87950
|
+
signal?.removeEventListener("abort", onAbort);
|
|
87951
|
+
resolve3();
|
|
87952
|
+
}, ms);
|
|
87953
|
+
const onAbort = () => {
|
|
87954
|
+
clearTimeout(timer);
|
|
87955
|
+
reject(new AbortError);
|
|
87956
|
+
};
|
|
87957
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
87958
|
+
});
|
|
87959
|
+
}
|
|
87960
|
+
function isAbort(err, signal) {
|
|
87961
|
+
if (signal?.aborted)
|
|
87962
|
+
return true;
|
|
87963
|
+
if (err instanceof AbortError)
|
|
87964
|
+
return true;
|
|
87965
|
+
if (err instanceof DOMException && err.name === "AbortError")
|
|
87966
|
+
return true;
|
|
87967
|
+
if (err instanceof Error && err.name === "AbortError")
|
|
87968
|
+
return true;
|
|
87969
|
+
return false;
|
|
87970
|
+
}
|
|
87971
|
+
function isSessionExpired(resp) {
|
|
87972
|
+
return resp.ret === SESSION_EXPIRED_ERRCODE || resp.errcode === SESSION_EXPIRED_ERRCODE;
|
|
87973
|
+
}
|
|
87974
|
+
|
|
87975
|
+
class WeixinClient {
|
|
87976
|
+
creds = null;
|
|
87977
|
+
fetchImpl;
|
|
87978
|
+
contextTokens = new Map;
|
|
87979
|
+
running = false;
|
|
87980
|
+
internalAbort = null;
|
|
87981
|
+
constructor(opts = {}) {
|
|
87982
|
+
this.fetchImpl = opts.fetchImpl;
|
|
87983
|
+
if (opts.accountId && opts.botToken && opts.baseUrl) {
|
|
87984
|
+
this.creds = {
|
|
87985
|
+
accountId: opts.accountId,
|
|
87986
|
+
botToken: opts.botToken,
|
|
87987
|
+
baseUrl: opts.baseUrl
|
|
87988
|
+
};
|
|
87989
|
+
}
|
|
87990
|
+
}
|
|
87991
|
+
get credentials() {
|
|
87992
|
+
return this.creds;
|
|
87993
|
+
}
|
|
87994
|
+
getContextToken(userId) {
|
|
87995
|
+
return this.contextTokens.get(userId);
|
|
87996
|
+
}
|
|
87997
|
+
setContextToken(userId, token) {
|
|
87998
|
+
this.contextTokens.set(userId, token);
|
|
87999
|
+
}
|
|
88000
|
+
isRunning() {
|
|
88001
|
+
return this.running;
|
|
88002
|
+
}
|
|
88003
|
+
static extractText(raw) {
|
|
88004
|
+
const msg = raw;
|
|
88005
|
+
if (!msg || typeof msg !== "object" || !Array.isArray(msg.item_list)) {
|
|
88006
|
+
return "";
|
|
88007
|
+
}
|
|
88008
|
+
const parts = [];
|
|
88009
|
+
for (const item of msg.item_list) {
|
|
88010
|
+
if (item?.type === ITEM_TYPE_TEXT && typeof item.text_item?.text === "string") {
|
|
88011
|
+
parts.push(item.text_item.text);
|
|
88012
|
+
}
|
|
88013
|
+
}
|
|
88014
|
+
return parts.join("");
|
|
88015
|
+
}
|
|
88016
|
+
async login(opts) {
|
|
88017
|
+
const { onQrcode, signal } = opts;
|
|
88018
|
+
const timeoutMs = Math.max(1000, opts.timeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS);
|
|
88019
|
+
const pollIntervalMs = opts.pollIntervalMs ?? DEFAULT_QRCODE_POLL_INTERVAL_MS;
|
|
88020
|
+
const deadline = Date.now() + timeoutMs;
|
|
88021
|
+
let qrcodeId = await this.fetchQrcode(onQrcode, signal);
|
|
88022
|
+
let refreshes = 0;
|
|
88023
|
+
while (Date.now() < deadline) {
|
|
88024
|
+
if (signal?.aborted)
|
|
88025
|
+
throw new AbortError;
|
|
88026
|
+
await sleep3(pollIntervalMs, signal);
|
|
88027
|
+
let status;
|
|
88028
|
+
try {
|
|
88029
|
+
status = await getQrcodeStatus(qrcodeId, {
|
|
88030
|
+
fetchImpl: this.fetchImpl,
|
|
88031
|
+
signal
|
|
88032
|
+
});
|
|
88033
|
+
} catch (err) {
|
|
88034
|
+
if (isAbort(err, signal))
|
|
88035
|
+
throw err;
|
|
88036
|
+
continue;
|
|
88037
|
+
}
|
|
88038
|
+
switch (status.status) {
|
|
88039
|
+
case "confirmed": {
|
|
88040
|
+
if (!status.bot_token || !status.baseurl) {
|
|
88041
|
+
throw new Error("iLink login confirmed but bot_token/baseurl missing.");
|
|
88042
|
+
}
|
|
88043
|
+
const accountId = status.ilink_bot_id ?? status.ilink_user_id ?? status.bot_token;
|
|
88044
|
+
this.creds = {
|
|
88045
|
+
accountId,
|
|
88046
|
+
botToken: status.bot_token,
|
|
88047
|
+
baseUrl: status.baseurl
|
|
88048
|
+
};
|
|
88049
|
+
return this.creds;
|
|
88050
|
+
}
|
|
88051
|
+
case "expired": {
|
|
88052
|
+
if (refreshes >= MAX_QRCODE_REFRESH) {
|
|
88053
|
+
throw new Error("iLink qrcode expired and refresh limit reached.");
|
|
88054
|
+
}
|
|
88055
|
+
refreshes += 1;
|
|
88056
|
+
qrcodeId = await this.fetchQrcode(onQrcode, signal);
|
|
88057
|
+
break;
|
|
88058
|
+
}
|
|
88059
|
+
default:
|
|
88060
|
+
break;
|
|
88061
|
+
}
|
|
88062
|
+
}
|
|
88063
|
+
throw new Error("iLink login timed out.");
|
|
88064
|
+
}
|
|
88065
|
+
async fetchQrcode(onQrcode, signal) {
|
|
88066
|
+
const resp = await getBotQrcode({ fetchImpl: this.fetchImpl, signal });
|
|
88067
|
+
if (!resp.qrcode) {
|
|
88068
|
+
throw new Error("iLink get_bot_qrcode returned no qrcode.");
|
|
88069
|
+
}
|
|
88070
|
+
if (!resp.qrcode_img_content) {
|
|
88071
|
+
throw new Error("iLink get_bot_qrcode returned no qrcode content.");
|
|
88072
|
+
}
|
|
88073
|
+
onQrcode({
|
|
88074
|
+
url: resp.qrcode_img_content,
|
|
88075
|
+
qrcodeId: resp.qrcode
|
|
88076
|
+
});
|
|
88077
|
+
return resp.qrcode;
|
|
88078
|
+
}
|
|
88079
|
+
async start(opts = {}) {
|
|
88080
|
+
if (!this.creds) {
|
|
88081
|
+
throw new Error("WeixinClient.start called without credentials; login first.");
|
|
88082
|
+
}
|
|
88083
|
+
if (this.running) {
|
|
88084
|
+
throw new Error("WeixinClient already running.");
|
|
88085
|
+
}
|
|
88086
|
+
if (opts.initialContextTokens) {
|
|
88087
|
+
for (const [userId, token] of Object.entries(opts.initialContextTokens)) {
|
|
88088
|
+
this.contextTokens.set(userId, token);
|
|
88089
|
+
}
|
|
88090
|
+
}
|
|
88091
|
+
this.internalAbort = new AbortController;
|
|
88092
|
+
const signal = this.linkSignals(opts.signal, this.internalAbort.signal);
|
|
88093
|
+
this.running = true;
|
|
88094
|
+
let getUpdatesBuf = await opts.loadSyncBuf?.() ?? "";
|
|
88095
|
+
let failures = 0;
|
|
88096
|
+
let longpollTimeoutMs = opts.longpollTimeoutMs ?? DEFAULT_LONGPOLL_TIMEOUT_MS;
|
|
88097
|
+
try {
|
|
88098
|
+
while (!signal.aborted) {
|
|
88099
|
+
let resp;
|
|
88100
|
+
try {
|
|
88101
|
+
const perRequestSignal = AbortSignal.any([
|
|
88102
|
+
signal,
|
|
88103
|
+
AbortSignal.timeout(longpollTimeoutMs)
|
|
88104
|
+
]);
|
|
88105
|
+
resp = await getUpdates({
|
|
88106
|
+
baseUrl: this.creds.baseUrl,
|
|
88107
|
+
botToken: this.creds.botToken,
|
|
88108
|
+
getUpdatesBuf,
|
|
88109
|
+
fetchImpl: this.fetchImpl,
|
|
88110
|
+
signal: perRequestSignal
|
|
88111
|
+
});
|
|
88112
|
+
} catch (err) {
|
|
88113
|
+
if (signal.aborted)
|
|
88114
|
+
break;
|
|
88115
|
+
opts.onError?.(err);
|
|
88116
|
+
failures += 1;
|
|
88117
|
+
await this.backoffSleep(failures, signal);
|
|
88118
|
+
continue;
|
|
88119
|
+
}
|
|
88120
|
+
if (isSessionExpired(resp)) {
|
|
88121
|
+
opts.onSessionExpired?.();
|
|
88122
|
+
throw new WeixinSessionExpiredError;
|
|
88123
|
+
}
|
|
88124
|
+
if (typeof resp.ret === "number" && resp.ret !== 0) {
|
|
88125
|
+
opts.onError?.(new Error(`getupdates ret=${resp.ret} errmsg=${resp.errmsg ?? ""}`));
|
|
88126
|
+
failures += 1;
|
|
88127
|
+
await this.backoffSleep(failures, signal);
|
|
88128
|
+
continue;
|
|
88129
|
+
}
|
|
88130
|
+
failures = 0;
|
|
88131
|
+
if (typeof resp.longpolling_timeout_ms === "number" && resp.longpolling_timeout_ms > 0) {
|
|
88132
|
+
longpollTimeoutMs = resp.longpolling_timeout_ms + LONGPOLL_TIMEOUT_MARGIN_MS;
|
|
88133
|
+
}
|
|
88134
|
+
const msgs = resp.msgs ?? [];
|
|
88135
|
+
for (const msg of msgs) {
|
|
88136
|
+
if (signal.aborted)
|
|
88137
|
+
break;
|
|
88138
|
+
const inbound = this.toInbound(msg);
|
|
88139
|
+
if (msg.from_user_id && msg.context_token) {
|
|
88140
|
+
this.contextTokens.set(msg.from_user_id, msg.context_token);
|
|
88141
|
+
}
|
|
88142
|
+
try {
|
|
88143
|
+
await opts.onMessage?.(inbound);
|
|
88144
|
+
} catch (err) {
|
|
88145
|
+
opts.onError?.(err);
|
|
88146
|
+
}
|
|
88147
|
+
}
|
|
88148
|
+
if (resp.get_updates_buf != null && resp.get_updates_buf !== "") {
|
|
88149
|
+
getUpdatesBuf = resp.get_updates_buf;
|
|
88150
|
+
await opts.saveSyncBuf?.(getUpdatesBuf);
|
|
88151
|
+
}
|
|
88152
|
+
}
|
|
88153
|
+
} finally {
|
|
88154
|
+
this.running = false;
|
|
88155
|
+
this.internalAbort = null;
|
|
88156
|
+
}
|
|
88157
|
+
}
|
|
88158
|
+
stop() {
|
|
88159
|
+
this.internalAbort?.abort();
|
|
88160
|
+
}
|
|
88161
|
+
async sendText(toUserId, text3, contextToken) {
|
|
88162
|
+
if (!this.creds) {
|
|
88163
|
+
throw new Error("WeixinClient.sendText called without credentials; login first.");
|
|
88164
|
+
}
|
|
88165
|
+
const token = contextToken ?? this.contextTokens.get(toUserId);
|
|
88166
|
+
if (!token) {
|
|
88167
|
+
throw new WeixinNoContextTokenError(toUserId);
|
|
88168
|
+
}
|
|
88169
|
+
const resp = await sendMessage({
|
|
88170
|
+
baseUrl: this.creds.baseUrl,
|
|
88171
|
+
botToken: this.creds.botToken,
|
|
88172
|
+
toUserId,
|
|
88173
|
+
contextToken: token,
|
|
88174
|
+
clientId: generateClientId(),
|
|
88175
|
+
items: [{ text: text3 }],
|
|
88176
|
+
fetchImpl: this.fetchImpl
|
|
88177
|
+
});
|
|
88178
|
+
if (resp.ret === SESSION_EXPIRED_ERRCODE || resp.errcode === SESSION_EXPIRED_ERRCODE) {
|
|
88179
|
+
throw new WeixinSessionExpiredError;
|
|
88180
|
+
}
|
|
88181
|
+
if (typeof resp.ret === "number" && resp.ret !== 0) {
|
|
88182
|
+
throw new Error(`sendmessage ret=${resp.ret} errmsg=${resp.errmsg ?? ""}`);
|
|
88183
|
+
}
|
|
88184
|
+
}
|
|
88185
|
+
toInbound(msg) {
|
|
88186
|
+
return {
|
|
88187
|
+
fromUserId: msg.from_user_id ?? "",
|
|
88188
|
+
contextToken: msg.context_token ?? null,
|
|
88189
|
+
text: WeixinClient.extractText(msg),
|
|
88190
|
+
raw: msg
|
|
88191
|
+
};
|
|
88192
|
+
}
|
|
88193
|
+
async backoffSleep(failures, signal) {
|
|
88194
|
+
const delay2 = Math.min(RETRY_DELAY_MS * 2 ** Math.max(0, failures - 1), BACKOFF_DELAY_MS);
|
|
88195
|
+
try {
|
|
88196
|
+
await sleep3(delay2, signal);
|
|
88197
|
+
} catch {}
|
|
88198
|
+
}
|
|
88199
|
+
linkSignals(external3, internal) {
|
|
88200
|
+
if (!external3)
|
|
88201
|
+
return internal;
|
|
88202
|
+
const controller = new AbortController;
|
|
88203
|
+
const onAbort = () => controller.abort();
|
|
88204
|
+
if (external3.aborted || internal.aborted) {
|
|
88205
|
+
controller.abort();
|
|
88206
|
+
return controller.signal;
|
|
88207
|
+
}
|
|
88208
|
+
external3.addEventListener("abort", onAbort, { once: true });
|
|
88209
|
+
internal.addEventListener("abort", onAbort, { once: true });
|
|
88210
|
+
return controller.signal;
|
|
88211
|
+
}
|
|
88212
|
+
}
|
|
88213
|
+
|
|
88214
|
+
// ../../apps/gateway/src/weixin/service.ts
|
|
88215
|
+
var KEEPALIVE_INTERVAL_MS = 8 * 60 * 60 * 1000;
|
|
88216
|
+
var KEEPALIVE_SWEEP_MS = 30 * 60 * 1000;
|
|
88217
|
+
|
|
88218
|
+
class WeixinService {
|
|
88219
|
+
createClient;
|
|
88220
|
+
runningAccounts = new Map;
|
|
88221
|
+
loginSessions = new Map;
|
|
88222
|
+
sessionExpiredNotified = new Set;
|
|
88223
|
+
refreshChain = Promise.resolve();
|
|
88224
|
+
keepaliveTimer = null;
|
|
88225
|
+
lastKeepaliveAt = new Map;
|
|
88226
|
+
constructor(createClient = (opts) => new WeixinClient(opts)) {
|
|
88227
|
+
this.createClient = createClient;
|
|
88228
|
+
}
|
|
88229
|
+
async refresh() {
|
|
88230
|
+
const run = this.refreshChain.then(() => this.doRefresh());
|
|
88231
|
+
this.refreshChain = run.catch(() => {});
|
|
88232
|
+
return run;
|
|
88233
|
+
}
|
|
88234
|
+
async doRefresh() {
|
|
88235
|
+
this.ensureKeepaliveTimer();
|
|
88236
|
+
const accounts = getAllWeixinAccounts();
|
|
88237
|
+
const activeIds = new Set(accounts.map((a) => a.id));
|
|
88238
|
+
for (const [id, session] of this.loginSessions) {
|
|
88239
|
+
if (!activeIds.has(id)) {
|
|
88240
|
+
session.abort.abort();
|
|
88241
|
+
this.loginSessions.delete(id);
|
|
88242
|
+
}
|
|
88243
|
+
}
|
|
88244
|
+
const toStop = [];
|
|
88245
|
+
for (const [id] of this.runningAccounts) {
|
|
88246
|
+
if (!activeIds.has(id)) {
|
|
88247
|
+
toStop.push(id);
|
|
88248
|
+
}
|
|
88249
|
+
}
|
|
88250
|
+
await Promise.all(toStop.map((id) => this.stopAccount(id)));
|
|
88251
|
+
for (const account of accounts) {
|
|
88252
|
+
const shouldRun = account.enabled && account.botTokenEnc != null && account.baseUrl != null;
|
|
88253
|
+
if (!shouldRun) {
|
|
88254
|
+
await this.stopAccount(account.id);
|
|
88255
|
+
continue;
|
|
88256
|
+
}
|
|
88257
|
+
let botToken;
|
|
88258
|
+
try {
|
|
88259
|
+
botToken = await decryptWithContext(account.botTokenEnc, {
|
|
88260
|
+
scope: "weixin_account",
|
|
88261
|
+
entityId: account.id,
|
|
88262
|
+
field: "bot_token_enc"
|
|
88263
|
+
});
|
|
88264
|
+
} catch (err) {
|
|
88265
|
+
console.error(`[weixin] failed to decrypt token for ${account.id}:`, err);
|
|
88266
|
+
continue;
|
|
88267
|
+
}
|
|
88268
|
+
const running = this.runningAccounts.get(account.id);
|
|
88269
|
+
if (running && running.botToken === botToken) {
|
|
88270
|
+
continue;
|
|
88271
|
+
}
|
|
88272
|
+
if (running) {
|
|
88273
|
+
await this.stopAccount(account.id);
|
|
88274
|
+
}
|
|
88275
|
+
this.startAccount(account, botToken);
|
|
88276
|
+
}
|
|
88277
|
+
}
|
|
88278
|
+
startAccount(account, botToken) {
|
|
88279
|
+
const baseUrl = account.baseUrl ?? "";
|
|
88280
|
+
const weixinUin = account.weixinUin ?? account.id;
|
|
88281
|
+
const client = this.createClient({ accountId: weixinUin, botToken, baseUrl });
|
|
88282
|
+
for (const { userId, contextToken } of getWeixinUserContextTokens(account.id)) {
|
|
88283
|
+
client.setContextToken(userId, contextToken);
|
|
88284
|
+
}
|
|
88285
|
+
this.runningAccounts.set(account.id, { id: account.id, botToken, client });
|
|
88286
|
+
this.sessionExpiredNotified.delete(account.id);
|
|
88287
|
+
client.start({
|
|
88288
|
+
loadSyncBuf: () => getWeixinAccountById(account.id)?.syncBuf ?? undefined,
|
|
88289
|
+
saveSyncBuf: (buf) => {
|
|
88290
|
+
updateWeixinAccount(account.id, { syncBuf: buf });
|
|
88291
|
+
},
|
|
88292
|
+
onMessage: (msg) => this.handleInbound(account.id, msg),
|
|
88293
|
+
onSessionExpired: () => this.handleSessionExpired(account.id),
|
|
88294
|
+
onError: (err) => console.error(`[weixin] account ${account.id} poll error:`, err)
|
|
88295
|
+
}).catch((err) => {
|
|
88296
|
+
if (!(err instanceof WeixinSessionExpiredError)) {
|
|
88297
|
+
console.error(`[weixin] account ${account.id} loop crashed:`, err);
|
|
88298
|
+
}
|
|
88299
|
+
}).finally(() => {
|
|
88300
|
+
const cur = this.runningAccounts.get(account.id);
|
|
88301
|
+
if (cur && cur.client === client) {
|
|
88302
|
+
this.runningAccounts.delete(account.id);
|
|
88303
|
+
}
|
|
88304
|
+
});
|
|
88305
|
+
console.log(`[weixin] account started: ${account.name} (${account.id})`);
|
|
88306
|
+
}
|
|
88307
|
+
async stopAccount(accountId) {
|
|
88308
|
+
const running = this.runningAccounts.get(accountId);
|
|
88309
|
+
if (!running) {
|
|
88310
|
+
return;
|
|
88311
|
+
}
|
|
88312
|
+
running.client.stop();
|
|
88313
|
+
this.runningAccounts.delete(accountId);
|
|
88314
|
+
console.log(`[weixin] account stopped: ${accountId}`);
|
|
88315
|
+
}
|
|
88316
|
+
async handleInbound(accountId, msg) {
|
|
88317
|
+
const account = getWeixinAccountById(accountId);
|
|
88318
|
+
if (!account) {
|
|
88319
|
+
return;
|
|
88320
|
+
}
|
|
88321
|
+
const userId = msg.fromUserId;
|
|
88322
|
+
if (!userId) {
|
|
88323
|
+
return;
|
|
88324
|
+
}
|
|
88325
|
+
const now2 = new Date().toISOString();
|
|
88326
|
+
try {
|
|
88327
|
+
upsertWeixinUserOnInbound({
|
|
88328
|
+
accountId,
|
|
88329
|
+
userId,
|
|
88330
|
+
displayName: userId,
|
|
88331
|
+
contextToken: msg.contextToken,
|
|
88332
|
+
allowAuthRequests: account.allowAuthRequests,
|
|
88333
|
+
at: now2
|
|
88334
|
+
});
|
|
88335
|
+
} catch (err) {
|
|
88336
|
+
console.error("[weixin] failed to upsert user on inbound:", err);
|
|
88337
|
+
}
|
|
88338
|
+
}
|
|
88339
|
+
handleSessionExpired(accountId) {
|
|
88340
|
+
if (!this.sessionExpiredNotified.has(accountId)) {
|
|
88341
|
+
this.sessionExpiredNotified.add(accountId);
|
|
88342
|
+
const account = getWeixinAccountById(accountId);
|
|
88343
|
+
console.warn(`[weixin] account ${account?.name ?? accountId} session expired; re-login required.`);
|
|
88344
|
+
for (const user of listAuthorizedWeixinUsersByAccount(accountId)) {
|
|
88345
|
+
setWeixinUserNeedsReactivation(accountId, user.userId, true);
|
|
88346
|
+
}
|
|
88347
|
+
}
|
|
88348
|
+
updateWeixinAccount(accountId, { botTokenEnc: null, baseUrl: null, weixinUin: null });
|
|
88349
|
+
}
|
|
88350
|
+
async sendGatewayOnlineMessage(siteName) {
|
|
88351
|
+
await this.sendToAuthorizedUsers({ text: t2("weixin.gatewayOnline", { siteName }) });
|
|
88352
|
+
}
|
|
88353
|
+
async sendToAuthorizedUsers(params) {
|
|
88354
|
+
for (const [accountId, running] of this.runningAccounts) {
|
|
88355
|
+
for (const user of listAuthorizedWeixinUsersByAccount(accountId)) {
|
|
88356
|
+
const cont = await this.sendToUser(accountId, running, user, params.text);
|
|
88357
|
+
if (!cont)
|
|
88358
|
+
break;
|
|
88359
|
+
}
|
|
88360
|
+
}
|
|
88361
|
+
}
|
|
88362
|
+
async sendToUser(accountId, running, user, text3) {
|
|
88363
|
+
try {
|
|
88364
|
+
await running.client.sendText(user.userId, text3);
|
|
88365
|
+
if (user.needsReactivation) {
|
|
88366
|
+
setWeixinUserNeedsReactivation(accountId, user.userId, false);
|
|
88367
|
+
}
|
|
88368
|
+
return true;
|
|
88369
|
+
} catch (err) {
|
|
88370
|
+
if (err instanceof WeixinSessionExpiredError) {
|
|
88371
|
+
this.handleSessionExpired(accountId);
|
|
88372
|
+
return false;
|
|
88373
|
+
}
|
|
88374
|
+
if (!user.needsReactivation) {
|
|
88375
|
+
console.warn(`[weixin] send to ${user.userId} failed; marking needs-reactivation:`, err instanceof Error ? err.message : err);
|
|
88376
|
+
}
|
|
88377
|
+
setWeixinUserNeedsReactivation(accountId, user.userId, true);
|
|
88378
|
+
return true;
|
|
88379
|
+
}
|
|
88380
|
+
}
|
|
88381
|
+
ensureKeepaliveTimer() {
|
|
88382
|
+
if (this.keepaliveTimer) {
|
|
88383
|
+
return;
|
|
88384
|
+
}
|
|
88385
|
+
this.keepaliveTimer = setInterval(() => void this.runKeepaliveSweep(), KEEPALIVE_SWEEP_MS);
|
|
88386
|
+
this.keepaliveTimer.unref?.();
|
|
88387
|
+
}
|
|
88388
|
+
async runKeepaliveSweep() {
|
|
88389
|
+
const now2 = Date.now();
|
|
88390
|
+
for (const [accountId, running] of this.runningAccounts) {
|
|
88391
|
+
for (const user of listAuthorizedWeixinUsersByAccount(accountId)) {
|
|
88392
|
+
const key = `${accountId}:${user.userId}`;
|
|
88393
|
+
const lastInbound = user.lastInboundAt ? Date.parse(user.lastInboundAt) : 0;
|
|
88394
|
+
const since = Math.max(lastInbound, this.lastKeepaliveAt.get(key) ?? 0);
|
|
88395
|
+
if (now2 - since < KEEPALIVE_INTERVAL_MS) {
|
|
88396
|
+
continue;
|
|
88397
|
+
}
|
|
88398
|
+
this.lastKeepaliveAt.set(key, now2);
|
|
88399
|
+
const cont = await this.sendToUser(accountId, running, user, t2("weixin.keepalivePrompt"));
|
|
88400
|
+
if (!cont)
|
|
88401
|
+
break;
|
|
88402
|
+
}
|
|
88403
|
+
}
|
|
88404
|
+
}
|
|
88405
|
+
async sendTestMessage(accountId, userId, text3) {
|
|
88406
|
+
const running = this.runningAccounts.get(accountId);
|
|
88407
|
+
if (!running) {
|
|
88408
|
+
throw new Error(t2("weixin.accountNotRunning"));
|
|
88409
|
+
}
|
|
88410
|
+
await running.client.sendText(userId, text3);
|
|
88411
|
+
}
|
|
88412
|
+
async sendTestMessageToBoundUser(accountId, text3) {
|
|
88413
|
+
const [user] = listAuthorizedWeixinUsersByAccount(accountId);
|
|
88414
|
+
if (!user) {
|
|
88415
|
+
throw new Error(t2("weixin.userNotFound"));
|
|
88416
|
+
}
|
|
88417
|
+
await this.sendTestMessage(accountId, user.userId, text3);
|
|
88418
|
+
}
|
|
88419
|
+
async startLogin(accountId) {
|
|
88420
|
+
const account = getWeixinAccountById(accountId);
|
|
88421
|
+
if (!account) {
|
|
88422
|
+
throw new Error(t2("weixin.accountNotFound"));
|
|
88423
|
+
}
|
|
88424
|
+
const prev = this.loginSessions.get(accountId);
|
|
88425
|
+
if (prev) {
|
|
88426
|
+
prev.abort.abort();
|
|
88427
|
+
this.loginSessions.delete(accountId);
|
|
88428
|
+
}
|
|
88429
|
+
const abort = new AbortController;
|
|
88430
|
+
const client = this.createClient({});
|
|
88431
|
+
return await new Promise((resolve3, reject) => {
|
|
88432
|
+
let resolved = false;
|
|
88433
|
+
const loginPromise = client.login({
|
|
88434
|
+
signal: abort.signal,
|
|
88435
|
+
onQrcode: (qr) => {
|
|
88436
|
+
this.loginSessions.set(accountId, {
|
|
88437
|
+
status: "pending",
|
|
88438
|
+
qrcodeUrl: qr.url,
|
|
88439
|
+
qrcodeId: qr.qrcodeId,
|
|
88440
|
+
client,
|
|
88441
|
+
abort
|
|
88442
|
+
});
|
|
88443
|
+
if (!resolved) {
|
|
88444
|
+
resolved = true;
|
|
88445
|
+
resolve3({ qrcodeUrl: qr.url, qrcodeId: qr.qrcodeId });
|
|
88446
|
+
}
|
|
88447
|
+
}
|
|
88448
|
+
});
|
|
88449
|
+
loginPromise.then(async (creds) => {
|
|
88450
|
+
await this.persistLogin(accountId, creds);
|
|
88451
|
+
const session = this.loginSessions.get(accountId);
|
|
88452
|
+
if (session) {
|
|
88453
|
+
session.status = "confirmed";
|
|
88454
|
+
}
|
|
88455
|
+
await this.refresh();
|
|
88456
|
+
}).catch((err) => {
|
|
88457
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
88458
|
+
const status = /expired|timed out/i.test(message) ? "expired" : "error";
|
|
88459
|
+
const session = this.loginSessions.get(accountId);
|
|
88460
|
+
if (session) {
|
|
88461
|
+
session.status = status;
|
|
88462
|
+
session.message = message;
|
|
88463
|
+
} else {
|
|
88464
|
+
this.loginSessions.set(accountId, {
|
|
88465
|
+
status,
|
|
88466
|
+
message,
|
|
88467
|
+
qrcodeUrl: "",
|
|
88468
|
+
qrcodeId: "",
|
|
88469
|
+
client,
|
|
88470
|
+
abort
|
|
88471
|
+
});
|
|
88472
|
+
}
|
|
88473
|
+
if (!resolved) {
|
|
88474
|
+
resolved = true;
|
|
88475
|
+
reject(err);
|
|
88476
|
+
}
|
|
88477
|
+
});
|
|
88478
|
+
});
|
|
88479
|
+
}
|
|
88480
|
+
getLoginStatus(accountId) {
|
|
88481
|
+
const account = getWeixinAccountById(accountId);
|
|
88482
|
+
const loggedIn = account?.botTokenEnc != null;
|
|
88483
|
+
const session = this.loginSessions.get(accountId);
|
|
88484
|
+
if (!session) {
|
|
88485
|
+
return { status: loggedIn ? "confirmed" : "pending", loggedIn };
|
|
88486
|
+
}
|
|
88487
|
+
return { status: session.status, loggedIn, message: session.message };
|
|
88488
|
+
}
|
|
88489
|
+
async persistLogin(accountId, creds) {
|
|
88490
|
+
const botTokenEnc = await encrypt(creds.botToken);
|
|
88491
|
+
updateWeixinAccount(accountId, {
|
|
88492
|
+
weixinUin: creds.accountId,
|
|
88493
|
+
botTokenEnc,
|
|
88494
|
+
baseUrl: creds.baseUrl
|
|
88495
|
+
});
|
|
88496
|
+
this.sessionExpiredNotified.delete(accountId);
|
|
88497
|
+
}
|
|
88498
|
+
async stopAll() {
|
|
88499
|
+
if (this.keepaliveTimer) {
|
|
88500
|
+
clearInterval(this.keepaliveTimer);
|
|
88501
|
+
this.keepaliveTimer = null;
|
|
88502
|
+
}
|
|
88503
|
+
for (const session of this.loginSessions.values()) {
|
|
88504
|
+
session.abort.abort();
|
|
88505
|
+
}
|
|
88506
|
+
this.loginSessions.clear();
|
|
88507
|
+
const ids = Array.from(this.runningAccounts.keys());
|
|
88508
|
+
await Promise.all(ids.map((id) => this.stopAccount(id)));
|
|
88509
|
+
}
|
|
88510
|
+
}
|
|
88511
|
+
var weixinService = new WeixinService;
|
|
88512
|
+
|
|
88513
|
+
// ../../apps/gateway/src/events/channels/weixin.ts
|
|
88514
|
+
var EMOJI_MAP = {
|
|
88515
|
+
terminal_bell: "\uD83D\uDD14",
|
|
88516
|
+
terminal_notification: "\uD83D\uDD14",
|
|
88517
|
+
tmux_window_close: "\uD83E\uDE9F",
|
|
88518
|
+
tmux_pane_close: "\uD83D\uDCF1",
|
|
88519
|
+
device_tmux_missing: "\u26A0\uFE0F",
|
|
88520
|
+
device_disconnect: "\uD83D\uDD0C",
|
|
88521
|
+
session_created: "\uD83C\uDD95",
|
|
88522
|
+
session_closed: "\uD83D\uDEAA",
|
|
88523
|
+
agent_confirmation_pending: "\uD83E\uDD16",
|
|
88524
|
+
agent_turn_finished: "\uD83E\uDD16",
|
|
88525
|
+
agent_error: "\uD83E\uDD16",
|
|
88526
|
+
watch_triggered: "\uD83D\uDC41\uFE0F",
|
|
88527
|
+
watch_model_unavailable: "\uD83D\uDC41\uFE0F",
|
|
88528
|
+
watch_rule_error: "\uD83D\uDC41\uFE0F"
|
|
88529
|
+
};
|
|
88530
|
+
|
|
88531
|
+
class WeixinChannel {
|
|
88532
|
+
id = "weixin";
|
|
88533
|
+
async notify(eventType, event) {
|
|
88534
|
+
const settings = getSiteSettings();
|
|
88535
|
+
if (eventType === "terminal_bell") {
|
|
88536
|
+
if (!settings.enableWeixinBellPush) {
|
|
88537
|
+
return;
|
|
88538
|
+
}
|
|
88539
|
+
await weixinService.sendToAuthorizedUsers({ text: this.formatBellMessage(event) });
|
|
88540
|
+
return;
|
|
88541
|
+
}
|
|
88542
|
+
if (!settings.enableWeixinNotificationPush) {
|
|
88543
|
+
return;
|
|
88544
|
+
}
|
|
88545
|
+
const text3 = eventType === "terminal_notification" ? this.formatNotificationMessage(event) : this.formatGenericMessage(event, settings);
|
|
88546
|
+
await weixinService.sendToAuthorizedUsers({ text: text3 });
|
|
88547
|
+
}
|
|
88548
|
+
buildTerminalTopbarLabel(event) {
|
|
88549
|
+
const windowLabel = typeof event.tmux?.windowIndex === "number" ? `${event.tmux.windowIndex}` : event.tmux?.windowId ?? "?";
|
|
88550
|
+
const paneLabel = typeof event.tmux?.paneIndex === "number" ? `${event.tmux.paneIndex}` : event.tmux?.paneId ?? "?";
|
|
88551
|
+
return t2("notification.telegramBell.terminalTopbarLabel", {
|
|
88552
|
+
window: windowLabel,
|
|
88553
|
+
pane: paneLabel,
|
|
88554
|
+
device: event.device.name
|
|
88555
|
+
});
|
|
88556
|
+
}
|
|
88557
|
+
buildPaneMetaLines(event) {
|
|
88558
|
+
const lines = [];
|
|
88559
|
+
if (event.tmux?.paneTitle) {
|
|
88560
|
+
lines.push(`${t2("notification.paneTitle")}\uFF1A${event.tmux.paneTitle}`);
|
|
88561
|
+
}
|
|
88562
|
+
if (event.tmux?.paneCurrentCommand) {
|
|
88563
|
+
lines.push(`${t2("notification.process")}\uFF1A${event.tmux.paneCurrentCommand}`);
|
|
88564
|
+
}
|
|
88565
|
+
return lines;
|
|
88566
|
+
}
|
|
88567
|
+
formatBellMessage(event) {
|
|
88568
|
+
const lines = [
|
|
88569
|
+
t2("notification.telegramBell.title", {
|
|
88570
|
+
siteName: event.site.name,
|
|
88571
|
+
terminalTopbarLabel: this.buildTerminalTopbarLabel(event)
|
|
88572
|
+
})
|
|
88573
|
+
];
|
|
88574
|
+
lines.push(...this.buildPaneMetaLines(event));
|
|
88575
|
+
const paneUrl = normalizeHttpUrl(buildPaneUrl(event));
|
|
88576
|
+
if (paneUrl) {
|
|
88577
|
+
lines.push("", paneUrl);
|
|
88578
|
+
}
|
|
88579
|
+
return lines.join(`
|
|
88580
|
+
`);
|
|
88581
|
+
}
|
|
88582
|
+
formatNotificationMessage(event) {
|
|
88583
|
+
const title = typeof event.payload?.title === "string" ? event.payload.title : "";
|
|
88584
|
+
const body = typeof event.payload?.message === "string" ? event.payload.message : "";
|
|
88585
|
+
const lines = [];
|
|
88586
|
+
if (title) {
|
|
88587
|
+
lines.push(title);
|
|
88588
|
+
}
|
|
88589
|
+
if (body) {
|
|
88590
|
+
lines.push(body);
|
|
88591
|
+
}
|
|
88592
|
+
lines.push(...this.buildPaneMetaLines(event));
|
|
88593
|
+
const footer = `from ${event.site.name}: ${this.buildTerminalTopbarLabel(event)}`;
|
|
88594
|
+
lines.push("", footer);
|
|
88595
|
+
const paneUrl = normalizeHttpUrl(buildPaneUrl(event));
|
|
88596
|
+
if (paneUrl) {
|
|
88597
|
+
lines.push(paneUrl);
|
|
88598
|
+
}
|
|
88599
|
+
return lines.join(`
|
|
88600
|
+
`);
|
|
88601
|
+
}
|
|
88602
|
+
formatGenericMessage(event, settings) {
|
|
88603
|
+
const eventTypeLabel = t2(`notification.eventType.${event.eventType}`);
|
|
88604
|
+
const windowLabel = event.tmux?.windowIndex !== undefined ? `${event.tmux.windowIndex} (${event.tmux.windowId ?? "-"})` : event.tmux?.windowId ?? "-";
|
|
88605
|
+
const paneLabel = event.tmux?.paneIndex !== undefined ? `${event.tmux.paneIndex} (${event.tmux.paneId ?? "-"})` : event.tmux?.paneId ?? "-";
|
|
88606
|
+
const lines = [
|
|
88607
|
+
`${EMOJI_MAP[event.eventType] ?? "\uD83D\uDCE2"} ${eventTypeLabel}`,
|
|
88608
|
+
`${t2("notification.site")}\uFF1A${event.site.name}`,
|
|
88609
|
+
`${t2("notification.time")}\uFF1A${new Date(event.timestamp).toLocaleString(toBCP47(settings.language))}`,
|
|
88610
|
+
`${t2("notification.device")}\uFF1A${event.device.name} (${event.device.type})`,
|
|
88611
|
+
`${t2("notification.window")}\uFF1A${windowLabel}`,
|
|
88612
|
+
`${t2("notification.pane")}\uFF1A${paneLabel}`
|
|
88613
|
+
];
|
|
88614
|
+
lines.push(...this.buildPaneMetaLines(event));
|
|
88615
|
+
if (typeof event.payload?.message === "string") {
|
|
88616
|
+
lines.push(`${t2("notification.message")}\uFF1A${event.payload.message}`);
|
|
88617
|
+
}
|
|
88618
|
+
const paneUrl = normalizeHttpUrl(buildPaneUrl(event));
|
|
88619
|
+
if (paneUrl) {
|
|
88620
|
+
lines.push("", paneUrl);
|
|
88621
|
+
}
|
|
88622
|
+
return lines.join(`
|
|
88623
|
+
`);
|
|
88624
|
+
}
|
|
88625
|
+
}
|
|
88626
|
+
var weixinChannel = new WeixinChannel;
|
|
88627
|
+
|
|
88628
|
+
// ../../apps/gateway/src/events/index.ts
|
|
88629
|
+
class EventNotifier {
|
|
88630
|
+
bellThrottleMap = new Map;
|
|
88631
|
+
notificationThrottleMap = new Map;
|
|
88632
|
+
channels = [
|
|
88633
|
+
webhookChannel,
|
|
88634
|
+
telegramChannel,
|
|
88635
|
+
weixinChannel
|
|
88636
|
+
];
|
|
88637
|
+
async notify(eventType, event) {
|
|
88638
|
+
const fullEvent = {
|
|
88639
|
+
...event,
|
|
88640
|
+
eventType,
|
|
88641
|
+
timestamp: new Date().toISOString()
|
|
88642
|
+
};
|
|
88643
|
+
if (eventType === "terminal_bell") {
|
|
88644
|
+
if (!this.shouldPassBellThrottle(fullEvent)) {
|
|
88645
|
+
return;
|
|
88646
|
+
}
|
|
88647
|
+
} else if (eventType === "terminal_notification") {
|
|
88648
|
+
if (!this.shouldPassNotificationThrottle(fullEvent)) {
|
|
88649
|
+
return;
|
|
88650
|
+
}
|
|
88651
|
+
}
|
|
88652
|
+
await Promise.all(this.channels.map((channel) => channel.notify(eventType, fullEvent)));
|
|
88653
|
+
}
|
|
88654
|
+
shouldPassBellThrottle(event) {
|
|
88655
|
+
const settings = getSiteSettings();
|
|
88656
|
+
const throttleMs = Math.max(0, settings.bellThrottleSeconds) * 1000;
|
|
88657
|
+
if (throttleMs === 0) {
|
|
88658
|
+
return true;
|
|
88659
|
+
}
|
|
88660
|
+
const key = `${event.device.id}:${event.tmux?.paneId ?? "-"}:${event.eventType}`;
|
|
88661
|
+
const now2 = Date.now();
|
|
88662
|
+
const previous = this.bellThrottleMap.get(key) ?? 0;
|
|
88663
|
+
if (now2 - previous < throttleMs) {
|
|
88664
|
+
return false;
|
|
88665
|
+
}
|
|
88666
|
+
this.bellThrottleMap.set(key, now2);
|
|
88667
|
+
return true;
|
|
88668
|
+
}
|
|
88669
|
+
shouldPassNotificationThrottle(event) {
|
|
88670
|
+
const settings = getSiteSettings();
|
|
88671
|
+
const throttleMs = Math.max(0, settings.notificationThrottleSeconds) * 1000;
|
|
88672
|
+
if (throttleMs === 0) {
|
|
88673
|
+
return true;
|
|
88674
|
+
}
|
|
88675
|
+
const source = typeof event.payload?.source === "string" ? event.payload.source : "unknown";
|
|
88676
|
+
const key = `${event.device.id}:${event.tmux?.paneId ?? "-"}:notification:${source}`;
|
|
88677
|
+
const now2 = Date.now();
|
|
88678
|
+
const previous = this.notificationThrottleMap.get(key) ?? 0;
|
|
88679
|
+
if (now2 - previous < throttleMs) {
|
|
88680
|
+
return false;
|
|
88681
|
+
}
|
|
88682
|
+
this.notificationThrottleMap.set(key, now2);
|
|
88683
|
+
return true;
|
|
88684
|
+
}
|
|
88685
|
+
}
|
|
87300
88686
|
var eventNotifier = new EventNotifier;
|
|
87301
88687
|
|
|
87302
88688
|
// ../../node_modules/.bun/@ai-sdk+openai@3.0.71+68a1e3a0c4588df3/node_modules/@ai-sdk/openai/dist/index.mjs
|
|
@@ -96906,6 +98292,28 @@ function createPaneStreamParser(options) {
|
|
|
96906
98292
|
options.onNotification({ source: "osc1337", body: "RequestAttention" });
|
|
96907
98293
|
}
|
|
96908
98294
|
return;
|
|
98295
|
+
case "52": {
|
|
98296
|
+
const separatorIndex = payload.indexOf(";");
|
|
98297
|
+
if (separatorIndex < 0) {
|
|
98298
|
+
return;
|
|
98299
|
+
}
|
|
98300
|
+
const base64Data = payload.slice(separatorIndex + 1);
|
|
98301
|
+
if (!base64Data || base64Data === "?") {
|
|
98302
|
+
return;
|
|
98303
|
+
}
|
|
98304
|
+
try {
|
|
98305
|
+
const binaryString = atob(base64Data);
|
|
98306
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
98307
|
+
for (let i = 0;i < binaryString.length; i++) {
|
|
98308
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
98309
|
+
}
|
|
98310
|
+
const text3 = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
|
|
98311
|
+
if (text3) {
|
|
98312
|
+
options.onClipboardWrite?.(text3);
|
|
98313
|
+
}
|
|
98314
|
+
} catch {}
|
|
98315
|
+
return;
|
|
98316
|
+
}
|
|
96909
98317
|
case "133": {
|
|
96910
98318
|
const parts = payload.split(";");
|
|
96911
98319
|
const kind = parts[0];
|
|
@@ -97045,7 +98453,7 @@ function createPaneStreamParser(options) {
|
|
|
97045
98453
|
}
|
|
97046
98454
|
if (phase === "osc-params") {
|
|
97047
98455
|
if (byte === 59) {
|
|
97048
|
-
phase = oscKind === "0" || oscKind === "1" || oscKind === "2" || oscKind === "9" || oscKind === "99" || oscKind === "133" || oscKind === "777" || oscKind === "1337" ? "osc-body" : "osc-body-ignore";
|
|
98456
|
+
phase = oscKind === "0" || oscKind === "1" || oscKind === "2" || oscKind === "9" || oscKind === "52" || oscKind === "99" || oscKind === "133" || oscKind === "777" || oscKind === "1337" ? "osc-body" : "osc-body-ignore";
|
|
97049
98457
|
return;
|
|
97050
98458
|
}
|
|
97051
98459
|
if (byte === 7) {
|
|
@@ -97173,7 +98581,8 @@ function createControlModeSubscription(callbacks) {
|
|
|
97173
98581
|
onTitle: (title) => callbacks.onTitle(paneId, title),
|
|
97174
98582
|
onBell: () => callbacks.onBell(paneId),
|
|
97175
98583
|
onNotification: (notification) => callbacks.onNotification(paneId, notification),
|
|
97176
|
-
onPromptMarker: (marker24) => callbacks.onPromptMarker?.(paneId, marker24)
|
|
98584
|
+
onPromptMarker: (marker24) => callbacks.onPromptMarker?.(paneId, marker24),
|
|
98585
|
+
onClipboardWrite: (text3) => callbacks.onClipboardWrite?.(paneId, text3)
|
|
97177
98586
|
});
|
|
97178
98587
|
paneParsers.set(paneId, parser2);
|
|
97179
98588
|
return parser2;
|
|
@@ -97676,7 +99085,10 @@ class LocalExternalTmuxConnection {
|
|
|
97676
99085
|
if (!this.connected) {
|
|
97677
99086
|
return;
|
|
97678
99087
|
}
|
|
97679
|
-
const argv =
|
|
99088
|
+
const argv = ["new-window", "-t", this.sessionName, "-c", this.resolveDefaultWorkingDir()];
|
|
99089
|
+
if (name24) {
|
|
99090
|
+
argv.push("-n", name24);
|
|
99091
|
+
}
|
|
97680
99092
|
this.runAndRefresh(argv).catch((error51) => {
|
|
97681
99093
|
this.callbacks.onError(error51);
|
|
97682
99094
|
});
|
|
@@ -97705,6 +99117,20 @@ class LocalExternalTmuxConnection {
|
|
|
97705
99117
|
this.callbacks.onError(error51);
|
|
97706
99118
|
});
|
|
97707
99119
|
}
|
|
99120
|
+
updateDefaultWorkingDir(dir2) {
|
|
99121
|
+
if (this.device) {
|
|
99122
|
+
this.device = { ...this.device, defaultWorkingDir: dir2 };
|
|
99123
|
+
}
|
|
99124
|
+
if (this.connected) {
|
|
99125
|
+
this.runTmuxAllowFailure([
|
|
99126
|
+
"set-option",
|
|
99127
|
+
"-t",
|
|
99128
|
+
this.sessionName,
|
|
99129
|
+
"default-path",
|
|
99130
|
+
this.resolveDefaultWorkingDir()
|
|
99131
|
+
]);
|
|
99132
|
+
}
|
|
99133
|
+
}
|
|
97708
99134
|
setWindowStyle(style) {
|
|
97709
99135
|
if (!this.connected) {
|
|
97710
99136
|
return;
|
|
@@ -97734,12 +99160,15 @@ class LocalExternalTmuxConnection {
|
|
|
97734
99160
|
const { stdout } = await this.runTmux(["display-message", "-p", "-t", paneId, PANE_META_FORMAT], "silent");
|
|
97735
99161
|
return parsePaneMeta(stdout);
|
|
97736
99162
|
}
|
|
99163
|
+
resolveDefaultWorkingDir() {
|
|
99164
|
+
return this.device?.defaultWorkingDir?.trim() || homedir();
|
|
99165
|
+
}
|
|
97737
99166
|
async ensureSession() {
|
|
97738
99167
|
const exists3 = await this.runTmuxAllowFailure(["has-session", "-t", this.sessionName]);
|
|
97739
99168
|
if (exists3.exitCode === 0) {
|
|
97740
99169
|
return;
|
|
97741
99170
|
}
|
|
97742
|
-
await this.runTmux(["new-session", "-d", "-c",
|
|
99171
|
+
await this.runTmux(["new-session", "-d", "-c", this.resolveDefaultWorkingDir(), "-s", this.sessionName]);
|
|
97743
99172
|
}
|
|
97744
99173
|
async configureSessionOptions() {
|
|
97745
99174
|
await this.runTmuxAllowFailure([
|
|
@@ -97793,6 +99222,13 @@ class LocalExternalTmuxConnection {
|
|
|
97793
99222
|
"COLORTERM",
|
|
97794
99223
|
"truecolor"
|
|
97795
99224
|
]);
|
|
99225
|
+
await this.runTmuxAllowFailure([
|
|
99226
|
+
"set-option",
|
|
99227
|
+
"-t",
|
|
99228
|
+
this.sessionName,
|
|
99229
|
+
"default-path",
|
|
99230
|
+
this.resolveDefaultWorkingDir()
|
|
99231
|
+
]);
|
|
97796
99232
|
await this.configureWindowStyle();
|
|
97797
99233
|
}
|
|
97798
99234
|
async configureWindowStyle(styleValue = config.tmuxWindowStyle) {
|
|
@@ -97911,6 +99347,9 @@ class LocalExternalTmuxConnection {
|
|
|
97911
99347
|
onPromptMarker: (paneId, marker24) => {
|
|
97912
99348
|
this.callbacks.onPromptMarker?.(paneId, marker24);
|
|
97913
99349
|
},
|
|
99350
|
+
onClipboardWrite: (paneId, text3) => {
|
|
99351
|
+
this.callbacks.onClipboardWrite?.(paneId, text3);
|
|
99352
|
+
},
|
|
97914
99353
|
onStructureChanged: () => {
|
|
97915
99354
|
this.requestSnapshot();
|
|
97916
99355
|
},
|
|
@@ -98054,7 +99493,7 @@ class LocalExternalTmuxConnection {
|
|
|
98054
99493
|
async closeWindowInternal(windowId) {
|
|
98055
99494
|
const count2 = Number.parseInt((await this.runTmux(["display-message", "-p", "-t", this.sessionName, "#{session_windows}"])).stdout.trim() || "0", 10);
|
|
98056
99495
|
if (count2 <= 1) {
|
|
98057
|
-
await this.runTmux(["new-window", "-d", "-t", this.sessionName]);
|
|
99496
|
+
await this.runTmux(["new-window", "-d", "-t", this.sessionName, "-c", this.resolveDefaultWorkingDir()]);
|
|
98058
99497
|
}
|
|
98059
99498
|
await this.runAndRefresh(["kill-window", "-t", windowId], true);
|
|
98060
99499
|
}
|
|
@@ -98878,7 +100317,10 @@ class SshExternalTmuxConnection {
|
|
|
98878
100317
|
if (!this.connected) {
|
|
98879
100318
|
return;
|
|
98880
100319
|
}
|
|
98881
|
-
const argv =
|
|
100320
|
+
const argv = ["new-window", "-t", this.sessionName, "-c", this.resolveDefaultWorkingDir()];
|
|
100321
|
+
if (name24) {
|
|
100322
|
+
argv.push("-n", name24);
|
|
100323
|
+
}
|
|
98882
100324
|
this.runAndRefresh(argv).catch((error51) => {
|
|
98883
100325
|
this.callbacks.onError(error51);
|
|
98884
100326
|
});
|
|
@@ -98907,6 +100349,20 @@ class SshExternalTmuxConnection {
|
|
|
98907
100349
|
this.callbacks.onError(error51);
|
|
98908
100350
|
});
|
|
98909
100351
|
}
|
|
100352
|
+
updateDefaultWorkingDir(dir2) {
|
|
100353
|
+
if (this.device) {
|
|
100354
|
+
this.device = { ...this.device, defaultWorkingDir: dir2 };
|
|
100355
|
+
}
|
|
100356
|
+
if (this.connected) {
|
|
100357
|
+
this.runTmuxAllowFailure([
|
|
100358
|
+
"set-option",
|
|
100359
|
+
"-t",
|
|
100360
|
+
this.sessionName,
|
|
100361
|
+
"default-path",
|
|
100362
|
+
this.resolveDefaultWorkingDir()
|
|
100363
|
+
]);
|
|
100364
|
+
}
|
|
100365
|
+
}
|
|
98910
100366
|
setWindowStyle(style) {
|
|
98911
100367
|
if (!this.connected) {
|
|
98912
100368
|
return;
|
|
@@ -99042,12 +100498,15 @@ class SshExternalTmuxConnection {
|
|
|
99042
100498
|
throw new Error(message);
|
|
99043
100499
|
}
|
|
99044
100500
|
}
|
|
100501
|
+
resolveDefaultWorkingDir() {
|
|
100502
|
+
return this.device?.defaultWorkingDir?.trim() || this.remoteHomeDir;
|
|
100503
|
+
}
|
|
99045
100504
|
async ensureSession() {
|
|
99046
100505
|
const exists3 = await this.runTmuxAllowFailure(["has-session", "-t", this.sessionName]);
|
|
99047
100506
|
if (exists3.exitCode === 0) {
|
|
99048
100507
|
return;
|
|
99049
100508
|
}
|
|
99050
|
-
await this.runTmux(["new-session", "-d", "-c", this.
|
|
100509
|
+
await this.runTmux(["new-session", "-d", "-c", this.resolveDefaultWorkingDir(), "-s", this.sessionName]);
|
|
99051
100510
|
}
|
|
99052
100511
|
async configureSessionOptions() {
|
|
99053
100512
|
await this.runTmuxAllowFailure([
|
|
@@ -99101,6 +100560,13 @@ class SshExternalTmuxConnection {
|
|
|
99101
100560
|
"COLORTERM",
|
|
99102
100561
|
"truecolor"
|
|
99103
100562
|
]);
|
|
100563
|
+
await this.runTmuxAllowFailure([
|
|
100564
|
+
"set-option",
|
|
100565
|
+
"-t",
|
|
100566
|
+
this.sessionName,
|
|
100567
|
+
"default-path",
|
|
100568
|
+
this.resolveDefaultWorkingDir()
|
|
100569
|
+
]);
|
|
99104
100570
|
await this.configureWindowStyle();
|
|
99105
100571
|
}
|
|
99106
100572
|
async configureWindowStyle(styleValue = config.tmuxWindowStyle) {
|
|
@@ -99215,6 +100681,9 @@ class SshExternalTmuxConnection {
|
|
|
99215
100681
|
onPromptMarker: (paneId, marker24) => {
|
|
99216
100682
|
this.callbacks.onPromptMarker?.(paneId, marker24);
|
|
99217
100683
|
},
|
|
100684
|
+
onClipboardWrite: (paneId, text3) => {
|
|
100685
|
+
this.callbacks.onClipboardWrite?.(paneId, text3);
|
|
100686
|
+
},
|
|
99218
100687
|
onStructureChanged: () => {
|
|
99219
100688
|
this.requestSnapshot();
|
|
99220
100689
|
},
|
|
@@ -99322,7 +100791,7 @@ class SshExternalTmuxConnection {
|
|
|
99322
100791
|
async closeWindowInternal(windowId) {
|
|
99323
100792
|
const count2 = Number.parseInt((await this.runTmux(["display-message", "-p", "-t", this.sessionName, "#{session_windows}"])).stdout.trim() || "0", 10);
|
|
99324
100793
|
if (count2 <= 1) {
|
|
99325
|
-
await this.runTmux(["new-window", "-d", "-t", this.sessionName]);
|
|
100794
|
+
await this.runTmux(["new-window", "-d", "-t", this.sessionName, "-c", this.resolveDefaultWorkingDir()]);
|
|
99326
100795
|
}
|
|
99327
100796
|
await this.runAndRefresh(["kill-window", "-t", windowId], true);
|
|
99328
100797
|
}
|
|
@@ -99792,6 +101261,9 @@ class DeviceSessionRuntime {
|
|
|
99792
101261
|
onPromptMarker: (paneId, marker24) => {
|
|
99793
101262
|
this.broadcast((listener) => listener.onPromptMarker?.(paneId, marker24));
|
|
99794
101263
|
},
|
|
101264
|
+
onClipboardWrite: (paneId, text3) => {
|
|
101265
|
+
this.broadcast((listener) => listener.onClipboardWrite?.(paneId, text3));
|
|
101266
|
+
},
|
|
99795
101267
|
onSnapshot: (payload) => {
|
|
99796
101268
|
this.broadcast((listener) => listener.onSnapshot?.(payload));
|
|
99797
101269
|
},
|
|
@@ -99862,6 +101334,9 @@ class DeviceSessionRuntime {
|
|
|
99862
101334
|
selectWindow(windowId) {
|
|
99863
101335
|
this.connection.selectWindow(windowId);
|
|
99864
101336
|
}
|
|
101337
|
+
updateDefaultWorkingDir(dir2) {
|
|
101338
|
+
this.connection.updateDefaultWorkingDir(dir2);
|
|
101339
|
+
}
|
|
99865
101340
|
createWindow(name24) {
|
|
99866
101341
|
this.connection.createWindow(name24);
|
|
99867
101342
|
}
|
|
@@ -102432,6 +103907,10 @@ class PushSupervisor {
|
|
|
102432
103907
|
}
|
|
102433
103908
|
await this.upsert(deviceId);
|
|
102434
103909
|
}
|
|
103910
|
+
updateDefaultWorkingDir(deviceId, dir2) {
|
|
103911
|
+
const entry = this.entries.get(deviceId);
|
|
103912
|
+
entry?.runtime?.updateDefaultWorkingDir(dir2);
|
|
103913
|
+
}
|
|
102435
103914
|
remove(deviceId) {
|
|
102436
103915
|
const entry = this.entries.get(deviceId);
|
|
102437
103916
|
if (!entry) {
|
|
@@ -103566,6 +105045,49 @@ function typeFromPerms(perms) {
|
|
|
103566
105045
|
return "other";
|
|
103567
105046
|
}
|
|
103568
105047
|
}
|
|
105048
|
+
function unescapeOctal(input) {
|
|
105049
|
+
if (!input.includes("\\"))
|
|
105050
|
+
return input;
|
|
105051
|
+
const result = [];
|
|
105052
|
+
let pendingBytes = [];
|
|
105053
|
+
const flushBytes = () => {
|
|
105054
|
+
if (pendingBytes.length === 0)
|
|
105055
|
+
return;
|
|
105056
|
+
result.push(new TextDecoder().decode(new Uint8Array(pendingBytes)));
|
|
105057
|
+
pendingBytes = [];
|
|
105058
|
+
};
|
|
105059
|
+
let i = 0;
|
|
105060
|
+
while (i < input.length) {
|
|
105061
|
+
if (input[i] !== "\\") {
|
|
105062
|
+
flushBytes();
|
|
105063
|
+
result.push(input[i]);
|
|
105064
|
+
i++;
|
|
105065
|
+
continue;
|
|
105066
|
+
}
|
|
105067
|
+
if (input[i + 1] === "\\") {
|
|
105068
|
+
flushBytes();
|
|
105069
|
+
result.push("\\");
|
|
105070
|
+
i += 2;
|
|
105071
|
+
continue;
|
|
105072
|
+
}
|
|
105073
|
+
if (i + 3 < input.length) {
|
|
105074
|
+
const digits = input.slice(i + 1, i + 4);
|
|
105075
|
+
if (/^[0-7]{3}$/.test(digits)) {
|
|
105076
|
+
const val = parseInt(digits, 8);
|
|
105077
|
+
if (val <= 255) {
|
|
105078
|
+
pendingBytes.push(val);
|
|
105079
|
+
i += 4;
|
|
105080
|
+
continue;
|
|
105081
|
+
}
|
|
105082
|
+
}
|
|
105083
|
+
}
|
|
105084
|
+
flushBytes();
|
|
105085
|
+
result.push("\\");
|
|
105086
|
+
i++;
|
|
105087
|
+
}
|
|
105088
|
+
flushBytes();
|
|
105089
|
+
return result.join("");
|
|
105090
|
+
}
|
|
103569
105091
|
function parseListOnly(stdout) {
|
|
103570
105092
|
const entries = [];
|
|
103571
105093
|
for (const rawLine of stdout.split(`
|
|
@@ -103575,7 +105097,7 @@ function parseListOnly(stdout) {
|
|
|
103575
105097
|
if (!m)
|
|
103576
105098
|
continue;
|
|
103577
105099
|
const type = typeFromPerms(m[1]);
|
|
103578
|
-
let name24 = m[9];
|
|
105100
|
+
let name24 = unescapeOctal(m[9]);
|
|
103579
105101
|
if (type === "symlink") {
|
|
103580
105102
|
const arrow = name24.indexOf(" -> ");
|
|
103581
105103
|
if (arrow >= 0)
|
|
@@ -103724,7 +105246,7 @@ function rsyncTargetArg(spec, remotePath) {
|
|
|
103724
105246
|
return `${spec.targetPrefix}${quoteShellArg(remotePath)}`;
|
|
103725
105247
|
}
|
|
103726
105248
|
function rsyncListArgs(spec, remotePath) {
|
|
103727
|
-
const args = ["--list-only"];
|
|
105249
|
+
const args = ["--list-only", "--8-bit-output"];
|
|
103728
105250
|
if (spec.rsh)
|
|
103729
105251
|
args.push("-e", spec.rsh);
|
|
103730
105252
|
args.push(rsyncTargetArg(spec, remotePath));
|
|
@@ -105079,8 +106601,8 @@ function getBaseVersion() {
|
|
|
105079
106601
|
if (cachedBase !== undefined)
|
|
105080
106602
|
return cachedBase;
|
|
105081
106603
|
let base = null;
|
|
105082
|
-
if ("0.
|
|
105083
|
-
base = "0.
|
|
106604
|
+
if ("0.14.0") {
|
|
106605
|
+
base = "0.14.0";
|
|
105084
106606
|
}
|
|
105085
106607
|
if (!base && config.isProd) {
|
|
105086
106608
|
base = readInstallMeta()?.cliVersion ?? null;
|
|
@@ -106878,6 +108400,18 @@ function normalizeSiteSettingsInput(body) {
|
|
|
106878
108400
|
}
|
|
106879
108401
|
updates.enableTelegramNotificationPush = body.enableTelegramNotificationPush;
|
|
106880
108402
|
}
|
|
108403
|
+
if (body.enableWeixinBellPush !== undefined) {
|
|
108404
|
+
if (typeof body.enableWeixinBellPush !== "boolean") {
|
|
108405
|
+
throw new Error(t2("apiError.invalidRequest"));
|
|
108406
|
+
}
|
|
108407
|
+
updates.enableWeixinBellPush = body.enableWeixinBellPush;
|
|
108408
|
+
}
|
|
108409
|
+
if (body.enableWeixinNotificationPush !== undefined) {
|
|
108410
|
+
if (typeof body.enableWeixinNotificationPush !== "boolean") {
|
|
108411
|
+
throw new Error(t2("apiError.invalidRequest"));
|
|
108412
|
+
}
|
|
108413
|
+
updates.enableWeixinNotificationPush = body.enableWeixinNotificationPush;
|
|
108414
|
+
}
|
|
106881
108415
|
if (body.sshReconnectMaxRetries !== undefined) {
|
|
106882
108416
|
const value = Math.floor(Number(body.sshReconnectMaxRetries));
|
|
106883
108417
|
if (Number.isNaN(value) || value < 0 || value > 20) {
|
|
@@ -106964,6 +108498,39 @@ function handleApiRequest(req, _server) {
|
|
|
106964
108498
|
if (path.match(/^\/api\/settings\/telegram\/bots\/[^/]+\/chats\/[^/]+$/) && req.method === "DELETE") {
|
|
106965
108499
|
return handleDeleteTelegramChat(path.split("/")[5], decodeURIComponent(path.split("/")[7]));
|
|
106966
108500
|
}
|
|
108501
|
+
if (path === "/api/settings/weixin/accounts" && req.method === "GET") {
|
|
108502
|
+
return handleGetWeixinAccounts();
|
|
108503
|
+
}
|
|
108504
|
+
if (path === "/api/settings/weixin/accounts" && req.method === "POST") {
|
|
108505
|
+
return handleCreateWeixinAccount(req);
|
|
108506
|
+
}
|
|
108507
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+$/) && req.method === "PATCH") {
|
|
108508
|
+
return handleUpdateWeixinAccount(req, path.split("/")[5]);
|
|
108509
|
+
}
|
|
108510
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+$/) && req.method === "DELETE") {
|
|
108511
|
+
return handleDeleteWeixinAccount(path.split("/")[5]);
|
|
108512
|
+
}
|
|
108513
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+\/login\/start$/) && req.method === "POST") {
|
|
108514
|
+
return handleStartWeixinLogin(path.split("/")[5]);
|
|
108515
|
+
}
|
|
108516
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+\/login\/status$/) && req.method === "GET") {
|
|
108517
|
+
return handleGetWeixinLoginStatus(path.split("/")[5]);
|
|
108518
|
+
}
|
|
108519
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+\/test$/) && req.method === "POST") {
|
|
108520
|
+
return handleTestWeixinAccount(path.split("/")[5]);
|
|
108521
|
+
}
|
|
108522
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+\/users$/) && req.method === "GET") {
|
|
108523
|
+
return handleListWeixinUsers(path.split("/")[5]);
|
|
108524
|
+
}
|
|
108525
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+\/users\/[^/]+\/approve$/) && req.method === "POST") {
|
|
108526
|
+
return handleApproveWeixinUser(path.split("/")[5], decodeURIComponent(path.split("/")[7]));
|
|
108527
|
+
}
|
|
108528
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+\/users\/[^/]+\/test$/) && req.method === "POST") {
|
|
108529
|
+
return handleTestWeixinUser(path.split("/")[5], decodeURIComponent(path.split("/")[7]));
|
|
108530
|
+
}
|
|
108531
|
+
if (path.match(/^\/api\/settings\/weixin\/accounts\/[^/]+\/users\/[^/]+$/) && req.method === "DELETE") {
|
|
108532
|
+
return handleDeleteWeixinUser(path.split("/")[5], decodeURIComponent(path.split("/")[7]));
|
|
108533
|
+
}
|
|
106967
108534
|
if (path.startsWith("/api/llm/")) {
|
|
106968
108535
|
const llmResponse = handleLlmApiRequest(req, path);
|
|
106969
108536
|
if (llmResponse) {
|
|
@@ -107054,6 +108621,7 @@ async function handleCreateDevice(req) {
|
|
|
107054
108621
|
username: body.username,
|
|
107055
108622
|
sshConfigRef: body.sshConfigRef,
|
|
107056
108623
|
session: body.session ?? "tmex",
|
|
108624
|
+
defaultWorkingDir: body.defaultWorkingDir?.trim() || undefined,
|
|
107057
108625
|
authMode: body.authMode,
|
|
107058
108626
|
passwordEnc: body.password ? await encrypt(body.password) : undefined,
|
|
107059
108627
|
privateKeyEnc: body.privateKey ? await encrypt(body.privateKey) : undefined,
|
|
@@ -107085,6 +108653,8 @@ async function handleUpdateDevice(req, id) {
|
|
|
107085
108653
|
updates.sshConfigRef = body.sshConfigRef;
|
|
107086
108654
|
if (body.session !== undefined)
|
|
107087
108655
|
updates.session = body.session;
|
|
108656
|
+
if (body.defaultWorkingDir !== undefined)
|
|
108657
|
+
updates.defaultWorkingDir = body.defaultWorkingDir.trim() || undefined;
|
|
107088
108658
|
if (body.authMode !== undefined)
|
|
107089
108659
|
updates.authMode = body.authMode;
|
|
107090
108660
|
if (body.password !== undefined)
|
|
@@ -107097,6 +108667,8 @@ async function handleUpdateDevice(req, id) {
|
|
|
107097
108667
|
updateDevice(id, updates);
|
|
107098
108668
|
if (shouldReconnectPushSupervisor(existing, updates)) {
|
|
107099
108669
|
await pushSupervisor.reconnect(id);
|
|
108670
|
+
} else if (updates.defaultWorkingDir !== undefined && updates.defaultWorkingDir !== existing.defaultWorkingDir) {
|
|
108671
|
+
pushSupervisor.updateDefaultWorkingDir(id, updates.defaultWorkingDir);
|
|
107100
108672
|
}
|
|
107101
108673
|
const device = getDeviceById(id);
|
|
107102
108674
|
return json9({ device });
|
|
@@ -107266,6 +108838,152 @@ async function handleTestTelegramChat(botId, chatId) {
|
|
|
107266
108838
|
}));
|
|
107267
108839
|
return json9({ success: true });
|
|
107268
108840
|
}
|
|
108841
|
+
async function handleGetWeixinAccounts() {
|
|
108842
|
+
const accounts = getWeixinAccountsWithStats();
|
|
108843
|
+
return json9({ accounts });
|
|
108844
|
+
}
|
|
108845
|
+
async function handleCreateWeixinAccount(req) {
|
|
108846
|
+
const body = await req.json();
|
|
108847
|
+
if (!body.name?.trim()) {
|
|
108848
|
+
return json9({ error: t2("weixin.accountNameRequired") }, 400);
|
|
108849
|
+
}
|
|
108850
|
+
const now2 = new Date().toISOString();
|
|
108851
|
+
const id = v4_default();
|
|
108852
|
+
createWeixinAccount({
|
|
108853
|
+
id,
|
|
108854
|
+
name: body.name.trim(),
|
|
108855
|
+
enabled: body.enabled ?? true,
|
|
108856
|
+
allowAuthRequests: body.allowAuthRequests ?? true,
|
|
108857
|
+
loggedIn: false,
|
|
108858
|
+
weixinUin: null,
|
|
108859
|
+
botTokenEnc: null,
|
|
108860
|
+
baseUrl: null,
|
|
108861
|
+
syncBuf: null,
|
|
108862
|
+
createdAt: now2,
|
|
108863
|
+
updatedAt: now2
|
|
108864
|
+
});
|
|
108865
|
+
return json9({ success: true, accountId: id }, 201);
|
|
108866
|
+
}
|
|
108867
|
+
async function handleUpdateWeixinAccount(req, accountId) {
|
|
108868
|
+
const existing = getWeixinAccountById(accountId);
|
|
108869
|
+
if (!existing) {
|
|
108870
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108871
|
+
}
|
|
108872
|
+
const body = await req.json();
|
|
108873
|
+
const updates = {};
|
|
108874
|
+
if (body.name !== undefined) {
|
|
108875
|
+
const value = body.name.trim();
|
|
108876
|
+
if (!value) {
|
|
108877
|
+
return json9({ error: t2("weixin.accountNameRequired") }, 400);
|
|
108878
|
+
}
|
|
108879
|
+
updates.name = value;
|
|
108880
|
+
}
|
|
108881
|
+
if (body.enabled !== undefined) {
|
|
108882
|
+
updates.enabled = body.enabled;
|
|
108883
|
+
}
|
|
108884
|
+
if (body.allowAuthRequests !== undefined) {
|
|
108885
|
+
updates.allowAuthRequests = body.allowAuthRequests;
|
|
108886
|
+
}
|
|
108887
|
+
updateWeixinAccount(accountId, updates);
|
|
108888
|
+
await weixinService.refresh();
|
|
108889
|
+
return json9({ success: true });
|
|
108890
|
+
}
|
|
108891
|
+
async function handleDeleteWeixinAccount(accountId) {
|
|
108892
|
+
const existing = getWeixinAccountById(accountId);
|
|
108893
|
+
if (!existing) {
|
|
108894
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108895
|
+
}
|
|
108896
|
+
deleteWeixinAccount(accountId);
|
|
108897
|
+
await weixinService.refresh();
|
|
108898
|
+
return json9({ success: true });
|
|
108899
|
+
}
|
|
108900
|
+
async function handleStartWeixinLogin(accountId) {
|
|
108901
|
+
const existing = getWeixinAccountById(accountId);
|
|
108902
|
+
if (!existing) {
|
|
108903
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108904
|
+
}
|
|
108905
|
+
try {
|
|
108906
|
+
const result = await weixinService.startLogin(accountId);
|
|
108907
|
+
return json9(result);
|
|
108908
|
+
} catch (err) {
|
|
108909
|
+
return json9({ error: err instanceof Error ? err.message : t2("weixin.loginFailed") }, 502);
|
|
108910
|
+
}
|
|
108911
|
+
}
|
|
108912
|
+
async function handleGetWeixinLoginStatus(accountId) {
|
|
108913
|
+
const existing = getWeixinAccountById(accountId);
|
|
108914
|
+
if (!existing) {
|
|
108915
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108916
|
+
}
|
|
108917
|
+
return json9(weixinService.getLoginStatus(accountId));
|
|
108918
|
+
}
|
|
108919
|
+
async function handleListWeixinUsers(accountId) {
|
|
108920
|
+
const existing = getWeixinAccountById(accountId);
|
|
108921
|
+
if (!existing) {
|
|
108922
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108923
|
+
}
|
|
108924
|
+
const users = listWeixinUsersByAccount(accountId);
|
|
108925
|
+
return json9({ users });
|
|
108926
|
+
}
|
|
108927
|
+
async function handleApproveWeixinUser(accountId, userId) {
|
|
108928
|
+
const existing = getWeixinAccountById(accountId);
|
|
108929
|
+
if (!existing) {
|
|
108930
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108931
|
+
}
|
|
108932
|
+
const user = approveWeixinUser(accountId, userId);
|
|
108933
|
+
if (!user) {
|
|
108934
|
+
return json9({ error: t2("weixin.userNotFound") }, 404);
|
|
108935
|
+
}
|
|
108936
|
+
const settings = getSiteSettings();
|
|
108937
|
+
try {
|
|
108938
|
+
await weixinService.sendTestMessage(accountId, userId, t2("weixin.approveMessageTemplate", {
|
|
108939
|
+
accountName: existing.name,
|
|
108940
|
+
time: new Date().toLocaleString(toBCP47(settings.language))
|
|
108941
|
+
}));
|
|
108942
|
+
} catch (err) {
|
|
108943
|
+
console.error("[weixin] approve ack failed:", err);
|
|
108944
|
+
}
|
|
108945
|
+
return json9({ user });
|
|
108946
|
+
}
|
|
108947
|
+
async function handleTestWeixinUser(accountId, userId) {
|
|
108948
|
+
const existing = getWeixinAccountById(accountId);
|
|
108949
|
+
if (!existing) {
|
|
108950
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108951
|
+
}
|
|
108952
|
+
const settings = getSiteSettings();
|
|
108953
|
+
try {
|
|
108954
|
+
await weixinService.sendTestMessage(accountId, userId, t2("weixin.testMessageTemplate", {
|
|
108955
|
+
siteName: settings.siteName,
|
|
108956
|
+
time: new Date().toLocaleString(toBCP47(settings.language))
|
|
108957
|
+
}));
|
|
108958
|
+
} catch (err) {
|
|
108959
|
+
return json9({ error: err instanceof Error ? err.message : t2("weixin.testMessageFailed") }, 400);
|
|
108960
|
+
}
|
|
108961
|
+
return json9({ success: true });
|
|
108962
|
+
}
|
|
108963
|
+
async function handleTestWeixinAccount(accountId) {
|
|
108964
|
+
const existing = getWeixinAccountById(accountId);
|
|
108965
|
+
if (!existing) {
|
|
108966
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108967
|
+
}
|
|
108968
|
+
const settings = getSiteSettings();
|
|
108969
|
+
try {
|
|
108970
|
+
await weixinService.sendTestMessageToBoundUser(accountId, t2("weixin.testMessageTemplate", {
|
|
108971
|
+
siteName: settings.siteName,
|
|
108972
|
+
time: new Date().toLocaleString(toBCP47(settings.language))
|
|
108973
|
+
}));
|
|
108974
|
+
} catch (err) {
|
|
108975
|
+
return json9({ error: err instanceof Error ? err.message : t2("weixin.testMessageFailed") }, 400);
|
|
108976
|
+
}
|
|
108977
|
+
return json9({ success: true });
|
|
108978
|
+
}
|
|
108979
|
+
async function handleDeleteWeixinUser(accountId, userId) {
|
|
108980
|
+
const existing = getWeixinAccountById(accountId);
|
|
108981
|
+
if (!existing) {
|
|
108982
|
+
return json9({ error: t2("weixin.accountNotFound") }, 404);
|
|
108983
|
+
}
|
|
108984
|
+
deleteWeixinUser(accountId, userId);
|
|
108985
|
+
return json9({ success: true });
|
|
108986
|
+
}
|
|
107269
108987
|
async function handleGetWebhooks() {
|
|
107270
108988
|
const webhooks = getAllWebhookEndpoints();
|
|
107271
108989
|
return json9({ webhooks });
|
|
@@ -108080,6 +109798,9 @@ class WebSocketServer {
|
|
|
108080
109798
|
onTerminalHistory: (paneId, data, alternateScreen) => {
|
|
108081
109799
|
this.broadcastTerminalHistory(deviceId, paneId, data, alternateScreen);
|
|
108082
109800
|
},
|
|
109801
|
+
onClipboardWrite: (paneId, text3) => {
|
|
109802
|
+
this.broadcastClipboardWrite(deviceId, paneId, text3);
|
|
109803
|
+
},
|
|
108083
109804
|
onSnapshot: (payload) => {
|
|
108084
109805
|
this.broadcastStateSnapshot(deviceId, payload);
|
|
108085
109806
|
},
|
|
@@ -108207,6 +109928,10 @@ class WebSocketServer {
|
|
|
108207
109928
|
this.connections.delete(deviceId);
|
|
108208
109929
|
}
|
|
108209
109930
|
}
|
|
109931
|
+
updateDefaultWorkingDir(deviceId, dir2) {
|
|
109932
|
+
const entry = this.connections.get(deviceId);
|
|
109933
|
+
entry?.runtime.updateDefaultWorkingDir(dir2);
|
|
109934
|
+
}
|
|
108210
109935
|
closeAll() {
|
|
108211
109936
|
for (const [deviceId, entry] of this.connections) {
|
|
108212
109937
|
this.releaseConnectionEntry(deviceId, entry);
|
|
@@ -108764,6 +110489,22 @@ class WebSocketServer {
|
|
|
108764
110489
|
this.sendChunked(client, exports_ws_borsh.KIND_TERM_OUTPUT, payloadBytes);
|
|
108765
110490
|
}
|
|
108766
110491
|
}
|
|
110492
|
+
broadcastClipboardWrite(deviceId, paneId, text3) {
|
|
110493
|
+
const entry = this.connections.get(deviceId);
|
|
110494
|
+
if (!entry)
|
|
110495
|
+
return;
|
|
110496
|
+
const payloadBytes = exports_ws_borsh.encodePayload(exports_ws_borsh.schema.ClipboardWriteSchema, {
|
|
110497
|
+
deviceId,
|
|
110498
|
+
paneId,
|
|
110499
|
+
text: text3
|
|
110500
|
+
});
|
|
110501
|
+
for (const client of entry.clients) {
|
|
110502
|
+
if (client.data.borshState.selectedPanes[deviceId] !== paneId) {
|
|
110503
|
+
continue;
|
|
110504
|
+
}
|
|
110505
|
+
this.sendEnvelope(client, exports_ws_borsh.KIND_CLIPBOARD_WRITE, payloadBytes);
|
|
110506
|
+
}
|
|
110507
|
+
}
|
|
108767
110508
|
broadcastTerminalHistory(deviceId, paneId, data, alternateScreen) {
|
|
108768
110509
|
const entry = this.connections.get(deviceId);
|
|
108769
110510
|
if (!entry)
|
|
@@ -108899,12 +110640,14 @@ async function createGatewayRuntime(options = {}) {
|
|
|
108899
110640
|
wsServer.broadcastDeviceError(deviceId, payload);
|
|
108900
110641
|
});
|
|
108901
110642
|
await telegramService.refresh();
|
|
110643
|
+
await weixinService.refresh();
|
|
108902
110644
|
await pushSupervisor.start();
|
|
108903
110645
|
await agentSupervisor.start();
|
|
108904
110646
|
await watchService.start();
|
|
108905
110647
|
try {
|
|
108906
110648
|
const settings = getSiteSettings();
|
|
108907
110649
|
await telegramService.sendGatewayOnlineMessage(settings.siteName);
|
|
110650
|
+
await weixinService.sendGatewayOnlineMessage(settings.siteName);
|
|
108908
110651
|
} catch (err) {
|
|
108909
110652
|
console.error("[gateway] failed to push startup message:", err);
|
|
108910
110653
|
}
|
|
@@ -108949,6 +110692,7 @@ async function createGatewayRuntime(options = {}) {
|
|
|
108949
110692
|
await pushSupervisor.stopAll();
|
|
108950
110693
|
await tmuxRuntimeRegistry.shutdownAll();
|
|
108951
110694
|
await telegramService.stopAll();
|
|
110695
|
+
await weixinService.stopAll();
|
|
108952
110696
|
}
|
|
108953
110697
|
};
|
|
108954
110698
|
}
|
|
@@ -108959,8 +110703,8 @@ var MESSAGES = {
|
|
|
108959
110703
|
"cli.help": `tmex CLI
|
|
108960
110704
|
|
|
108961
110705
|
Usage:
|
|
108962
|
-
tmex init [--no-interactive --install-dir <path> --host <host> --port <port> --db-path <path> --autostart <true|false> --bun-path <path>]
|
|
108963
|
-
tmex doctor [--install-dir <path>] [--json] [--bun-path <path>]
|
|
110706
|
+
tmex init [--no-interactive --install-dir <path> --host <host> --port <port> --db-path <path> --autostart <true|false> --bun-path <path> --install-deps --skip-dep-check]
|
|
110707
|
+
tmex doctor [--install-dir <path>] [--json] [--bun-path <path>] [--fix]
|
|
108964
110708
|
tmex upgrade [--version <version>] [--install-dir <path>] [--bun-path <path>]
|
|
108965
110709
|
tmex uninstall [--install-dir <path>] [--yes] [--purge]
|
|
108966
110710
|
|
|
@@ -109004,6 +110748,7 @@ Global flags:
|
|
|
109004
110748
|
"init.prompt.serviceName": "Service name (service-name)",
|
|
109005
110749
|
"init.prompt.dirExistsConfirm": "Directory {{installDir}} already exists. Continue (will not delete existing config/db)?",
|
|
109006
110750
|
"init.error.installDirNotEmpty": "Install directory is not empty: {{installDir}}. Use --force to overwrite.",
|
|
110751
|
+
"init.error.noServiceManager": "No supported service manager found (platform: {{platform}}). tmex requires systemd (Linux) or launchd (macOS).",
|
|
109007
110752
|
"init.warning.noServiceManager": "Service manager is not supported on platform {{platform}}. Files are deployed but autostart is not configured.",
|
|
109008
110753
|
"init.done": "Initialization completed.",
|
|
109009
110754
|
"init.summary.installDir": "Install dir",
|
|
@@ -109017,8 +110762,12 @@ Global flags:
|
|
|
109017
110762
|
"doctor.platform.unsupported": "Platform {{platform}} is not officially supported (only macOS and common Linux distros are guaranteed).",
|
|
109018
110763
|
"doctor.bun.ok": "Bun installed: {{version}}",
|
|
109019
110764
|
"doctor.bun.fail": "Bun check failed: {{reason}}",
|
|
109020
|
-
"doctor.tmux.ok": "tmux installed",
|
|
109021
|
-
"doctor.tmux.fail": "tmux not found (tmex requires tmux).",
|
|
110765
|
+
"doctor.tmux.ok": "tmux installed: {{version}}",
|
|
110766
|
+
"doctor.tmux.fail": "tmux not found (tmex requires tmux >= 3.0).",
|
|
110767
|
+
"doctor.tmux.versionLow": "tmux version too low: {{version}} (requires >= 3.0)",
|
|
110768
|
+
"doctor.fix.header": "Attempting to fix issues...",
|
|
110769
|
+
"doctor.fix.skip": "Skipping unfixable item: {{id}}",
|
|
110770
|
+
"doctor.fix.hint": 'Run "tmex doctor --fix" to attempt automatic installation.',
|
|
109022
110771
|
"doctor.ssh.ok": "ssh installed",
|
|
109023
110772
|
"doctor.ssh.missing": "ssh not found; SSH devices will not work.",
|
|
109024
110773
|
"doctor.installDir.exists": "Install directory exists: {{installDir}}",
|
|
@@ -109049,6 +110798,19 @@ Global flags:
|
|
|
109049
110798
|
"uninstall.done": "Uninstall completed.",
|
|
109050
110799
|
"uninstall.summary.installDir": "Install dir",
|
|
109051
110800
|
"uninstall.summary.serviceName": "Service name",
|
|
110801
|
+
"tmux.notFound": "tmux not found. tmex requires tmux >= 3.0 to operate.",
|
|
110802
|
+
"tmux.versionTooLow": "tmux version too low: current {{version}}, required >= 3.0",
|
|
110803
|
+
"deps.install.confirm": "Install {{dep}} now?",
|
|
110804
|
+
"deps.install.running": "Installing {{dep}}...",
|
|
110805
|
+
"deps.install.success": "{{dep}} installed successfully.",
|
|
110806
|
+
"deps.install.failed": "Failed to install {{dep}}.",
|
|
110807
|
+
"deps.install.manual": "Please install manually and retry.",
|
|
110808
|
+
"deps.install.sudoRequired": "This operation requires sudo.",
|
|
110809
|
+
"deps.install.sudoUnavailable": "sudo is not available. Please run as root or install sudo.",
|
|
110810
|
+
"deps.install.nonInteractive": "Missing dependency: {{dep}}. Use --install-deps to install automatically.",
|
|
110811
|
+
"deps.install.hint": "Suggested install command: {{command}}",
|
|
110812
|
+
"deps.install.brewMissing": "Homebrew not found. Install Homebrew first: https://brew.sh",
|
|
110813
|
+
"deps.install.unknownDistro": "Unable to detect Linux distribution. Please install {{dep}} manually.",
|
|
109052
110814
|
"runtime.restartRequested": "Restart requested; exiting for service manager restart.",
|
|
109053
110815
|
"runtime.started": "Service started on {{url}}",
|
|
109054
110816
|
"runtime.frontendMissing": "Frontend assets not found.",
|
|
@@ -109060,8 +110822,8 @@ Global flags:
|
|
|
109060
110822
|
"cli.help": `tmex CLI
|
|
109061
110823
|
|
|
109062
110824
|
\u7528\u6CD5\uFF1A
|
|
109063
|
-
tmex init [--no-interactive --install-dir <path> --host <host> --port <port> --db-path <path> --autostart <true|false> --bun-path <path>]
|
|
109064
|
-
tmex doctor [--install-dir <path>] [--json] [--bun-path <path>]
|
|
110825
|
+
tmex init [--no-interactive --install-dir <path> --host <host> --port <port> --db-path <path> --autostart <true|false> --bun-path <path> --install-deps --skip-dep-check]
|
|
110826
|
+
tmex doctor [--install-dir <path>] [--json] [--bun-path <path>] [--fix]
|
|
109065
110827
|
tmex upgrade [--version <version>] [--install-dir <path>] [--bun-path <path>]
|
|
109066
110828
|
tmex uninstall [--install-dir <path>] [--yes] [--purge]
|
|
109067
110829
|
|
|
@@ -109105,6 +110867,7 @@ Global flags:
|
|
|
109105
110867
|
"init.prompt.serviceName": "\u670D\u52A1\u540D\u79F0\uFF08service-name\uFF09",
|
|
109106
110868
|
"init.prompt.dirExistsConfirm": "\u76EE\u5F55 {{installDir}} \u5DF2\u5B58\u5728\uFF0C\u662F\u5426\u7EE7\u7EED\uFF08\u4E0D\u4F1A\u5220\u9664\u73B0\u6709\u914D\u7F6E\u4E0E\u6570\u636E\u5E93\uFF09\uFF1F",
|
|
109107
110869
|
"init.error.installDirNotEmpty": "\u5B89\u88C5\u76EE\u5F55\u5DF2\u5B58\u5728\u4E14\u975E\u7A7A\uFF1A{{installDir}}\u3002\u5982\u9700\u8986\u76D6\u8BF7\u52A0 --force",
|
|
110870
|
+
"init.error.noServiceManager": "\u672A\u68C0\u6D4B\u5230\u53EF\u7528\u7684\u670D\u52A1\u7BA1\u7406\u5668\uFF08\u5E73\u53F0\uFF1A{{platform}}\uFF09\u3002tmex \u9700\u8981 systemd\uFF08Linux\uFF09\u6216 launchd\uFF08macOS\uFF09\u3002",
|
|
109108
110871
|
"init.warning.noServiceManager": "\u5F53\u524D\u5E73\u53F0 {{platform}} \u672A\u5B9E\u73B0\u81EA\u52A8\u670D\u52A1\u5B89\u88C5\uFF0C\u5DF2\u5B8C\u6210\u6587\u4EF6\u90E8\u7F72\u3002",
|
|
109109
110872
|
"init.done": "\u521D\u59CB\u5316\u5B8C\u6210\u3002",
|
|
109110
110873
|
"init.summary.installDir": "\u5B89\u88C5\u76EE\u5F55",
|
|
@@ -109118,8 +110881,12 @@ Global flags:
|
|
|
109118
110881
|
"doctor.platform.unsupported": "\u5F53\u524D\u5E73\u53F0 {{platform}} \u975E\u5B98\u65B9\u652F\u6301\u8303\u56F4\uFF08\u4EC5\u4FDD\u8BC1 macOS \u4E0E\u5E38\u89C1 Linux \u53D1\u884C\u7248\uFF09\u3002",
|
|
109119
110882
|
"doctor.bun.ok": "Bun \u5DF2\u5B89\u88C5\uFF1A{{version}}",
|
|
109120
110883
|
"doctor.bun.fail": "Bun \u68C0\u67E5\u5931\u8D25\uFF1A{{reason}}",
|
|
109121
|
-
"doctor.tmux.ok": "tmux \u5DF2\u5B89\u88C5",
|
|
109122
|
-
"doctor.tmux.fail": "\u672A\u68C0\u6D4B\u5230 tmux\uFF08tmex \u9700\u8981 tmux \u624D\u80FD\u5DE5\u4F5C\uFF09\u3002",
|
|
110884
|
+
"doctor.tmux.ok": "tmux \u5DF2\u5B89\u88C5\uFF1A{{version}}",
|
|
110885
|
+
"doctor.tmux.fail": "\u672A\u68C0\u6D4B\u5230 tmux\uFF08tmex \u9700\u8981 tmux >= 3.0 \u624D\u80FD\u5DE5\u4F5C\uFF09\u3002",
|
|
110886
|
+
"doctor.tmux.versionLow": "tmux \u7248\u672C\u8FC7\u4F4E\uFF1A{{version}}\uFF08\u8981\u6C42 >= 3.0\uFF09",
|
|
110887
|
+
"doctor.fix.header": "\u6B63\u5728\u5C1D\u8BD5\u4FEE\u590D\u95EE\u9898...",
|
|
110888
|
+
"doctor.fix.skip": "\u8DF3\u8FC7\u65E0\u6CD5\u81EA\u52A8\u4FEE\u590D\u7684\u9879\u76EE\uFF1A{{id}}",
|
|
110889
|
+
"doctor.fix.hint": '\u8FD0\u884C "tmex doctor --fix" \u5C1D\u8BD5\u81EA\u52A8\u5B89\u88C5\u7F3A\u5931\u7684\u4F9D\u8D56\u3002',
|
|
109123
110890
|
"doctor.ssh.ok": "ssh \u5DF2\u5B89\u88C5",
|
|
109124
110891
|
"doctor.ssh.missing": "\u672A\u68C0\u6D4B\u5230 ssh\uFF0C\u8FDC\u7A0B\u8BBE\u5907\u5C06\u4E0D\u53EF\u7528\u3002",
|
|
109125
110892
|
"doctor.installDir.exists": "\u5B89\u88C5\u76EE\u5F55\u5B58\u5728\uFF1A{{installDir}}",
|
|
@@ -109150,6 +110917,19 @@ Global flags:
|
|
|
109150
110917
|
"uninstall.done": "\u5378\u8F7D\u5B8C\u6210\u3002",
|
|
109151
110918
|
"uninstall.summary.installDir": "\u5B89\u88C5\u76EE\u5F55",
|
|
109152
110919
|
"uninstall.summary.serviceName": "\u670D\u52A1\u540D\u79F0",
|
|
110920
|
+
"tmux.notFound": "\u672A\u68C0\u6D4B\u5230 tmux\u3002tmex \u9700\u8981 tmux >= 3.0 \u624D\u80FD\u5DE5\u4F5C\u3002",
|
|
110921
|
+
"tmux.versionTooLow": "tmux \u7248\u672C\u8FC7\u4F4E\uFF1A\u5F53\u524D {{version}}\uFF0C\u8981\u6C42 >= 3.0",
|
|
110922
|
+
"deps.install.confirm": "\u662F\u5426\u73B0\u5728\u5B89\u88C5 {{dep}}\uFF1F",
|
|
110923
|
+
"deps.install.running": "\u6B63\u5728\u5B89\u88C5 {{dep}}...",
|
|
110924
|
+
"deps.install.success": "{{dep}} \u5B89\u88C5\u6210\u529F\u3002",
|
|
110925
|
+
"deps.install.failed": "\u5B89\u88C5 {{dep}} \u5931\u8D25\u3002",
|
|
110926
|
+
"deps.install.manual": "\u8BF7\u624B\u52A8\u5B89\u88C5\u540E\u91CD\u8BD5\u3002",
|
|
110927
|
+
"deps.install.sudoRequired": "\u6B64\u64CD\u4F5C\u9700\u8981 sudo \u6743\u9650\u3002",
|
|
110928
|
+
"deps.install.sudoUnavailable": "sudo \u4E0D\u53EF\u7528\uFF0C\u8BF7\u4EE5 root \u8EAB\u4EFD\u6267\u884C\u6216\u5B89\u88C5 sudo\u3002",
|
|
110929
|
+
"deps.install.nonInteractive": "\u7F3A\u5C11\u4F9D\u8D56\uFF1A{{dep}}\u3002\u4F7F\u7528 --install-deps \u81EA\u52A8\u5B89\u88C5\u3002",
|
|
110930
|
+
"deps.install.hint": "\u5EFA\u8BAE\u5B89\u88C5\u547D\u4EE4\uFF1A{{command}}",
|
|
110931
|
+
"deps.install.brewMissing": "\u672A\u68C0\u6D4B\u5230 Homebrew\uFF0C\u8BF7\u5148\u5B89\u88C5 Homebrew\uFF1Ahttps://brew.sh",
|
|
110932
|
+
"deps.install.unknownDistro": "\u65E0\u6CD5\u68C0\u6D4B Linux \u53D1\u884C\u7248\uFF0C\u8BF7\u624B\u52A8\u5B89\u88C5 {{dep}}\u3002",
|
|
109153
110933
|
"runtime.restartRequested": "\u6536\u5230\u91CD\u542F\u8BF7\u6C42\uFF0C\u9000\u51FA\u5E76\u7B49\u5F85\u670D\u52A1\u7BA1\u7406\u5668\u62C9\u8D77\u3002",
|
|
109154
110934
|
"runtime.started": "\u670D\u52A1\u5DF2\u542F\u52A8\uFF1A{{url}}",
|
|
109155
110935
|
"runtime.frontendMissing": "\u672A\u627E\u5230\u524D\u7AEF\u9759\u6001\u8D44\u6E90\u3002",
|