tmex-cli 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/runtime/server.js +736 -186
- package/package.json +1 -1
- package/resources/fe-dist/assets/DevicePage-CKaPUo7L.js +26 -0
- package/resources/fe-dist/assets/DevicePage-CKaPUo7L.js.map +1 -0
- package/resources/fe-dist/assets/DevicesPage-FqU-Dxhu.js +17 -0
- package/resources/fe-dist/assets/{DevicesPage-CtNzaW_c.js.map → DevicesPage-FqU-Dxhu.js.map} +1 -1
- package/resources/fe-dist/assets/SettingsPage-BfkOW0fc.js +17 -0
- package/resources/fe-dist/assets/SettingsPage-BfkOW0fc.js.map +1 -0
- package/resources/fe-dist/assets/index-EgHfq93I.js +449 -0
- package/resources/fe-dist/assets/index-EgHfq93I.js.map +1 -0
- package/resources/fe-dist/assets/index-Ytlj3p_q.css +1 -0
- package/resources/fe-dist/assets/select-CNlE6MiW.js +17 -0
- package/resources/fe-dist/assets/{select-BNsiC9zT.js.map → select-CNlE6MiW.js.map} +1 -1
- package/resources/fe-dist/assets/switch-CxkzOIL6.js +12 -0
- package/resources/fe-dist/assets/{switch-CIU4AisU.js.map → switch-CxkzOIL6.js.map} +1 -1
- package/resources/fe-dist/assets/useValueChanged-CO2U5MoL.js +7 -0
- package/resources/fe-dist/assets/{useValueChanged-V23H0VpC.js.map → useValueChanged-CO2U5MoL.js.map} +1 -1
- package/resources/fe-dist/index.html +2 -2
- package/resources/gateway-drizzle/0002_broad_vengeance.sql +3 -0
- package/resources/gateway-drizzle/meta/0000_snapshot.json +17 -6
- package/resources/gateway-drizzle/meta/0001_snapshot.json +17 -6
- package/resources/gateway-drizzle/meta/0002_snapshot.json +535 -0
- package/resources/gateway-drizzle/meta/_journal.json +8 -1
- package/resources/fe-dist/assets/DevicePage-iSkEDEpS.js +0 -4570
- package/resources/fe-dist/assets/DevicePage-iSkEDEpS.js.map +0 -1
- package/resources/fe-dist/assets/DevicesPage-CtNzaW_c.js +0 -2143
- package/resources/fe-dist/assets/SettingsPage-D25_d6j9.js +0 -1144
- package/resources/fe-dist/assets/SettingsPage-D25_d6j9.js.map +0 -1
- package/resources/fe-dist/assets/index-CJaX5rlK.css +0 -4527
- package/resources/fe-dist/assets/index-dsVN7rgz.js +0 -200
- package/resources/fe-dist/assets/index-dsVN7rgz.js.map +0 -1
- package/resources/fe-dist/assets/select-BNsiC9zT.js +0 -2805
- package/resources/fe-dist/assets/switch-CIU4AisU.js +0 -234
- package/resources/fe-dist/assets/useValueChanged-V23H0VpC.js +0 -351
package/dist/runtime/server.js
CHANGED
|
@@ -20363,8 +20363,13 @@ var I18N_RESOURCES = {
|
|
|
20363
20363
|
title: "Device Management",
|
|
20364
20364
|
devices: "Devices",
|
|
20365
20365
|
addDevice: "Add Device",
|
|
20366
|
+
addDeviceDescription: "Fill in device details and choose a connection method",
|
|
20366
20367
|
addFirstDevice: "Add First Device",
|
|
20367
20368
|
editDevice: "Edit Device",
|
|
20369
|
+
editDeviceDescription: "Update device configuration",
|
|
20370
|
+
sectionBasic: "Basic Info",
|
|
20371
|
+
sectionConnection: "Connection",
|
|
20372
|
+
sectionAuth: "Authentication",
|
|
20368
20373
|
noDevices: "No Devices",
|
|
20369
20374
|
noDevicesDescription: "Add a local or SSH device to get started",
|
|
20370
20375
|
name: "Device Name",
|
|
@@ -20397,6 +20402,7 @@ var I18N_RESOURCES = {
|
|
|
20397
20402
|
disconnected: "Disconnected",
|
|
20398
20403
|
connecting: "Connecting...",
|
|
20399
20404
|
deleteConfirm: "Delete this device?",
|
|
20405
|
+
deleteDescription: 'Device "{{name}}" will be permanently removed. This action cannot be undone.',
|
|
20400
20406
|
deleteSuccess: "Device deleted",
|
|
20401
20407
|
createSuccess: "Device created",
|
|
20402
20408
|
updateSuccess: "Device updated",
|
|
@@ -20446,8 +20452,11 @@ var I18N_RESOURCES = {
|
|
|
20446
20452
|
siteUrl: "Site URL",
|
|
20447
20453
|
siteUrlPlaceholder: "http://localhost:3000",
|
|
20448
20454
|
bellThrottle: "Bell Throttle (seconds)",
|
|
20455
|
+
notificationThrottle: "Notification Throttle (seconds)",
|
|
20449
20456
|
enableBrowserBellToast: "Enable Browser Bell Toast",
|
|
20457
|
+
enableBrowserNotificationToast: "Enable Browser Notification Toast",
|
|
20450
20458
|
enableTelegramBellPush: "Enable Telegram Bell Push",
|
|
20459
|
+
enableTelegramNotificationPush: "Enable Telegram Notification Push",
|
|
20451
20460
|
sshReconnectRetries: "SSH Reconnect Retries",
|
|
20452
20461
|
sshReconnectDelay: "SSH Reconnect Delay (seconds)",
|
|
20453
20462
|
language: "Language",
|
|
@@ -20591,6 +20600,7 @@ Time: {{time}}`,
|
|
|
20591
20600
|
clickToJump: "Click to jump to corresponding pane",
|
|
20592
20601
|
eventType: {
|
|
20593
20602
|
terminal_bell: "\uD83D\uDD14 Terminal Bell",
|
|
20603
|
+
terminal_notification: "\uD83D\uDD14 Terminal Notification",
|
|
20594
20604
|
tmux_window_close: "\uD83E\uDE9F Window Closed",
|
|
20595
20605
|
tmux_pane_close: "\uD83D\uDCF1 Pane Closed",
|
|
20596
20606
|
device_tmux_missing: "\u26A0\uFE0F Tmux Missing",
|
|
@@ -20609,7 +20619,8 @@ Time: {{time}}`,
|
|
|
20609
20619
|
title: "\uD83D\uDD14 Bell from {{siteName}}: {{terminalTopbarLabel}}",
|
|
20610
20620
|
viewLink: "Click to view",
|
|
20611
20621
|
terminalTopbarLabel: "Window {{window}} \xB7 Pane {{pane}} @ {{device}}"
|
|
20612
|
-
}
|
|
20622
|
+
},
|
|
20623
|
+
telegramNotification: {}
|
|
20613
20624
|
},
|
|
20614
20625
|
sidebar: {
|
|
20615
20626
|
noWindows: "No windows",
|
|
@@ -20690,8 +20701,13 @@ Time: {{time}}`,
|
|
|
20690
20701
|
title: "\u8BBE\u5907\u7BA1\u7406",
|
|
20691
20702
|
devices: "\u8BBE\u5907",
|
|
20692
20703
|
addDevice: "\u6DFB\u52A0\u8BBE\u5907",
|
|
20704
|
+
addDeviceDescription: "\u586B\u5199\u8BBE\u5907\u4FE1\u606F\u5E76\u9009\u62E9\u8FDE\u63A5\u65B9\u5F0F",
|
|
20693
20705
|
addFirstDevice: "\u6DFB\u52A0\u7B2C\u4E00\u4E2A\u8BBE\u5907",
|
|
20694
20706
|
editDevice: "\u4FEE\u6539\u8BBE\u5907",
|
|
20707
|
+
editDeviceDescription: "\u66F4\u65B0\u8BBE\u5907\u914D\u7F6E",
|
|
20708
|
+
sectionBasic: "\u57FA\u672C\u4FE1\u606F",
|
|
20709
|
+
sectionConnection: "\u8FDE\u63A5\u4FE1\u606F",
|
|
20710
|
+
sectionAuth: "\u8BA4\u8BC1\u4FE1\u606F",
|
|
20695
20711
|
noDevices: "\u6682\u65E0\u8BBE\u5907",
|
|
20696
20712
|
noDevicesDescription: "\u6DFB\u52A0\u672C\u5730\u6216 SSH \u8BBE\u5907\u5F00\u59CB\u4F7F\u7528",
|
|
20697
20713
|
name: "\u8BBE\u5907\u540D\u79F0",
|
|
@@ -20724,6 +20740,7 @@ Time: {{time}}`,
|
|
|
20724
20740
|
disconnected: "\u5DF2\u65AD\u5F00",
|
|
20725
20741
|
connecting: "\u8FDE\u63A5\u4E2D...",
|
|
20726
20742
|
deleteConfirm: "\u5220\u9664\u6B64\u8BBE\u5907\uFF1F",
|
|
20743
|
+
deleteDescription: '\u8BBE\u5907 "{{name}}" \u5C06\u88AB\u6C38\u4E45\u79FB\u9664\uFF0C\u6B64\u64CD\u4F5C\u65E0\u6CD5\u64A4\u9500\u3002',
|
|
20727
20744
|
deleteSuccess: "\u8BBE\u5907\u5DF2\u5220\u9664",
|
|
20728
20745
|
createSuccess: "\u8BBE\u5907\u5DF2\u521B\u5EFA",
|
|
20729
20746
|
updateSuccess: "\u8BBE\u5907\u5DF2\u66F4\u65B0",
|
|
@@ -20773,8 +20790,11 @@ Time: {{time}}`,
|
|
|
20773
20790
|
siteUrl: "\u7AD9\u70B9\u8BBF\u95EE URL",
|
|
20774
20791
|
siteUrlPlaceholder: "http://localhost:3000",
|
|
20775
20792
|
bellThrottle: "Bell \u9891\u63A7\uFF08\u79D2\uFF09",
|
|
20793
|
+
notificationThrottle: "\u901A\u77E5\u9891\u63A7\uFF08\u79D2\uFF09",
|
|
20776
20794
|
enableBrowserBellToast: "\u5F00\u542F\u6D4F\u89C8\u5668 Bell Toast",
|
|
20795
|
+
enableBrowserNotificationToast: "\u5F00\u542F\u6D4F\u89C8\u5668\u901A\u77E5 Toast",
|
|
20777
20796
|
enableTelegramBellPush: "\u5F00\u542F Telegram Bell \u63A8\u9001",
|
|
20797
|
+
enableTelegramNotificationPush: "\u5F00\u542F Telegram \u901A\u77E5\u63A8\u9001",
|
|
20778
20798
|
sshReconnectRetries: "SSH \u91CD\u8FDE\u6B21\u6570",
|
|
20779
20799
|
sshReconnectDelay: "SSH \u91CD\u8FDE\u7B49\u5F85\uFF08\u79D2\uFF09",
|
|
20780
20800
|
language: "\u8BED\u8A00",
|
|
@@ -20918,6 +20938,7 @@ Bot\uFF1A{{botName}}
|
|
|
20918
20938
|
clickToJump: "\u70B9\u51FB\u8DF3\u8F6C\u5230\u5BF9\u5E94 Pane",
|
|
20919
20939
|
eventType: {
|
|
20920
20940
|
terminal_bell: "\uD83D\uDD14 \u7EC8\u7AEF Bell",
|
|
20941
|
+
terminal_notification: "\uD83D\uDD14 \u7EC8\u7AEF\u901A\u77E5",
|
|
20921
20942
|
tmux_window_close: "\uD83E\uDE9F \u7A97\u53E3\u5173\u95ED",
|
|
20922
20943
|
tmux_pane_close: "\uD83D\uDCF1 Pane \u5173\u95ED",
|
|
20923
20944
|
device_tmux_missing: "\u26A0\uFE0F Tmux \u4E0D\u53EF\u7528",
|
|
@@ -20936,7 +20957,8 @@ Bot\uFF1A{{botName}}
|
|
|
20936
20957
|
title: "\uD83D\uDD14 \u6765\u81EA {{siteName}} \u7684 Bell\uFF1A{{terminalTopbarLabel}}",
|
|
20937
20958
|
viewLink: "\u70B9\u51FB\u67E5\u770B",
|
|
20938
20959
|
terminalTopbarLabel: "\u7A97\u53E3 {{window}} \xB7 Pane {{pane}} @ {{device}}"
|
|
20939
|
-
}
|
|
20960
|
+
},
|
|
20961
|
+
telegramNotification: {}
|
|
20940
20962
|
},
|
|
20941
20963
|
sidebar: {
|
|
20942
20964
|
noWindows: "\u6682\u65E0\u7A97\u53E3",
|
|
@@ -21017,8 +21039,13 @@ Bot\uFF1A{{botName}}
|
|
|
21017
21039
|
title: "\u30C7\u30D0\u30A4\u30B9\u7BA1\u7406",
|
|
21018
21040
|
devices: "\u30C7\u30D0\u30A4\u30B9",
|
|
21019
21041
|
addDevice: "\u30C7\u30D0\u30A4\u30B9\u3092\u8FFD\u52A0",
|
|
21042
|
+
addDeviceDescription: "\u30C7\u30D0\u30A4\u30B9\u60C5\u5831\u3092\u5165\u529B\u3057\u3001\u63A5\u7D9A\u65B9\u6CD5\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
21020
21043
|
addFirstDevice: "\u6700\u521D\u306E\u30C7\u30D0\u30A4\u30B9\u3092\u8FFD\u52A0",
|
|
21021
21044
|
editDevice: "\u30C7\u30D0\u30A4\u30B9\u3092\u7DE8\u96C6",
|
|
21045
|
+
editDeviceDescription: "\u30C7\u30D0\u30A4\u30B9\u8A2D\u5B9A\u3092\u66F4\u65B0",
|
|
21046
|
+
sectionBasic: "\u57FA\u672C\u60C5\u5831",
|
|
21047
|
+
sectionConnection: "\u63A5\u7D9A\u60C5\u5831",
|
|
21048
|
+
sectionAuth: "\u8A8D\u8A3C\u60C5\u5831",
|
|
21022
21049
|
noDevices: "\u30C7\u30D0\u30A4\u30B9\u304C\u3042\u308A\u307E\u305B\u3093",
|
|
21023
21050
|
noDevicesDescription: "\u30ED\u30FC\u30AB\u30EB\u307E\u305F\u306F SSH \u30C7\u30D0\u30A4\u30B9\u3092\u8FFD\u52A0\u3057\u3066\u958B\u59CB",
|
|
21024
21051
|
name: "\u30C7\u30D0\u30A4\u30B9\u540D",
|
|
@@ -21051,6 +21078,7 @@ Bot\uFF1A{{botName}}
|
|
|
21051
21078
|
disconnected: "\u5207\u65AD\u6E08\u307F",
|
|
21052
21079
|
connecting: "\u63A5\u7D9A\u4E2D...",
|
|
21053
21080
|
deleteConfirm: "\u3053\u306E\u30C7\u30D0\u30A4\u30B9\u3092\u524A\u9664\u3057\u307E\u3059\u304B\uFF1F",
|
|
21081
|
+
deleteDescription: "\u30C7\u30D0\u30A4\u30B9\u300C{{name}}\u300D\u306F\u5B8C\u5168\u306B\u524A\u9664\u3055\u308C\u307E\u3059\u3002\u3053\u306E\u64CD\u4F5C\u306F\u53D6\u308A\u6D88\u305B\u307E\u305B\u3093\u3002",
|
|
21054
21082
|
deleteSuccess: "\u30C7\u30D0\u30A4\u30B9\u3092\u524A\u9664\u3057\u307E\u3057\u305F",
|
|
21055
21083
|
createSuccess: "\u30C7\u30D0\u30A4\u30B9\u3092\u4F5C\u6210\u3057\u307E\u3057\u305F",
|
|
21056
21084
|
updateSuccess: "\u30C7\u30D0\u30A4\u30B9\u3092\u66F4\u65B0\u3057\u307E\u3057\u305F",
|
|
@@ -21100,8 +21128,11 @@ Bot\uFF1A{{botName}}
|
|
|
21100
21128
|
siteUrl: "\u30B5\u30A4\u30C8 URL",
|
|
21101
21129
|
siteUrlPlaceholder: "http://localhost:3000",
|
|
21102
21130
|
bellThrottle: "\u30D9\u30EB\u5236\u9650\uFF08\u79D2\uFF09",
|
|
21131
|
+
notificationThrottle: "\u901A\u77E5\u5236\u9650\uFF08\u79D2\uFF09",
|
|
21103
21132
|
enableBrowserBellToast: "\u30D6\u30E9\u30A6\u30B6\u30D9\u30EB Toast \u3092\u6709\u52B9\u306B\u3059\u308B",
|
|
21133
|
+
enableBrowserNotificationToast: "\u30D6\u30E9\u30A6\u30B6\u901A\u77E5 Toast \u3092\u6709\u52B9\u306B\u3059\u308B",
|
|
21104
21134
|
enableTelegramBellPush: "Telegram \u30D9\u30EB\u30D7\u30C3\u30B7\u30E5\u3092\u6709\u52B9\u306B\u3059\u308B",
|
|
21135
|
+
enableTelegramNotificationPush: "Telegram \u901A\u77E5\u30D7\u30C3\u30B7\u30E5\u3092\u6709\u52B9\u306B\u3059\u308B",
|
|
21105
21136
|
sshReconnectRetries: "SSH \u518D\u63A5\u7D9A\u8A66\u884C\u56DE\u6570",
|
|
21106
21137
|
sshReconnectDelay: "SSH \u518D\u63A5\u7D9A\u5F85\u6A5F\uFF08\u79D2\uFF09",
|
|
21107
21138
|
language: "\u8A00\u8A9E",
|
|
@@ -21245,6 +21276,7 @@ Bot\uFF1A{{botName}}
|
|
|
21245
21276
|
clickToJump: "\u5BFE\u5FDC\u3059\u308B\u30DA\u30A4\u30F3\u306B\u30B8\u30E3\u30F3\u30D7",
|
|
21246
21277
|
eventType: {
|
|
21247
21278
|
terminal_bell: "\uD83D\uDD14 \u30BF\u30FC\u30DF\u30CA\u30EB\u30D9\u30EB",
|
|
21279
|
+
terminal_notification: "\uD83D\uDD14 \u30BF\u30FC\u30DF\u30CA\u30EB\u901A\u77E5",
|
|
21248
21280
|
tmux_window_close: "\uD83E\uDE9F \u30A6\u30A3\u30F3\u30C9\u30A6\u9589\u3058\u308B",
|
|
21249
21281
|
tmux_pane_close: "\uD83D\uDCF1 \u30DA\u30A4\u30F3\u9589\u3058\u308B",
|
|
21250
21282
|
device_tmux_missing: "\u26A0\uFE0F Tmux \u304C\u3042\u308A\u307E\u305B\u3093",
|
|
@@ -21263,7 +21295,8 @@ Bot\uFF1A{{botName}}
|
|
|
21263
21295
|
title: "\uD83D\uDD14 {{siteName}} \u304B\u3089\u306E\u30D9\u30EB\uFF1A{{terminalTopbarLabel}}",
|
|
21264
21296
|
viewLink: "\u8868\u793A",
|
|
21265
21297
|
terminalTopbarLabel: "\u30A6\u30A3\u30F3\u30C9\u30A6 {{window}} \xB7 \u30DA\u30A4\u30F3 {{pane}} @ {{device}}"
|
|
21266
|
-
}
|
|
21298
|
+
},
|
|
21299
|
+
telegramNotification: {}
|
|
21267
21300
|
},
|
|
21268
21301
|
sidebar: {
|
|
21269
21302
|
noWindows: "\u30A6\u30A3\u30F3\u30C9\u30A6\u304C\u3042\u308A\u307E\u305B\u3093",
|
|
@@ -21550,6 +21583,7 @@ __export(exports_schema, {
|
|
|
21550
21583
|
OptionU32Schema: () => OptionU32Schema,
|
|
21551
21584
|
OptionU16Schema: () => OptionU16Schema,
|
|
21552
21585
|
OptionStringSchema: () => OptionStringSchema,
|
|
21586
|
+
NotificationEventSchema: () => NotificationEventSchema,
|
|
21553
21587
|
LiveResumeSchema: () => LiveResumeSchema,
|
|
21554
21588
|
LayoutChangeEventSchema: () => LayoutChangeEventSchema,
|
|
21555
21589
|
HelloS2CSchema: () => HelloS2CSchema,
|
|
@@ -21776,6 +21810,16 @@ var BellEventSchema = import_zorsh.b.struct({
|
|
|
21776
21810
|
paneIndex: OptionU16Schema,
|
|
21777
21811
|
paneUrl: OptionStringSchema
|
|
21778
21812
|
});
|
|
21813
|
+
var NotificationEventSchema = import_zorsh.b.struct({
|
|
21814
|
+
source: import_zorsh.b.u8(),
|
|
21815
|
+
title: OptionStringSchema,
|
|
21816
|
+
body: import_zorsh.b.string(),
|
|
21817
|
+
windowId: OptionStringSchema,
|
|
21818
|
+
paneId: OptionStringSchema,
|
|
21819
|
+
windowIndex: OptionU16Schema,
|
|
21820
|
+
paneIndex: OptionU16Schema,
|
|
21821
|
+
paneUrl: OptionStringSchema
|
|
21822
|
+
});
|
|
21779
21823
|
// ../shared/src/ws-borsh/codec.ts
|
|
21780
21824
|
var MAGIC = new Uint8Array([84, 88]);
|
|
21781
21825
|
var CURRENT_VERSION = 1;
|
|
@@ -21988,6 +22032,16 @@ function resetChunkStreamId() {
|
|
|
21988
22032
|
nextChunkStreamId = 1;
|
|
21989
22033
|
}
|
|
21990
22034
|
// ../shared/src/ws-borsh/convert.ts
|
|
22035
|
+
var notificationSourceToU8 = {
|
|
22036
|
+
osc9: 1,
|
|
22037
|
+
osc777: 2,
|
|
22038
|
+
osc1337: 3
|
|
22039
|
+
};
|
|
22040
|
+
var notificationSourceFromU8 = {
|
|
22041
|
+
1: "osc9",
|
|
22042
|
+
2: "osc777",
|
|
22043
|
+
3: "osc1337"
|
|
22044
|
+
};
|
|
21991
22045
|
function encodeDeviceEventPayload(payload) {
|
|
21992
22046
|
const eventTypeMap = {
|
|
21993
22047
|
"tmux-missing": 1,
|
|
@@ -22015,7 +22069,8 @@ function encodeTmuxEventPayload(payload) {
|
|
|
22015
22069
|
"pane-active": 7,
|
|
22016
22070
|
"layout-change": 8,
|
|
22017
22071
|
bell: 9,
|
|
22018
|
-
output: 10
|
|
22072
|
+
output: 10,
|
|
22073
|
+
notification: 11
|
|
22019
22074
|
};
|
|
22020
22075
|
const eventData = encodeEventData(payload.type, payload.data);
|
|
22021
22076
|
const wireData = {
|
|
@@ -22083,6 +22138,19 @@ function encodeEventData(type, data) {
|
|
|
22083
22138
|
}
|
|
22084
22139
|
case "output":
|
|
22085
22140
|
return new Uint8Array;
|
|
22141
|
+
case "notification": {
|
|
22142
|
+
const d = data;
|
|
22143
|
+
return NotificationEventSchema.serialize({
|
|
22144
|
+
source: notificationSourceToU8[d.source],
|
|
22145
|
+
title: d.title ?? null,
|
|
22146
|
+
body: d.body,
|
|
22147
|
+
windowId: d.windowId ?? null,
|
|
22148
|
+
paneId: d.paneId ?? null,
|
|
22149
|
+
windowIndex: d.windowIndex ?? null,
|
|
22150
|
+
paneIndex: d.paneIndex ?? null,
|
|
22151
|
+
paneUrl: d.paneUrl ?? null
|
|
22152
|
+
});
|
|
22153
|
+
}
|
|
22086
22154
|
default:
|
|
22087
22155
|
return new Uint8Array;
|
|
22088
22156
|
}
|
|
@@ -22149,9 +22217,13 @@ function decodeTmuxEventPayload(data) {
|
|
|
22149
22217
|
7: "pane-active",
|
|
22150
22218
|
8: "layout-change",
|
|
22151
22219
|
9: "bell",
|
|
22152
|
-
10: "output"
|
|
22220
|
+
10: "output",
|
|
22221
|
+
11: "notification"
|
|
22153
22222
|
};
|
|
22154
|
-
const type = eventTypeMap[wire.eventType]
|
|
22223
|
+
const type = eventTypeMap[wire.eventType];
|
|
22224
|
+
if (!type) {
|
|
22225
|
+
throw new Error(`Unknown tmux event type: ${wire.eventType}`);
|
|
22226
|
+
}
|
|
22155
22227
|
return {
|
|
22156
22228
|
deviceId: wire.deviceId,
|
|
22157
22229
|
type,
|
|
@@ -22189,6 +22261,19 @@ function decodeEventData(type, data) {
|
|
|
22189
22261
|
paneUrl: bell.paneUrl ?? undefined
|
|
22190
22262
|
};
|
|
22191
22263
|
}
|
|
22264
|
+
case "notification": {
|
|
22265
|
+
const notification = NotificationEventSchema.deserialize(data);
|
|
22266
|
+
return {
|
|
22267
|
+
source: notificationSourceFromU8[notification.source] ?? "osc9",
|
|
22268
|
+
title: notification.title ?? undefined,
|
|
22269
|
+
body: notification.body,
|
|
22270
|
+
windowId: notification.windowId ?? undefined,
|
|
22271
|
+
paneId: notification.paneId ?? undefined,
|
|
22272
|
+
windowIndex: notification.windowIndex ?? undefined,
|
|
22273
|
+
paneIndex: notification.paneIndex ?? undefined,
|
|
22274
|
+
paneUrl: notification.paneUrl ?? undefined
|
|
22275
|
+
};
|
|
22276
|
+
}
|
|
22192
22277
|
default:
|
|
22193
22278
|
return {};
|
|
22194
22279
|
}
|
|
@@ -22308,6 +22393,13 @@ var runtimeController = new RuntimeController;
|
|
|
22308
22393
|
function getEnv(key, defaultValue) {
|
|
22309
22394
|
return process.env[key] ?? defaultValue;
|
|
22310
22395
|
}
|
|
22396
|
+
function getBooleanEnv(key, defaultValue) {
|
|
22397
|
+
const value = process.env[key];
|
|
22398
|
+
if (value === undefined) {
|
|
22399
|
+
return defaultValue;
|
|
22400
|
+
}
|
|
22401
|
+
return value === "1" || value.toLowerCase() === "true" || value.toLowerCase() === "yes";
|
|
22402
|
+
}
|
|
22311
22403
|
var config = {
|
|
22312
22404
|
masterKey: process.env.TMEX_MASTER_KEY,
|
|
22313
22405
|
port: Number.parseInt(getEnv("GATEWAY_PORT", "9663"), 10),
|
|
@@ -22315,6 +22407,8 @@ var config = {
|
|
|
22315
22407
|
siteNameDefault: getEnv("TMEX_SITE_NAME", "tmex"),
|
|
22316
22408
|
databaseUrl: getEnv("DATABASE_URL", "./tmex.db"),
|
|
22317
22409
|
bellThrottleSecondsDefault: Number.parseInt(getEnv("TMEX_BELL_THROTTLE_SECONDS", "6"), 10),
|
|
22410
|
+
notificationThrottleSecondsDefault: Number.parseInt(getEnv("TMEX_NOTIFICATION_THROTTLE_SECONDS", "3"), 10),
|
|
22411
|
+
tmuxAllowPassthrough: getBooleanEnv("TMEX_TMUX_ALLOW_PASSTHROUGH", false),
|
|
22318
22412
|
sshReconnectMaxRetriesDefault: Number.parseInt(getEnv("TMEX_SSH_RECONNECT_MAX_RETRIES", "2"), 10),
|
|
22319
22413
|
sshReconnectDelaySecondsDefault: Number.parseInt(getEnv("TMEX_SSH_RECONNECT_DELAY_SECONDS", "10"), 10),
|
|
22320
22414
|
languageDefault: getEnv("TMEX_DEFAULT_LANGUAGE", "en_US"),
|
|
@@ -28753,8 +28847,13 @@ var siteSettings = sqliteTable("site_settings", {
|
|
|
28753
28847
|
siteName: text("site_name").notNull(),
|
|
28754
28848
|
siteUrl: text("site_url").notNull(),
|
|
28755
28849
|
bellThrottleSeconds: integer("bell_throttle_seconds").notNull(),
|
|
28850
|
+
notificationThrottleSeconds: integer("notification_throttle_seconds").notNull().default(3),
|
|
28756
28851
|
enableBrowserBellToast: integer("enable_browser_bell_toast", { mode: "boolean" }).notNull().default(true),
|
|
28852
|
+
enableBrowserNotificationToast: integer("enable_browser_notification_toast", { mode: "boolean" }).notNull().default(true),
|
|
28757
28853
|
enableTelegramBellPush: integer("enable_telegram_bell_push", { mode: "boolean" }).notNull().default(true),
|
|
28854
|
+
enableTelegramNotificationPush: integer("enable_telegram_notification_push", {
|
|
28855
|
+
mode: "boolean"
|
|
28856
|
+
}).notNull().default(true),
|
|
28758
28857
|
sshReconnectMaxRetries: integer("ssh_reconnect_max_retries").notNull(),
|
|
28759
28858
|
sshReconnectDelaySeconds: integer("ssh_reconnect_delay_seconds").notNull(),
|
|
28760
28859
|
language: text("language").notNull().default("en_US"),
|
|
@@ -28869,8 +28968,11 @@ function toSiteSettings(row) {
|
|
|
28869
28968
|
siteName: row.siteName,
|
|
28870
28969
|
siteUrl: row.siteUrl,
|
|
28871
28970
|
bellThrottleSeconds: row.bellThrottleSeconds,
|
|
28971
|
+
notificationThrottleSeconds: row.notificationThrottleSeconds,
|
|
28872
28972
|
enableBrowserBellToast: row.enableBrowserBellToast,
|
|
28973
|
+
enableBrowserNotificationToast: row.enableBrowserNotificationToast,
|
|
28873
28974
|
enableTelegramBellPush: row.enableTelegramBellPush,
|
|
28975
|
+
enableTelegramNotificationPush: row.enableTelegramNotificationPush,
|
|
28874
28976
|
sshReconnectMaxRetries: row.sshReconnectMaxRetries,
|
|
28875
28977
|
sshReconnectDelaySeconds: row.sshReconnectDelaySeconds,
|
|
28876
28978
|
language: normalizeLocale(row.language),
|
|
@@ -28921,8 +29023,11 @@ function ensureSiteSettingsInitialized() {
|
|
|
28921
29023
|
siteName: config.siteNameDefault,
|
|
28922
29024
|
siteUrl: config.baseUrl,
|
|
28923
29025
|
bellThrottleSeconds: config.bellThrottleSecondsDefault,
|
|
29026
|
+
notificationThrottleSeconds: config.notificationThrottleSecondsDefault,
|
|
28924
29027
|
enableBrowserBellToast: true,
|
|
29028
|
+
enableBrowserNotificationToast: true,
|
|
28925
29029
|
enableTelegramBellPush: true,
|
|
29030
|
+
enableTelegramNotificationPush: true,
|
|
28926
29031
|
sshReconnectMaxRetries: config.sshReconnectMaxRetriesDefault,
|
|
28927
29032
|
sshReconnectDelaySeconds: config.sshReconnectDelaySecondsDefault,
|
|
28928
29033
|
language: normalizeLocale(config.languageDefault),
|
|
@@ -29048,8 +29153,11 @@ function updateSiteSettings(updates) {
|
|
|
29048
29153
|
siteName: updates.siteName ?? current.siteName,
|
|
29049
29154
|
siteUrl: updates.siteUrl ?? current.siteUrl,
|
|
29050
29155
|
bellThrottleSeconds: updates.bellThrottleSeconds ?? current.bellThrottleSeconds,
|
|
29156
|
+
notificationThrottleSeconds: updates.notificationThrottleSeconds ?? current.notificationThrottleSeconds,
|
|
29051
29157
|
enableBrowserBellToast: updates.enableBrowserBellToast ?? current.enableBrowserBellToast,
|
|
29158
|
+
enableBrowserNotificationToast: updates.enableBrowserNotificationToast ?? current.enableBrowserNotificationToast,
|
|
29052
29159
|
enableTelegramBellPush: updates.enableTelegramBellPush ?? current.enableTelegramBellPush,
|
|
29160
|
+
enableTelegramNotificationPush: updates.enableTelegramNotificationPush ?? current.enableTelegramNotificationPush,
|
|
29053
29161
|
sshReconnectMaxRetries: updates.sshReconnectMaxRetries ?? current.sshReconnectMaxRetries,
|
|
29054
29162
|
sshReconnectDelaySeconds: updates.sshReconnectDelaySeconds ?? current.sshReconnectDelaySeconds,
|
|
29055
29163
|
language: updates.language ? normalizeLocale(updates.language) : current.language,
|
|
@@ -29060,8 +29168,11 @@ function updateSiteSettings(updates) {
|
|
|
29060
29168
|
siteName: next.siteName,
|
|
29061
29169
|
siteUrl: next.siteUrl,
|
|
29062
29170
|
bellThrottleSeconds: next.bellThrottleSeconds,
|
|
29171
|
+
notificationThrottleSeconds: next.notificationThrottleSeconds,
|
|
29063
29172
|
enableBrowserBellToast: next.enableBrowserBellToast,
|
|
29173
|
+
enableBrowserNotificationToast: next.enableBrowserNotificationToast,
|
|
29064
29174
|
enableTelegramBellPush: next.enableTelegramBellPush,
|
|
29175
|
+
enableTelegramNotificationPush: next.enableTelegramNotificationPush,
|
|
29065
29176
|
sshReconnectMaxRetries: next.sshReconnectMaxRetries,
|
|
29066
29177
|
sshReconnectDelaySeconds: next.sshReconnectDelaySeconds,
|
|
29067
29178
|
language: next.language,
|
|
@@ -51725,6 +51836,7 @@ class EventNotifier {
|
|
|
51725
51836
|
lastRefresh = 0;
|
|
51726
51837
|
REFRESH_INTERVAL = 60000;
|
|
51727
51838
|
bellThrottleMap = new Map;
|
|
51839
|
+
notificationThrottleMap = new Map;
|
|
51728
51840
|
refreshConfig() {
|
|
51729
51841
|
const now = Date.now();
|
|
51730
51842
|
if (now - this.lastRefresh < this.REFRESH_INTERVAL)
|
|
@@ -51740,8 +51852,14 @@ class EventNotifier {
|
|
|
51740
51852
|
eventType,
|
|
51741
51853
|
timestamp: new Date().toISOString()
|
|
51742
51854
|
};
|
|
51743
|
-
if (eventType === "terminal_bell"
|
|
51744
|
-
|
|
51855
|
+
if (eventType === "terminal_bell") {
|
|
51856
|
+
if (!this.shouldPassBellThrottle(fullEvent)) {
|
|
51857
|
+
return;
|
|
51858
|
+
}
|
|
51859
|
+
} else if (eventType === "terminal_notification") {
|
|
51860
|
+
if (!this.shouldPassNotificationThrottle(fullEvent)) {
|
|
51861
|
+
return;
|
|
51862
|
+
}
|
|
51745
51863
|
}
|
|
51746
51864
|
await Promise.all([
|
|
51747
51865
|
this.sendWebhooks(eventType, fullEvent),
|
|
@@ -51763,6 +51881,22 @@ class EventNotifier {
|
|
|
51763
51881
|
this.bellThrottleMap.set(key, now);
|
|
51764
51882
|
return true;
|
|
51765
51883
|
}
|
|
51884
|
+
shouldPassNotificationThrottle(event) {
|
|
51885
|
+
const settings = getSiteSettings();
|
|
51886
|
+
const throttleMs = Math.max(0, settings.notificationThrottleSeconds) * 1000;
|
|
51887
|
+
if (throttleMs === 0) {
|
|
51888
|
+
return true;
|
|
51889
|
+
}
|
|
51890
|
+
const source = typeof event.payload?.source === "string" ? event.payload.source : "unknown";
|
|
51891
|
+
const key = `${event.device.id}:${event.tmux?.paneId ?? "-"}:notification:${source}`;
|
|
51892
|
+
const now = Date.now();
|
|
51893
|
+
const previous = this.notificationThrottleMap.get(key) ?? 0;
|
|
51894
|
+
if (now - previous < throttleMs) {
|
|
51895
|
+
return false;
|
|
51896
|
+
}
|
|
51897
|
+
this.notificationThrottleMap.set(key, now);
|
|
51898
|
+
return true;
|
|
51899
|
+
}
|
|
51766
51900
|
async sendWebhooks(eventType, event) {
|
|
51767
51901
|
const targets = this.webhooks.filter((w) => w.eventMask.includes(eventType));
|
|
51768
51902
|
await Promise.all(targets.map(async (webhook) => {
|
|
@@ -51800,6 +51934,14 @@ class EventNotifier {
|
|
|
51800
51934
|
await telegramService.sendToAuthorizedChats({ text: bellMessage, parseMode: "HTML" });
|
|
51801
51935
|
return;
|
|
51802
51936
|
}
|
|
51937
|
+
if (eventType === "terminal_notification") {
|
|
51938
|
+
if (!settings.enableTelegramNotificationPush) {
|
|
51939
|
+
return;
|
|
51940
|
+
}
|
|
51941
|
+
const notificationMessage = this.formatTelegramNotificationMessage(event);
|
|
51942
|
+
await telegramService.sendToAuthorizedChats({ text: notificationMessage, parseMode: "HTML" });
|
|
51943
|
+
return;
|
|
51944
|
+
}
|
|
51803
51945
|
const message = this.formatTelegramMessage(event, settings);
|
|
51804
51946
|
await telegramService.sendToAuthorizedChats({ text: message });
|
|
51805
51947
|
}
|
|
@@ -51824,11 +51966,34 @@ class EventNotifier {
|
|
|
51824
51966
|
lines.push("", `<a href="${escapeTelegramHtmlAttribute(tgSafePaneUrl)}">${escapeTelegramHtmlText(t2("notification.telegramBell.viewLink"))}</a>`);
|
|
51825
51967
|
}
|
|
51826
51968
|
return lines.join(`
|
|
51969
|
+
`);
|
|
51970
|
+
}
|
|
51971
|
+
formatTelegramNotificationMessage(event) {
|
|
51972
|
+
const title = typeof event.payload?.title === "string" ? event.payload.title : "";
|
|
51973
|
+
const body = typeof event.payload?.message === "string" ? event.payload.message : "";
|
|
51974
|
+
const lines = [];
|
|
51975
|
+
if (title) {
|
|
51976
|
+
lines.push(escapeTelegramHtmlText(title));
|
|
51977
|
+
}
|
|
51978
|
+
if (body) {
|
|
51979
|
+
lines.push(escapeTelegramHtmlText(body));
|
|
51980
|
+
}
|
|
51981
|
+
const paneUrl = normalizeHttpUrl(buildPaneUrl(event));
|
|
51982
|
+
const topbarLabel = this.buildTerminalTopbarLabel(event);
|
|
51983
|
+
const footer = `from ${event.site.name}: ${topbarLabel}`;
|
|
51984
|
+
if (paneUrl) {
|
|
51985
|
+
const tgSafePaneUrl = encodePercentForTelegramUrl(paneUrl);
|
|
51986
|
+
lines.push("", `<a href="${escapeTelegramHtmlAttribute(tgSafePaneUrl)}">${escapeTelegramHtmlText(footer)}</a>`);
|
|
51987
|
+
} else {
|
|
51988
|
+
lines.push("", escapeTelegramHtmlText(footer));
|
|
51989
|
+
}
|
|
51990
|
+
return lines.join(`
|
|
51827
51991
|
`);
|
|
51828
51992
|
}
|
|
51829
51993
|
formatTelegramMessage(event, settings) {
|
|
51830
51994
|
const emojiMap = {
|
|
51831
51995
|
terminal_bell: "\uD83D\uDD14",
|
|
51996
|
+
terminal_notification: "\uD83D\uDD14",
|
|
51832
51997
|
tmux_window_close: "\uD83E\uDE9F",
|
|
51833
51998
|
tmux_pane_close: "\uD83D\uDCF1",
|
|
51834
51999
|
device_tmux_missing: "\u26A0\uFE0F",
|
|
@@ -52075,12 +52240,80 @@ function encodeInputToHexChunks(input, chunkBytes = SEND_KEYS_HEX_CHUNK_BYTES) {
|
|
|
52075
52240
|
return chunks;
|
|
52076
52241
|
}
|
|
52077
52242
|
|
|
52078
|
-
// ../../apps/gateway/src/tmux-client/pane-
|
|
52243
|
+
// ../../apps/gateway/src/tmux-client/pane-stream-parser.ts
|
|
52079
52244
|
var decoder = new TextDecoder;
|
|
52080
|
-
|
|
52245
|
+
var MAX_OSC_KIND_BYTES = 16;
|
|
52246
|
+
var MAX_OSC_PAYLOAD_BYTES = 8 * 1024;
|
|
52247
|
+
function createPaneStreamParser(options) {
|
|
52081
52248
|
let phase = "normal";
|
|
52082
52249
|
let oscKind = "";
|
|
52250
|
+
let oscPayloadBytes = [];
|
|
52083
52251
|
let titleBytes = [];
|
|
52252
|
+
let warnedOscPayloadOverflow = false;
|
|
52253
|
+
function resetOscState() {
|
|
52254
|
+
oscKind = "";
|
|
52255
|
+
oscPayloadBytes = [];
|
|
52256
|
+
}
|
|
52257
|
+
function appendOscPayloadByte(byte) {
|
|
52258
|
+
if (oscPayloadBytes.length >= MAX_OSC_PAYLOAD_BYTES) {
|
|
52259
|
+
if (!warnedOscPayloadOverflow) {
|
|
52260
|
+
warnedOscPayloadOverflow = true;
|
|
52261
|
+
console.warn("[tmex] pane stream parser dropped oversized OSC payload");
|
|
52262
|
+
}
|
|
52263
|
+
oscPayloadBytes = [];
|
|
52264
|
+
phase = "osc-body-ignore";
|
|
52265
|
+
return false;
|
|
52266
|
+
}
|
|
52267
|
+
oscPayloadBytes.push(byte);
|
|
52268
|
+
return true;
|
|
52269
|
+
}
|
|
52270
|
+
function emitTitle(bytes) {
|
|
52271
|
+
const title = decoder.decode(new Uint8Array(bytes)).trim();
|
|
52272
|
+
if (!title) {
|
|
52273
|
+
return;
|
|
52274
|
+
}
|
|
52275
|
+
options.onTitle(title);
|
|
52276
|
+
}
|
|
52277
|
+
function emitOsc() {
|
|
52278
|
+
const payload = decoder.decode(new Uint8Array(oscPayloadBytes));
|
|
52279
|
+
switch (oscKind) {
|
|
52280
|
+
case "0":
|
|
52281
|
+
case "1":
|
|
52282
|
+
case "2":
|
|
52283
|
+
emitTitle(oscPayloadBytes);
|
|
52284
|
+
return;
|
|
52285
|
+
case "9":
|
|
52286
|
+
if (/^4(;|$)/.test(payload)) {
|
|
52287
|
+
return;
|
|
52288
|
+
}
|
|
52289
|
+
options.onNotification({ source: "osc9", body: payload });
|
|
52290
|
+
return;
|
|
52291
|
+
case "777": {
|
|
52292
|
+
const verbSeparatorIndex = payload.indexOf(";");
|
|
52293
|
+
const verb = verbSeparatorIndex >= 0 ? payload.slice(0, verbSeparatorIndex) : payload;
|
|
52294
|
+
if (verb !== "notify") {
|
|
52295
|
+
return;
|
|
52296
|
+
}
|
|
52297
|
+
const rest = verbSeparatorIndex >= 0 ? payload.slice(verbSeparatorIndex + 1) : "";
|
|
52298
|
+
const titleSeparatorIndex = rest.indexOf(";");
|
|
52299
|
+
const title = titleSeparatorIndex >= 0 ? rest.slice(0, titleSeparatorIndex) : rest;
|
|
52300
|
+
const body = titleSeparatorIndex >= 0 ? rest.slice(titleSeparatorIndex + 1) : "";
|
|
52301
|
+
options.onNotification({
|
|
52302
|
+
source: "osc777",
|
|
52303
|
+
title: title || undefined,
|
|
52304
|
+
body
|
|
52305
|
+
});
|
|
52306
|
+
return;
|
|
52307
|
+
}
|
|
52308
|
+
case "1337":
|
|
52309
|
+
if (/^RequestAttention=(yes|once|fireworks|true)$/i.test(payload)) {
|
|
52310
|
+
options.onNotification({ source: "osc1337", body: "RequestAttention" });
|
|
52311
|
+
}
|
|
52312
|
+
return;
|
|
52313
|
+
default:
|
|
52314
|
+
return;
|
|
52315
|
+
}
|
|
52316
|
+
}
|
|
52084
52317
|
return {
|
|
52085
52318
|
push(data) {
|
|
52086
52319
|
const output = [];
|
|
@@ -52088,36 +52321,57 @@ function createPaneTitleParser(options) {
|
|
|
52088
52321
|
if (phase === "normal") {
|
|
52089
52322
|
if (byte === 27) {
|
|
52090
52323
|
phase = "esc";
|
|
52091
|
-
|
|
52092
|
-
output.push(byte);
|
|
52324
|
+
continue;
|
|
52093
52325
|
}
|
|
52326
|
+
if (byte === 7) {
|
|
52327
|
+
options.onBell();
|
|
52328
|
+
continue;
|
|
52329
|
+
}
|
|
52330
|
+
output.push(byte);
|
|
52094
52331
|
continue;
|
|
52095
52332
|
}
|
|
52096
52333
|
if (phase === "esc") {
|
|
52097
52334
|
if (byte === 93) {
|
|
52098
|
-
|
|
52099
|
-
|
|
52335
|
+
resetOscState();
|
|
52336
|
+
phase = "osc-params";
|
|
52337
|
+
continue;
|
|
52338
|
+
}
|
|
52339
|
+
if (byte === 107) {
|
|
52100
52340
|
titleBytes = [];
|
|
52341
|
+
phase = "screen-title";
|
|
52101
52342
|
continue;
|
|
52102
52343
|
}
|
|
52103
52344
|
output.push(27, byte);
|
|
52104
52345
|
phase = "normal";
|
|
52105
52346
|
continue;
|
|
52106
52347
|
}
|
|
52107
|
-
if (phase === "osc") {
|
|
52348
|
+
if (phase === "osc-params") {
|
|
52108
52349
|
if (byte === 59) {
|
|
52109
|
-
phase = oscKind === "0" || oscKind === "2" ? "osc-
|
|
52110
|
-
|
|
52111
|
-
|
|
52112
|
-
|
|
52350
|
+
phase = oscKind === "0" || oscKind === "1" || oscKind === "2" || oscKind === "9" || oscKind === "777" || oscKind === "1337" ? "osc-body" : "osc-body-ignore";
|
|
52351
|
+
continue;
|
|
52352
|
+
}
|
|
52353
|
+
if (byte === 7) {
|
|
52354
|
+
emitOsc();
|
|
52355
|
+
resetOscState();
|
|
52356
|
+
phase = "normal";
|
|
52357
|
+
continue;
|
|
52358
|
+
}
|
|
52359
|
+
if (byte === 27) {
|
|
52360
|
+
phase = "osc-st";
|
|
52361
|
+
continue;
|
|
52362
|
+
}
|
|
52363
|
+
if (oscKind.length >= MAX_OSC_KIND_BYTES) {
|
|
52364
|
+
resetOscState();
|
|
52365
|
+
phase = "osc-body-ignore";
|
|
52113
52366
|
continue;
|
|
52114
52367
|
}
|
|
52115
52368
|
oscKind += String.fromCharCode(byte);
|
|
52116
52369
|
continue;
|
|
52117
52370
|
}
|
|
52118
|
-
if (phase === "osc-
|
|
52371
|
+
if (phase === "osc-body") {
|
|
52119
52372
|
if (byte === 7) {
|
|
52120
|
-
|
|
52373
|
+
emitOsc();
|
|
52374
|
+
resetOscState();
|
|
52121
52375
|
phase = "normal";
|
|
52122
52376
|
continue;
|
|
52123
52377
|
}
|
|
@@ -52125,31 +52379,69 @@ function createPaneTitleParser(options) {
|
|
|
52125
52379
|
phase = "osc-st";
|
|
52126
52380
|
continue;
|
|
52127
52381
|
}
|
|
52382
|
+
appendOscPayloadByte(byte);
|
|
52383
|
+
continue;
|
|
52384
|
+
}
|
|
52385
|
+
if (phase === "osc-body-ignore") {
|
|
52386
|
+
if (byte === 7) {
|
|
52387
|
+
resetOscState();
|
|
52388
|
+
phase = "normal";
|
|
52389
|
+
continue;
|
|
52390
|
+
}
|
|
52391
|
+
if (byte === 27) {
|
|
52392
|
+
phase = "osc-st-ignore";
|
|
52393
|
+
}
|
|
52394
|
+
continue;
|
|
52395
|
+
}
|
|
52396
|
+
if (phase === "osc-st") {
|
|
52397
|
+
if (byte === 92) {
|
|
52398
|
+
emitOsc();
|
|
52399
|
+
resetOscState();
|
|
52400
|
+
phase = "normal";
|
|
52401
|
+
continue;
|
|
52402
|
+
}
|
|
52403
|
+
if (!appendOscPayloadByte(27)) {
|
|
52404
|
+
continue;
|
|
52405
|
+
}
|
|
52406
|
+
appendOscPayloadByte(byte);
|
|
52407
|
+
continue;
|
|
52408
|
+
}
|
|
52409
|
+
if (phase === "osc-st-ignore") {
|
|
52410
|
+
if (byte === 92) {
|
|
52411
|
+
resetOscState();
|
|
52412
|
+
phase = "normal";
|
|
52413
|
+
continue;
|
|
52414
|
+
}
|
|
52415
|
+
phase = "osc-body-ignore";
|
|
52416
|
+
continue;
|
|
52417
|
+
}
|
|
52418
|
+
if (phase === "screen-title") {
|
|
52419
|
+
if (byte === 7) {
|
|
52420
|
+
emitTitle(titleBytes);
|
|
52421
|
+
titleBytes = [];
|
|
52422
|
+
phase = "normal";
|
|
52423
|
+
continue;
|
|
52424
|
+
}
|
|
52425
|
+
if (byte === 27) {
|
|
52426
|
+
phase = "screen-title-st";
|
|
52427
|
+
continue;
|
|
52428
|
+
}
|
|
52128
52429
|
titleBytes.push(byte);
|
|
52129
52430
|
continue;
|
|
52130
52431
|
}
|
|
52131
52432
|
if (byte === 92) {
|
|
52132
|
-
emitTitle(
|
|
52433
|
+
emitTitle(titleBytes);
|
|
52434
|
+
titleBytes = [];
|
|
52133
52435
|
phase = "normal";
|
|
52134
52436
|
continue;
|
|
52135
52437
|
}
|
|
52136
52438
|
titleBytes.push(27, byte);
|
|
52137
|
-
phase = "
|
|
52439
|
+
phase = "screen-title";
|
|
52138
52440
|
}
|
|
52139
52441
|
return new Uint8Array(output);
|
|
52140
52442
|
}
|
|
52141
52443
|
};
|
|
52142
52444
|
}
|
|
52143
|
-
function emitTitle(onTitle, titleBytes) {
|
|
52144
|
-
const title = decoder.decode(new Uint8Array(titleBytes)).trim();
|
|
52145
|
-
if (!title) {
|
|
52146
|
-
return;
|
|
52147
|
-
}
|
|
52148
|
-
onTitle(title);
|
|
52149
|
-
}
|
|
52150
|
-
function encoderFromString(value) {
|
|
52151
|
-
return new TextEncoder().encode(value);
|
|
52152
|
-
}
|
|
52153
52445
|
|
|
52154
52446
|
// ../../apps/gateway/src/tmux-client/local-external-connection.ts
|
|
52155
52447
|
function hasRenderableTerminalContent(value) {
|
|
@@ -52193,8 +52485,7 @@ class LocalExternalTmuxConnection {
|
|
|
52193
52485
|
pendingPaneTitles = new Map;
|
|
52194
52486
|
snapshotSession = null;
|
|
52195
52487
|
snapshotWindows = new Map;
|
|
52196
|
-
|
|
52197
|
-
pipeReadAbort = null;
|
|
52488
|
+
paneReaders = new Map;
|
|
52198
52489
|
pipeTransition = Promise.resolve();
|
|
52199
52490
|
inputTransition = Promise.resolve();
|
|
52200
52491
|
hookReadAbort = null;
|
|
@@ -52231,6 +52522,7 @@ class LocalExternalTmuxConnection {
|
|
|
52231
52522
|
});
|
|
52232
52523
|
this.ensureRuntimeDirs();
|
|
52233
52524
|
await this.ensureSession();
|
|
52525
|
+
await this.configureSessionOptions();
|
|
52234
52526
|
if (this.deps.enableHooks) {
|
|
52235
52527
|
await this.startHooks();
|
|
52236
52528
|
}
|
|
@@ -52248,7 +52540,7 @@ class LocalExternalTmuxConnection {
|
|
|
52248
52540
|
}
|
|
52249
52541
|
this.manualDisconnect = true;
|
|
52250
52542
|
this.connected = false;
|
|
52251
|
-
this.
|
|
52543
|
+
this.stopAllPipeReaders();
|
|
52252
52544
|
if (this.deps.enableHooks) {
|
|
52253
52545
|
this.stopHooks();
|
|
52254
52546
|
}
|
|
@@ -52346,6 +52638,26 @@ class LocalExternalTmuxConnection {
|
|
|
52346
52638
|
}
|
|
52347
52639
|
await this.runTmux(["new-session", "-d", "-c", homedir(), "-s", this.sessionName]);
|
|
52348
52640
|
}
|
|
52641
|
+
async configureSessionOptions() {
|
|
52642
|
+
await this.runTmuxAllowFailure([
|
|
52643
|
+
"set-option",
|
|
52644
|
+
"-t",
|
|
52645
|
+
this.sessionName,
|
|
52646
|
+
"-s",
|
|
52647
|
+
"allow-passthrough",
|
|
52648
|
+
config.tmuxAllowPassthrough ? "on" : "off"
|
|
52649
|
+
]);
|
|
52650
|
+
await this.runTmuxAllowFailure(["set-option", "-t", this.sessionName, "-g", "extended-keys", "on"]);
|
|
52651
|
+
await this.runTmuxAllowFailure([
|
|
52652
|
+
"set-option",
|
|
52653
|
+
"-t",
|
|
52654
|
+
this.sessionName,
|
|
52655
|
+
"-s",
|
|
52656
|
+
"extended-keys-format",
|
|
52657
|
+
"csi-u"
|
|
52658
|
+
]);
|
|
52659
|
+
await this.runTmuxAllowFailure(["set-option", "-t", this.sessionName, "-g", "focus-events", "on"]);
|
|
52660
|
+
}
|
|
52349
52661
|
ensureRuntimeDirs() {
|
|
52350
52662
|
mkdirSync(this.fsPaths.rootDir, { recursive: true, mode: 448 });
|
|
52351
52663
|
mkdirSync(this.fsPaths.panesDir, { recursive: true, mode: 448 });
|
|
@@ -52381,14 +52693,16 @@ class LocalExternalTmuxConnection {
|
|
|
52381
52693
|
readerProcess.kill();
|
|
52382
52694
|
rmSync(fifoPath, { force: true });
|
|
52383
52695
|
};
|
|
52384
|
-
await this.installHook("alert-bell", ["bell", "#{window_id}", "#{pane_id}"]);
|
|
52385
52696
|
await this.installHook("pane-exited", ["pane-exited", "#{window_id}", "#{pane_id}"]);
|
|
52386
52697
|
await this.installHook("pane-died", ["pane-died", "#{window_id}", "#{pane_id}"]);
|
|
52698
|
+
await this.installHook("after-new-window", ["refresh"]);
|
|
52699
|
+
await this.installHook("after-split-window", ["refresh"]);
|
|
52387
52700
|
}
|
|
52388
52701
|
async stopHooks() {
|
|
52389
|
-
await this.runTmuxAllowFailure(["set-hook", "-u", "-t", this.sessionName, "alert-bell"]);
|
|
52390
52702
|
await this.runTmuxAllowFailure(["set-hook", "-u", "-t", this.sessionName, "pane-exited"]);
|
|
52391
52703
|
await this.runTmuxAllowFailure(["set-hook", "-u", "-t", this.sessionName, "pane-died"]);
|
|
52704
|
+
await this.runTmuxAllowFailure(["set-hook", "-u", "-t", this.sessionName, "after-new-window"]);
|
|
52705
|
+
await this.runTmuxAllowFailure(["set-hook", "-u", "-t", this.sessionName, "after-split-window"]);
|
|
52392
52706
|
this.hookReadAbort?.();
|
|
52393
52707
|
this.hookReadAbort = null;
|
|
52394
52708
|
this.hookBuffer = "";
|
|
@@ -52419,22 +52733,9 @@ class LocalExternalTmuxConnection {
|
|
|
52419
52733
|
}
|
|
52420
52734
|
const [type, windowId, paneId] = line.split("\t");
|
|
52421
52735
|
if (type === "bell") {
|
|
52422
|
-
const key = paneId || windowId || "-";
|
|
52423
|
-
const previous = this.bellDedup.get(key) ?? 0;
|
|
52424
|
-
const now = Date.now();
|
|
52425
|
-
if (now - previous >= BELL_DEDUP_WINDOW_MS) {
|
|
52426
|
-
this.bellDedup.set(key, now);
|
|
52427
|
-
this.callbacks.onEvent({
|
|
52428
|
-
type: "bell",
|
|
52429
|
-
data: {
|
|
52430
|
-
windowId: windowId || undefined,
|
|
52431
|
-
paneId: paneId || this.activePaneId || undefined
|
|
52432
|
-
}
|
|
52433
|
-
});
|
|
52434
|
-
}
|
|
52435
52736
|
continue;
|
|
52436
52737
|
}
|
|
52437
|
-
if (type === "pane-exited" || type === "pane-died") {
|
|
52738
|
+
if (type === "pane-exited" || type === "pane-died" || type === "refresh") {
|
|
52438
52739
|
this.requestSnapshot();
|
|
52439
52740
|
}
|
|
52440
52741
|
}
|
|
@@ -52465,7 +52766,6 @@ class LocalExternalTmuxConnection {
|
|
|
52465
52766
|
this.activePaneId = paneId;
|
|
52466
52767
|
await this.runTmux(["select-window", "-t", windowId], true);
|
|
52467
52768
|
await this.runTmux(["select-pane", "-t", paneId], true);
|
|
52468
|
-
await this.startPipeForPane(paneId);
|
|
52469
52769
|
if (size) {
|
|
52470
52770
|
await this.resizePaneInternal(paneId, size.cols, size.rows);
|
|
52471
52771
|
}
|
|
@@ -52520,6 +52820,7 @@ class LocalExternalTmuxConnection {
|
|
|
52520
52820
|
this.parseSnapshotSession(sessionRes.stdout.split(/\r?\n/));
|
|
52521
52821
|
this.parseSnapshotWindows(windowsRes.stdout.split(/\r?\n/));
|
|
52522
52822
|
this.parseSnapshotPanes(panesRes.stdout.split(/\r?\n/));
|
|
52823
|
+
await this.syncPipeReaders();
|
|
52523
52824
|
this.emitSnapshot();
|
|
52524
52825
|
}
|
|
52525
52826
|
parseSnapshotSession(lines) {
|
|
@@ -52617,69 +52918,122 @@ class LocalExternalTmuxConnection {
|
|
|
52617
52918
|
}
|
|
52618
52919
|
return null;
|
|
52619
52920
|
}
|
|
52620
|
-
|
|
52621
|
-
|
|
52622
|
-
|
|
52623
|
-
|
|
52921
|
+
recordBell(paneId, windowId) {
|
|
52922
|
+
const key = paneId || windowId || "-";
|
|
52923
|
+
const previous = this.bellDedup.get(key) ?? 0;
|
|
52924
|
+
const now = Date.now();
|
|
52925
|
+
if (now - previous < BELL_DEDUP_WINDOW_MS) {
|
|
52926
|
+
return;
|
|
52927
|
+
}
|
|
52928
|
+
this.bellDedup.set(key, now);
|
|
52929
|
+
this.callbacks.onEvent({
|
|
52930
|
+
type: "bell",
|
|
52931
|
+
data: {
|
|
52932
|
+
windowId,
|
|
52933
|
+
paneId: paneId || this.activePaneId || undefined
|
|
52934
|
+
}
|
|
52935
|
+
});
|
|
52936
|
+
}
|
|
52937
|
+
emitNotification(paneId, notification) {
|
|
52938
|
+
this.callbacks.onEvent({
|
|
52939
|
+
type: "notification",
|
|
52940
|
+
data: {
|
|
52941
|
+
paneId,
|
|
52942
|
+
...notification
|
|
52943
|
+
}
|
|
52944
|
+
});
|
|
52945
|
+
}
|
|
52946
|
+
getExpectedPaneIds() {
|
|
52947
|
+
return Array.from(this.snapshotWindows.values()).sort((left, right) => left.index - right.index).flatMap((window2) => window2.panes.map((pane) => pane.id));
|
|
52948
|
+
}
|
|
52949
|
+
async startPipeForPaneNow(paneId) {
|
|
52950
|
+
if (this.paneReaders.has(paneId)) {
|
|
52951
|
+
return;
|
|
52952
|
+
}
|
|
52953
|
+
const fifoPath = this.fsPaths.paneFifoPath(paneId);
|
|
52954
|
+
this.ensureRuntimeDirs();
|
|
52955
|
+
rmSync(fifoPath, { force: true });
|
|
52956
|
+
await this.runShell(`mkfifo ${quoteShellArg(fifoPath)}`);
|
|
52957
|
+
const parser = createPaneStreamParser({
|
|
52958
|
+
onTitle: (title) => {
|
|
52959
|
+
this.pendingPaneTitles.set(paneId, title);
|
|
52960
|
+
this.requestSnapshot();
|
|
52961
|
+
},
|
|
52962
|
+
onBell: () => {
|
|
52963
|
+
this.recordBell(paneId);
|
|
52964
|
+
},
|
|
52965
|
+
onNotification: (notification) => {
|
|
52966
|
+
this.emitNotification(paneId, notification);
|
|
52624
52967
|
}
|
|
52625
|
-
|
|
52626
|
-
|
|
52627
|
-
|
|
52968
|
+
});
|
|
52969
|
+
const readerProcess = Bun.spawn(["/bin/sh", "-lc", `cat ${quoteShellArg(fifoPath)}`], {
|
|
52970
|
+
stdout: "pipe",
|
|
52971
|
+
stderr: "pipe"
|
|
52972
|
+
});
|
|
52973
|
+
const reader = readerProcess.stdout.getReader();
|
|
52974
|
+
const stopReader = () => {
|
|
52975
|
+
reader.releaseLock();
|
|
52976
|
+
readerProcess.kill();
|
|
52628
52977
|
rmSync(fifoPath, { force: true });
|
|
52629
|
-
|
|
52630
|
-
|
|
52631
|
-
|
|
52632
|
-
|
|
52633
|
-
|
|
52634
|
-
|
|
52635
|
-
|
|
52636
|
-
|
|
52637
|
-
stdout: "pipe",
|
|
52638
|
-
stderr: "pipe"
|
|
52639
|
-
});
|
|
52640
|
-
const reader = readerProcess.stdout.getReader();
|
|
52641
|
-
(async () => {
|
|
52642
|
-
try {
|
|
52643
|
-
while (true) {
|
|
52644
|
-
const chunk2 = await reader.read();
|
|
52645
|
-
if (chunk2.done) {
|
|
52646
|
-
break;
|
|
52647
|
-
}
|
|
52648
|
-
const raw = chunk2.value;
|
|
52649
|
-
const output = parser.push(raw);
|
|
52650
|
-
if (Array.from(raw).includes(7)) {
|
|
52651
|
-
this.callbacks.onEvent({ type: "bell", data: { paneId } });
|
|
52652
|
-
}
|
|
52653
|
-
if (output.length > 0) {
|
|
52654
|
-
this.callbacks.onTerminalOutput(paneId, output);
|
|
52655
|
-
}
|
|
52978
|
+
};
|
|
52979
|
+
this.paneReaders.set(paneId, { paneId, fifoPath, stopReader });
|
|
52980
|
+
(async () => {
|
|
52981
|
+
try {
|
|
52982
|
+
while (true) {
|
|
52983
|
+
const chunk2 = await reader.read();
|
|
52984
|
+
if (chunk2.done) {
|
|
52985
|
+
break;
|
|
52656
52986
|
}
|
|
52657
|
-
|
|
52658
|
-
if (
|
|
52659
|
-
this.callbacks.
|
|
52987
|
+
const output = parser.push(chunk2.value);
|
|
52988
|
+
if (output.length > 0) {
|
|
52989
|
+
this.callbacks.onTerminalOutput(paneId, output);
|
|
52660
52990
|
}
|
|
52661
52991
|
}
|
|
52662
|
-
}
|
|
52663
|
-
|
|
52664
|
-
|
|
52665
|
-
|
|
52666
|
-
|
|
52667
|
-
|
|
52992
|
+
} catch (error) {
|
|
52993
|
+
if (!this.manualDisconnect && !shouldIgnoreReaderAbortError(error)) {
|
|
52994
|
+
this.callbacks.onError(error instanceof Error ? error : new Error(String(error)));
|
|
52995
|
+
}
|
|
52996
|
+
}
|
|
52997
|
+
})();
|
|
52998
|
+
try {
|
|
52668
52999
|
await this.runTmux(["pipe-pane", "-O", "-t", paneId, `cat >${fifoPath}`]);
|
|
52669
|
-
|
|
52670
|
-
|
|
52671
|
-
|
|
52672
|
-
|
|
52673
|
-
|
|
53000
|
+
} catch (error) {
|
|
53001
|
+
this.paneReaders.delete(paneId);
|
|
53002
|
+
stopReader();
|
|
53003
|
+
throw error;
|
|
53004
|
+
}
|
|
52674
53005
|
}
|
|
52675
|
-
async
|
|
52676
|
-
const
|
|
52677
|
-
|
|
52678
|
-
|
|
52679
|
-
await this.runTmuxAllowFailure(["pipe-pane", "-t", paneId]);
|
|
53006
|
+
async stopPipeForPaneNow(paneId) {
|
|
53007
|
+
const handle = this.paneReaders.get(paneId);
|
|
53008
|
+
if (!handle) {
|
|
53009
|
+
return;
|
|
52680
53010
|
}
|
|
52681
|
-
this.
|
|
52682
|
-
this.
|
|
53011
|
+
this.paneReaders.delete(paneId);
|
|
53012
|
+
await this.runTmuxAllowFailure(["pipe-pane", "-t", paneId]);
|
|
53013
|
+
handle.stopReader();
|
|
53014
|
+
}
|
|
53015
|
+
async syncPipeReaders() {
|
|
53016
|
+
const expectedPaneIds = this.getExpectedPaneIds();
|
|
53017
|
+
const expectedSet = new Set(expectedPaneIds);
|
|
53018
|
+
await this.queuePipeTransition(async () => {
|
|
53019
|
+
for (const paneId of Array.from(this.paneReaders.keys())) {
|
|
53020
|
+
if (!expectedSet.has(paneId)) {
|
|
53021
|
+
await this.stopPipeForPaneNow(paneId);
|
|
53022
|
+
}
|
|
53023
|
+
}
|
|
53024
|
+
for (const paneId of expectedPaneIds) {
|
|
53025
|
+
if (!this.paneReaders.has(paneId)) {
|
|
53026
|
+
await this.startPipeForPaneNow(paneId);
|
|
53027
|
+
}
|
|
53028
|
+
}
|
|
53029
|
+
});
|
|
53030
|
+
}
|
|
53031
|
+
async stopAllPipeReaders() {
|
|
53032
|
+
await this.queuePipeTransition(async () => {
|
|
53033
|
+
for (const paneId of Array.from(this.paneReaders.keys())) {
|
|
53034
|
+
await this.stopPipeForPaneNow(paneId);
|
|
53035
|
+
}
|
|
53036
|
+
});
|
|
52683
53037
|
}
|
|
52684
53038
|
queuePipeTransition(task) {
|
|
52685
53039
|
const next = this.pipeTransition.catch(() => {
|
|
@@ -53068,8 +53422,7 @@ class SshExternalTmuxConnection {
|
|
|
53068
53422
|
pendingPaneTitles = new Map;
|
|
53069
53423
|
snapshotSession = null;
|
|
53070
53424
|
snapshotWindows = new Map;
|
|
53071
|
-
|
|
53072
|
-
pipeReadAbort = null;
|
|
53425
|
+
paneReaders = new Map;
|
|
53073
53426
|
pipeTransition = Promise.resolve();
|
|
53074
53427
|
hookReadAbort = null;
|
|
53075
53428
|
hookBuffer = "";
|
|
@@ -53115,6 +53468,7 @@ class SshExternalTmuxConnection {
|
|
|
53115
53468
|
await this.openCommandChannel();
|
|
53116
53469
|
await this.ensureRemoteRuntimeDirs();
|
|
53117
53470
|
await this.ensureSession();
|
|
53471
|
+
await this.configureSessionOptions();
|
|
53118
53472
|
await this.startHooks();
|
|
53119
53473
|
this.connected = true;
|
|
53120
53474
|
updateDeviceRuntimeStatus(this.deviceId, {
|
|
@@ -53323,6 +53677,26 @@ class SshExternalTmuxConnection {
|
|
|
53323
53677
|
}
|
|
53324
53678
|
await this.runTmux(["new-session", "-d", "-c", this.remoteHomeDir, "-s", this.sessionName]);
|
|
53325
53679
|
}
|
|
53680
|
+
async configureSessionOptions() {
|
|
53681
|
+
await this.runTmuxAllowFailure([
|
|
53682
|
+
"set-option",
|
|
53683
|
+
"-t",
|
|
53684
|
+
this.sessionName,
|
|
53685
|
+
"-s",
|
|
53686
|
+
"allow-passthrough",
|
|
53687
|
+
config.tmuxAllowPassthrough ? "on" : "off"
|
|
53688
|
+
]);
|
|
53689
|
+
await this.runTmuxAllowFailure(["set-option", "-t", this.sessionName, "-g", "extended-keys", "on"]);
|
|
53690
|
+
await this.runTmuxAllowFailure([
|
|
53691
|
+
"set-option",
|
|
53692
|
+
"-t",
|
|
53693
|
+
this.sessionName,
|
|
53694
|
+
"-s",
|
|
53695
|
+
"extended-keys-format",
|
|
53696
|
+
"csi-u"
|
|
53697
|
+
]);
|
|
53698
|
+
await this.runTmuxAllowFailure(["set-option", "-t", this.sessionName, "-g", "focus-events", "on"]);
|
|
53699
|
+
}
|
|
53326
53700
|
async startHooks() {
|
|
53327
53701
|
await this.ensureRemoteRuntimeDirs();
|
|
53328
53702
|
const fifoPath = this.fsPaths.hookFifoPath;
|
|
@@ -53341,14 +53715,16 @@ class SshExternalTmuxConnection {
|
|
|
53341
53715
|
stopReader();
|
|
53342
53716
|
this.runShellAllowFailure(`rm -f ${quoteShellArg(fifoPath)}`);
|
|
53343
53717
|
};
|
|
53344
|
-
await this.installHook("alert-bell", ["bell", "#{window_id}", "#{pane_id}"]);
|
|
53345
53718
|
await this.installHook("pane-exited", ["pane-exited", "#{window_id}", "#{pane_id}"]);
|
|
53346
53719
|
await this.installHook("pane-died", ["pane-died", "#{window_id}", "#{pane_id}"]);
|
|
53720
|
+
await this.installHook("after-new-window", ["refresh"]);
|
|
53721
|
+
await this.installHook("after-split-window", ["refresh"]);
|
|
53347
53722
|
}
|
|
53348
53723
|
async stopHooks() {
|
|
53349
|
-
await this.runTmuxAllowFailure(["set-hook", "-u", "-t", this.sessionName, "alert-bell"]);
|
|
53350
53724
|
await this.runTmuxAllowFailure(["set-hook", "-u", "-t", this.sessionName, "pane-exited"]);
|
|
53351
53725
|
await this.runTmuxAllowFailure(["set-hook", "-u", "-t", this.sessionName, "pane-died"]);
|
|
53726
|
+
await this.runTmuxAllowFailure(["set-hook", "-u", "-t", this.sessionName, "after-new-window"]);
|
|
53727
|
+
await this.runTmuxAllowFailure(["set-hook", "-u", "-t", this.sessionName, "after-split-window"]);
|
|
53352
53728
|
this.hookReadAbort?.();
|
|
53353
53729
|
this.hookReadAbort = null;
|
|
53354
53730
|
this.hookBuffer = "";
|
|
@@ -53379,22 +53755,9 @@ class SshExternalTmuxConnection {
|
|
|
53379
53755
|
}
|
|
53380
53756
|
const [type, windowId, paneId] = line.split("\t");
|
|
53381
53757
|
if (type === "bell") {
|
|
53382
|
-
const key = paneId || windowId || "-";
|
|
53383
|
-
const previous = this.bellDedup.get(key) ?? 0;
|
|
53384
|
-
const now = Date.now();
|
|
53385
|
-
if (now - previous >= BELL_DEDUP_WINDOW_MS2) {
|
|
53386
|
-
this.bellDedup.set(key, now);
|
|
53387
|
-
this.callbacks.onEvent({
|
|
53388
|
-
type: "bell",
|
|
53389
|
-
data: {
|
|
53390
|
-
windowId: windowId || undefined,
|
|
53391
|
-
paneId: paneId || this.activePaneId || undefined
|
|
53392
|
-
}
|
|
53393
|
-
});
|
|
53394
|
-
}
|
|
53395
53758
|
continue;
|
|
53396
53759
|
}
|
|
53397
|
-
if (type === "pane-exited" || type === "pane-died") {
|
|
53760
|
+
if (type === "pane-exited" || type === "pane-died" || type === "refresh") {
|
|
53398
53761
|
this.requestSnapshot();
|
|
53399
53762
|
}
|
|
53400
53763
|
}
|
|
@@ -53425,7 +53788,6 @@ class SshExternalTmuxConnection {
|
|
|
53425
53788
|
this.activePaneId = paneId;
|
|
53426
53789
|
await this.runTmux(["select-window", "-t", windowId], true);
|
|
53427
53790
|
await this.runTmux(["select-pane", "-t", paneId], true);
|
|
53428
|
-
await this.startPipeForPane(paneId);
|
|
53429
53791
|
if (size) {
|
|
53430
53792
|
await this.resizePaneInternal(paneId, size.cols, size.rows);
|
|
53431
53793
|
}
|
|
@@ -53480,6 +53842,7 @@ class SshExternalTmuxConnection {
|
|
|
53480
53842
|
this.parseSnapshotSession(sessionRes.stdout.split(/\r?\n/));
|
|
53481
53843
|
this.parseSnapshotWindows(windowsRes.stdout.split(/\r?\n/));
|
|
53482
53844
|
this.parseSnapshotPanes(panesRes.stdout.split(/\r?\n/));
|
|
53845
|
+
await this.syncPipeReaders();
|
|
53483
53846
|
this.emitSnapshot();
|
|
53484
53847
|
}
|
|
53485
53848
|
parseSnapshotSession(lines) {
|
|
@@ -53577,56 +53940,114 @@ class SshExternalTmuxConnection {
|
|
|
53577
53940
|
}
|
|
53578
53941
|
return null;
|
|
53579
53942
|
}
|
|
53580
|
-
|
|
53581
|
-
|
|
53582
|
-
|
|
53583
|
-
|
|
53943
|
+
recordBell(paneId, windowId) {
|
|
53944
|
+
const key = paneId || windowId || "-";
|
|
53945
|
+
const previous = this.bellDedup.get(key) ?? 0;
|
|
53946
|
+
const now = Date.now();
|
|
53947
|
+
if (now - previous < BELL_DEDUP_WINDOW_MS2) {
|
|
53948
|
+
return;
|
|
53949
|
+
}
|
|
53950
|
+
this.bellDedup.set(key, now);
|
|
53951
|
+
this.callbacks.onEvent({
|
|
53952
|
+
type: "bell",
|
|
53953
|
+
data: {
|
|
53954
|
+
windowId,
|
|
53955
|
+
paneId: paneId || this.activePaneId || undefined
|
|
53956
|
+
}
|
|
53957
|
+
});
|
|
53958
|
+
}
|
|
53959
|
+
emitNotification(paneId, notification) {
|
|
53960
|
+
this.callbacks.onEvent({
|
|
53961
|
+
type: "notification",
|
|
53962
|
+
data: {
|
|
53963
|
+
paneId,
|
|
53964
|
+
...notification
|
|
53584
53965
|
}
|
|
53585
|
-
|
|
53586
|
-
|
|
53587
|
-
|
|
53588
|
-
|
|
53589
|
-
|
|
53590
|
-
|
|
53591
|
-
|
|
53592
|
-
|
|
53966
|
+
});
|
|
53967
|
+
}
|
|
53968
|
+
getExpectedPaneIds() {
|
|
53969
|
+
return Array.from(this.snapshotWindows.values()).sort((left, right) => left.index - right.index).flatMap((window2) => window2.panes.map((pane) => pane.id));
|
|
53970
|
+
}
|
|
53971
|
+
async startPipeForPaneNow(paneId) {
|
|
53972
|
+
if (this.paneReaders.has(paneId)) {
|
|
53973
|
+
return;
|
|
53974
|
+
}
|
|
53975
|
+
const fifoPath = this.fsPaths.paneFifoPath(paneId);
|
|
53976
|
+
await this.ensureRemoteRuntimeDirs();
|
|
53977
|
+
await this.runShell(`rm -f ${quoteShellArg(fifoPath)} && mkfifo ${quoteShellArg(fifoPath)} && chmod 600 ${quoteShellArg(fifoPath)}`);
|
|
53978
|
+
const parser = createPaneStreamParser({
|
|
53979
|
+
onTitle: (title) => {
|
|
53980
|
+
this.pendingPaneTitles.set(paneId, title);
|
|
53981
|
+
this.requestSnapshot();
|
|
53982
|
+
},
|
|
53983
|
+
onBell: () => {
|
|
53984
|
+
this.recordBell(paneId);
|
|
53985
|
+
},
|
|
53986
|
+
onNotification: (notification) => {
|
|
53987
|
+
this.emitNotification(paneId, notification);
|
|
53988
|
+
}
|
|
53989
|
+
});
|
|
53990
|
+
const stopReader = await this.openReaderChannel(`exec cat ${quoteShellArg(fifoPath)}`, {
|
|
53991
|
+
onData: (raw) => {
|
|
53992
|
+
const output = parser.push(raw);
|
|
53993
|
+
if (output.length > 0) {
|
|
53994
|
+
this.callbacks.onTerminalOutput(paneId, output);
|
|
53593
53995
|
}
|
|
53594
|
-
}
|
|
53595
|
-
|
|
53596
|
-
|
|
53597
|
-
|
|
53598
|
-
if (Array.from(raw).includes(7)) {
|
|
53599
|
-
this.callbacks.onEvent({ type: "bell", data: { paneId } });
|
|
53600
|
-
}
|
|
53601
|
-
if (output.length > 0) {
|
|
53602
|
-
this.callbacks.onTerminalOutput(paneId, output);
|
|
53603
|
-
}
|
|
53604
|
-
},
|
|
53605
|
-
onClose: () => {
|
|
53606
|
-
if (!this.manualDisconnect && this.currentPipePaneId === paneId) {
|
|
53607
|
-
this.callbacks.onError(new Error(`SSH pane reader closed unexpectedly: ${paneId}`));
|
|
53608
|
-
}
|
|
53996
|
+
},
|
|
53997
|
+
onClose: () => {
|
|
53998
|
+
if (!this.manualDisconnect && this.paneReaders.has(paneId)) {
|
|
53999
|
+
this.callbacks.onError(new Error(`SSH pane reader closed unexpectedly: ${paneId}`));
|
|
53609
54000
|
}
|
|
53610
|
-
}
|
|
53611
|
-
|
|
54001
|
+
}
|
|
54002
|
+
});
|
|
54003
|
+
const handle = {
|
|
54004
|
+
paneId,
|
|
54005
|
+
fifoPath,
|
|
54006
|
+
stopReader: () => {
|
|
53612
54007
|
stopReader();
|
|
53613
54008
|
this.runShellAllowFailure(`rm -f ${quoteShellArg(fifoPath)}`);
|
|
53614
|
-
}
|
|
54009
|
+
}
|
|
54010
|
+
};
|
|
54011
|
+
this.paneReaders.set(paneId, handle);
|
|
54012
|
+
try {
|
|
53615
54013
|
await this.runTmux(["pipe-pane", "-O", "-t", paneId, `cat >${fifoPath}`]);
|
|
53616
|
-
|
|
53617
|
-
|
|
53618
|
-
|
|
53619
|
-
|
|
53620
|
-
|
|
54014
|
+
} catch (error) {
|
|
54015
|
+
this.paneReaders.delete(paneId);
|
|
54016
|
+
handle.stopReader();
|
|
54017
|
+
throw error;
|
|
54018
|
+
}
|
|
53621
54019
|
}
|
|
53622
|
-
async
|
|
53623
|
-
const
|
|
53624
|
-
|
|
53625
|
-
|
|
53626
|
-
await this.runTmuxAllowFailure(["pipe-pane", "-t", paneId]);
|
|
54020
|
+
async stopPipeForPaneNow(paneId) {
|
|
54021
|
+
const handle = this.paneReaders.get(paneId);
|
|
54022
|
+
if (!handle) {
|
|
54023
|
+
return;
|
|
53627
54024
|
}
|
|
53628
|
-
this.
|
|
53629
|
-
this.
|
|
54025
|
+
this.paneReaders.delete(paneId);
|
|
54026
|
+
await this.runTmuxAllowFailure(["pipe-pane", "-t", paneId]);
|
|
54027
|
+
handle.stopReader();
|
|
54028
|
+
}
|
|
54029
|
+
async syncPipeReaders() {
|
|
54030
|
+
const expectedPaneIds = this.getExpectedPaneIds();
|
|
54031
|
+
const expectedSet = new Set(expectedPaneIds);
|
|
54032
|
+
await this.queuePipeTransition(async () => {
|
|
54033
|
+
for (const paneId of Array.from(this.paneReaders.keys())) {
|
|
54034
|
+
if (!expectedSet.has(paneId)) {
|
|
54035
|
+
await this.stopPipeForPaneNow(paneId);
|
|
54036
|
+
}
|
|
54037
|
+
}
|
|
54038
|
+
for (const paneId of expectedPaneIds) {
|
|
54039
|
+
if (!this.paneReaders.has(paneId)) {
|
|
54040
|
+
await this.startPipeForPaneNow(paneId);
|
|
54041
|
+
}
|
|
54042
|
+
}
|
|
54043
|
+
});
|
|
54044
|
+
}
|
|
54045
|
+
async stopAllPipeReaders() {
|
|
54046
|
+
await this.queuePipeTransition(async () => {
|
|
54047
|
+
for (const paneId of Array.from(this.paneReaders.keys())) {
|
|
54048
|
+
await this.stopPipeForPaneNow(paneId);
|
|
54049
|
+
}
|
|
54050
|
+
});
|
|
53630
54051
|
}
|
|
53631
54052
|
queuePipeTransition(task) {
|
|
53632
54053
|
const next = this.pipeTransition.catch(() => {
|
|
@@ -53796,7 +54217,7 @@ printf '\\036TMEX_END %s %d\\036\\n' ${quoteShellArg(commandId)} $?
|
|
|
53796
54217
|
}
|
|
53797
54218
|
this.connected = false;
|
|
53798
54219
|
this.cleanupPromise = (async () => {
|
|
53799
|
-
await this.
|
|
54220
|
+
await this.stopAllPipeReaders().catch(() => {
|
|
53800
54221
|
return;
|
|
53801
54222
|
});
|
|
53802
54223
|
await this.stopHooks().catch(() => {
|
|
@@ -54025,7 +54446,7 @@ function pickPaneById(windows, paneId) {
|
|
|
54025
54446
|
}
|
|
54026
54447
|
return null;
|
|
54027
54448
|
}
|
|
54028
|
-
function
|
|
54449
|
+
function resolvePaneContext(options) {
|
|
54029
54450
|
const { deviceId, snapshot, rawData } = options;
|
|
54030
54451
|
const raw = rawData ?? {};
|
|
54031
54452
|
const bellWindowId = typeof raw.windowId === "string" && raw.windowId ? raw.windowId : undefined;
|
|
@@ -54100,6 +54521,34 @@ var defaultDeps = {
|
|
|
54100
54521
|
}
|
|
54101
54522
|
});
|
|
54102
54523
|
},
|
|
54524
|
+
async notifyNotification(context) {
|
|
54525
|
+
const { device, settings, notification } = context;
|
|
54526
|
+
await eventNotifier.notify("terminal_notification", {
|
|
54527
|
+
site: {
|
|
54528
|
+
name: settings.siteName,
|
|
54529
|
+
url: settings.siteUrl
|
|
54530
|
+
},
|
|
54531
|
+
device: {
|
|
54532
|
+
id: device.id,
|
|
54533
|
+
name: device.name,
|
|
54534
|
+
type: device.type,
|
|
54535
|
+
host: device.host
|
|
54536
|
+
},
|
|
54537
|
+
tmux: {
|
|
54538
|
+
sessionName: device.session,
|
|
54539
|
+
windowId: notification.windowId,
|
|
54540
|
+
paneId: notification.paneId,
|
|
54541
|
+
windowIndex: notification.windowIndex,
|
|
54542
|
+
paneIndex: notification.paneIndex,
|
|
54543
|
+
paneUrl: notification.paneUrl
|
|
54544
|
+
},
|
|
54545
|
+
payload: {
|
|
54546
|
+
source: notification.source,
|
|
54547
|
+
title: notification.title,
|
|
54548
|
+
message: notification.body
|
|
54549
|
+
}
|
|
54550
|
+
});
|
|
54551
|
+
},
|
|
54103
54552
|
fallbackReconnectDelayMs: 60000
|
|
54104
54553
|
};
|
|
54105
54554
|
|
|
@@ -54294,25 +54743,44 @@ class PushSupervisor {
|
|
|
54294
54743
|
if (!entry || entry.generation !== generation || entry.runtime !== runtime) {
|
|
54295
54744
|
return;
|
|
54296
54745
|
}
|
|
54297
|
-
if (event.type !== "bell") {
|
|
54298
|
-
return;
|
|
54299
|
-
}
|
|
54300
54746
|
const device = this.deps.getDevice(deviceId);
|
|
54301
54747
|
if (!device) {
|
|
54302
54748
|
return;
|
|
54303
54749
|
}
|
|
54304
54750
|
const settings = this.deps.getSettings();
|
|
54305
|
-
const
|
|
54751
|
+
const paneContext = resolvePaneContext({
|
|
54306
54752
|
deviceId,
|
|
54307
54753
|
siteUrl: settings.siteUrl,
|
|
54308
54754
|
snapshot: entry.lastSnapshot,
|
|
54309
54755
|
rawData: event.data
|
|
54310
54756
|
});
|
|
54311
|
-
|
|
54312
|
-
|
|
54313
|
-
|
|
54314
|
-
|
|
54315
|
-
|
|
54757
|
+
if (event.type === "bell") {
|
|
54758
|
+
await this.deps.notifyBell({
|
|
54759
|
+
device,
|
|
54760
|
+
settings,
|
|
54761
|
+
bell: paneContext
|
|
54762
|
+
});
|
|
54763
|
+
return;
|
|
54764
|
+
}
|
|
54765
|
+
if (event.type === "notification") {
|
|
54766
|
+
const raw = event.data ?? {};
|
|
54767
|
+
const title = typeof raw.title === "string" && raw.title ? raw.title : undefined;
|
|
54768
|
+
const body = typeof raw.body === "string" ? raw.body : "";
|
|
54769
|
+
if (!title && !body) {
|
|
54770
|
+
return;
|
|
54771
|
+
}
|
|
54772
|
+
const source = raw.source === "osc9" || raw.source === "osc777" || raw.source === "osc1337" ? raw.source : "osc9";
|
|
54773
|
+
await this.deps.notifyNotification({
|
|
54774
|
+
device,
|
|
54775
|
+
settings,
|
|
54776
|
+
notification: {
|
|
54777
|
+
...paneContext,
|
|
54778
|
+
source,
|
|
54779
|
+
title,
|
|
54780
|
+
body
|
|
54781
|
+
}
|
|
54782
|
+
});
|
|
54783
|
+
}
|
|
54316
54784
|
}
|
|
54317
54785
|
}
|
|
54318
54786
|
var pushSupervisor = new PushSupervisor;
|
|
@@ -54501,18 +54969,37 @@ function normalizeSiteSettingsInput(body) {
|
|
|
54501
54969
|
}
|
|
54502
54970
|
updates.bellThrottleSeconds = value;
|
|
54503
54971
|
}
|
|
54972
|
+
if (body.notificationThrottleSeconds !== undefined) {
|
|
54973
|
+
const value = Math.floor(Number(body.notificationThrottleSeconds));
|
|
54974
|
+
if (Number.isNaN(value) || value < 0 || value > 300) {
|
|
54975
|
+
throw new Error(t2("apiError.bellThrottleInvalid"));
|
|
54976
|
+
}
|
|
54977
|
+
updates.notificationThrottleSeconds = value;
|
|
54978
|
+
}
|
|
54504
54979
|
if (body.enableBrowserBellToast !== undefined) {
|
|
54505
54980
|
if (typeof body.enableBrowserBellToast !== "boolean") {
|
|
54506
54981
|
throw new Error(t2("apiError.invalidRequest"));
|
|
54507
54982
|
}
|
|
54508
54983
|
updates.enableBrowserBellToast = body.enableBrowserBellToast;
|
|
54509
54984
|
}
|
|
54985
|
+
if (body.enableBrowserNotificationToast !== undefined) {
|
|
54986
|
+
if (typeof body.enableBrowserNotificationToast !== "boolean") {
|
|
54987
|
+
throw new Error(t2("apiError.invalidRequest"));
|
|
54988
|
+
}
|
|
54989
|
+
updates.enableBrowserNotificationToast = body.enableBrowserNotificationToast;
|
|
54990
|
+
}
|
|
54510
54991
|
if (body.enableTelegramBellPush !== undefined) {
|
|
54511
54992
|
if (typeof body.enableTelegramBellPush !== "boolean") {
|
|
54512
54993
|
throw new Error(t2("apiError.invalidRequest"));
|
|
54513
54994
|
}
|
|
54514
54995
|
updates.enableTelegramBellPush = body.enableTelegramBellPush;
|
|
54515
54996
|
}
|
|
54997
|
+
if (body.enableTelegramNotificationPush !== undefined) {
|
|
54998
|
+
if (typeof body.enableTelegramNotificationPush !== "boolean") {
|
|
54999
|
+
throw new Error(t2("apiError.invalidRequest"));
|
|
55000
|
+
}
|
|
55001
|
+
updates.enableTelegramNotificationPush = body.enableTelegramNotificationPush;
|
|
55002
|
+
}
|
|
54516
55003
|
if (body.sshReconnectMaxRetries !== undefined) {
|
|
54517
55004
|
const value = Math.floor(Number(body.sshReconnectMaxRetries));
|
|
54518
55005
|
if (Number.isNaN(value) || value < 0 || value > 20) {
|
|
@@ -55018,7 +55505,8 @@ class SessionStateStore {
|
|
|
55018
55505
|
deviceConnections: new Map,
|
|
55019
55506
|
selectTransactions: new Map,
|
|
55020
55507
|
outputGates: new Map,
|
|
55021
|
-
bellThrottles: new Map
|
|
55508
|
+
bellThrottles: new Map,
|
|
55509
|
+
notificationThrottles: new Map
|
|
55022
55510
|
};
|
|
55023
55511
|
this.states.set(ws, state);
|
|
55024
55512
|
return state;
|
|
@@ -55248,6 +55736,28 @@ class SessionStateStore {
|
|
|
55248
55736
|
ctx.throttleSeconds = throttleSeconds;
|
|
55249
55737
|
return true;
|
|
55250
55738
|
}
|
|
55739
|
+
shouldAllowNotification(ws, deviceId, paneId, source, throttleSeconds) {
|
|
55740
|
+
const state = this.states.get(ws);
|
|
55741
|
+
if (!state)
|
|
55742
|
+
return false;
|
|
55743
|
+
const key = `${deviceId}:${paneId}:${source}`;
|
|
55744
|
+
const now = Date.now();
|
|
55745
|
+
let ctx = state.notificationThrottles.get(key);
|
|
55746
|
+
if (!ctx) {
|
|
55747
|
+
ctx = {
|
|
55748
|
+
lastBellAt: 0,
|
|
55749
|
+
throttleSeconds
|
|
55750
|
+
};
|
|
55751
|
+
state.notificationThrottles.set(key, ctx);
|
|
55752
|
+
}
|
|
55753
|
+
const throttleMs = throttleSeconds * 1000;
|
|
55754
|
+
if (now - ctx.lastBellAt < throttleMs) {
|
|
55755
|
+
return false;
|
|
55756
|
+
}
|
|
55757
|
+
ctx.lastBellAt = now;
|
|
55758
|
+
ctx.throttleSeconds = throttleSeconds;
|
|
55759
|
+
return true;
|
|
55760
|
+
}
|
|
55251
55761
|
cleanupDevice(ws, deviceId) {
|
|
55252
55762
|
const state = this.states.get(ws);
|
|
55253
55763
|
if (!state)
|
|
@@ -55260,6 +55770,11 @@ class SessionStateStore {
|
|
|
55260
55770
|
state.bellThrottles.delete(key);
|
|
55261
55771
|
}
|
|
55262
55772
|
}
|
|
55773
|
+
for (const key of state.notificationThrottles.keys()) {
|
|
55774
|
+
if (key.startsWith(`${deviceId}:`)) {
|
|
55775
|
+
state.notificationThrottles.delete(key);
|
|
55776
|
+
}
|
|
55777
|
+
}
|
|
55263
55778
|
}
|
|
55264
55779
|
cleanup(ws) {
|
|
55265
55780
|
this.states.delete(ws);
|
|
@@ -56028,13 +56543,21 @@ class WebSocketServer {
|
|
|
56028
56543
|
return;
|
|
56029
56544
|
this.scheduleSnapshot(deviceId);
|
|
56030
56545
|
const extendedEvent = await this.extendTmuxEvent(deviceId, event);
|
|
56546
|
+
const settings = getSiteSettings();
|
|
56547
|
+
if (extendedEvent.type === "notification") {
|
|
56548
|
+
const data = extendedEvent.data ?? {};
|
|
56549
|
+
const title = typeof data.title === "string" && data.title ? data.title : "";
|
|
56550
|
+
const body = typeof data.body === "string" ? data.body : "";
|
|
56551
|
+
if (!title && !body) {
|
|
56552
|
+
return;
|
|
56553
|
+
}
|
|
56554
|
+
}
|
|
56031
56555
|
const payloadBytes = exports_ws_borsh.encodeTmuxEventPayload({
|
|
56032
56556
|
deviceId,
|
|
56033
56557
|
type: extendedEvent.type,
|
|
56034
56558
|
data: extendedEvent.data
|
|
56035
56559
|
});
|
|
56036
56560
|
if (extendedEvent.type === "bell") {
|
|
56037
|
-
const settings = getSiteSettings();
|
|
56038
56561
|
const data = extendedEvent.data ?? {};
|
|
56039
56562
|
const paneId = typeof data.paneId === "string" && data.paneId ? data.paneId : "-";
|
|
56040
56563
|
for (const client of entry.clients) {
|
|
@@ -56045,25 +56568,52 @@ class WebSocketServer {
|
|
|
56045
56568
|
}
|
|
56046
56569
|
return;
|
|
56047
56570
|
}
|
|
56571
|
+
if (extendedEvent.type === "notification") {
|
|
56572
|
+
const data = extendedEvent.data ?? {};
|
|
56573
|
+
const paneId = typeof data.paneId === "string" && data.paneId ? data.paneId : "-";
|
|
56574
|
+
const source = typeof data.source === "string" && data.source ? data.source : "osc9";
|
|
56575
|
+
for (const client of entry.clients) {
|
|
56576
|
+
if (!sessionStateStore.shouldAllowNotification(client, deviceId, paneId, source, settings.notificationThrottleSeconds)) {
|
|
56577
|
+
continue;
|
|
56578
|
+
}
|
|
56579
|
+
this.sendEnvelope(client, exports_ws_borsh.KIND_TMUX_EVENT, payloadBytes);
|
|
56580
|
+
}
|
|
56581
|
+
return;
|
|
56582
|
+
}
|
|
56048
56583
|
for (const client of entry.clients) {
|
|
56049
56584
|
this.sendEnvelope(client, exports_ws_borsh.KIND_TMUX_EVENT, payloadBytes);
|
|
56050
56585
|
}
|
|
56051
56586
|
}
|
|
56052
56587
|
async extendTmuxEvent(deviceId, event) {
|
|
56053
|
-
if (event.type !== "bell") {
|
|
56588
|
+
if (event.type !== "bell" && event.type !== "notification") {
|
|
56054
56589
|
return event;
|
|
56055
56590
|
}
|
|
56056
56591
|
const settings = getSiteSettings();
|
|
56057
56592
|
const snapshot = this.connections.get(deviceId)?.lastSnapshot ?? null;
|
|
56058
|
-
const
|
|
56593
|
+
const paneContext = resolvePaneContext({
|
|
56059
56594
|
deviceId,
|
|
56060
56595
|
siteUrl: settings.siteUrl,
|
|
56061
56596
|
snapshot,
|
|
56062
56597
|
rawData: event.data
|
|
56063
56598
|
});
|
|
56599
|
+
if (event.type === "bell") {
|
|
56600
|
+
return {
|
|
56601
|
+
type: "bell",
|
|
56602
|
+
data: paneContext
|
|
56603
|
+
};
|
|
56604
|
+
}
|
|
56605
|
+
const raw = event.data ?? {};
|
|
56606
|
+
const source = raw.source === "osc9" || raw.source === "osc777" || raw.source === "osc1337" ? raw.source : "osc9";
|
|
56607
|
+
const title = typeof raw.title === "string" && raw.title ? raw.title : undefined;
|
|
56608
|
+
const body = typeof raw.body === "string" ? raw.body : "";
|
|
56064
56609
|
return {
|
|
56065
|
-
type: "
|
|
56066
|
-
data
|
|
56610
|
+
type: "notification",
|
|
56611
|
+
data: {
|
|
56612
|
+
...paneContext,
|
|
56613
|
+
source,
|
|
56614
|
+
title,
|
|
56615
|
+
body
|
|
56616
|
+
}
|
|
56067
56617
|
};
|
|
56068
56618
|
}
|
|
56069
56619
|
broadcastStateSnapshot(deviceId, payload) {
|