herdr-remote 0.2.39 → 0.3.32
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/README.md +4 -2
- package/README.zh-CN.md +2 -2
- package/dist/chunks/3FQAXVGC.js +3 -0
- package/dist/chunks/42KARAL4.js +31 -0
- package/dist/chunks/44IGVPMA.js +5 -0
- package/dist/chunks/5LZFJ6UZ.js +3 -0
- package/dist/chunks/7SMZ43LI.js +9 -0
- package/dist/chunks/AX4EPJCP.js +4 -0
- package/dist/chunks/B76DFI4H.js +16 -0
- package/dist/chunks/BYGDPZZD.js +3 -0
- package/dist/chunks/C2UUZY6J.js +2 -0
- package/dist/chunks/CVJ4HBMP.js +3 -0
- package/dist/chunks/DGUZCGQN.js +3 -0
- package/dist/chunks/FONUO5O5.js +4 -0
- package/dist/chunks/H24ZP5D5.js +16 -0
- package/dist/chunks/I6NRCYLI.js +3 -0
- package/dist/chunks/JJGEV4EZ.js +3 -0
- package/dist/chunks/K5RFPHSF.js +5 -0
- package/dist/chunks/KGOQKOHH.js +5 -0
- package/dist/chunks/KRVF37OW.js +3 -0
- package/dist/chunks/KUDCC7ZO.js +6 -0
- package/dist/chunks/LPR662JH.js +77 -0
- package/dist/chunks/LY2ZGCXN.js +4 -0
- package/dist/chunks/M52LJEES.js +6 -0
- package/dist/chunks/MCUVFSSK.js +3 -0
- package/dist/chunks/MEFDV375.js +10 -0
- package/dist/chunks/MKAEXKXG.js +10 -0
- package/dist/chunks/O2LNVFUT.js +3 -0
- package/dist/chunks/PBMBFWLQ.js +3 -0
- package/dist/chunks/PBRQ3HEG.js +3 -0
- package/dist/chunks/QCSILMFB.js +3 -0
- package/dist/chunks/QRGNEKME.js +5 -0
- package/dist/chunks/RMHH3IAX.js +63 -0
- package/dist/chunks/TNLCNVA2.js +3 -0
- package/dist/chunks/U6QAYG3E.js +3 -0
- package/dist/chunks/VD54DF7R.js +45 -0
- package/dist/chunks/WY7WHEJ4.js +3 -0
- package/dist/cli.js +21 -294
- package/dist/connector/admin-broker.js +3 -303
- package/dist/connector/main.js +20 -18
- package/dist/harfbuzz-subset.wasm +0 -0
- package/dist/postinstall.js +17 -0
- package/herdr-plugin.toml +1 -1
- package/node_modules/herdr-remote-relay/bin/herdr-remote-relay.js +33 -92
- package/node_modules/herdr-remote-relay/package.json +3 -46
- package/package.json +8 -27
- package/dist/agent-status.js +0 -130
- package/dist/binary-plist.js +0 -157
- package/dist/config.js +0 -278
- package/dist/connector/admin-broker-setup.js +0 -40
- package/dist/connector/admin-shell.js +0 -67
- package/dist/connector/admin-tab.js +0 -90
- package/dist/connector/adminBrokerEndpoint.js +0 -68
- package/dist/connector/agent-watch.js +0 -123
- package/dist/connector/elevatedPty.js +0 -141
- package/dist/connector/font-server.js +0 -165
- package/dist/connector/herdr-prefix-keys.js +0 -28
- package/dist/connector/host-connector.js +0 -433
- package/dist/connector/host-hello.js +0 -32
- package/dist/connector/lock.js +0 -73
- package/dist/connector/sessions.js +0 -381
- package/dist/connector/update-report.js +0 -70
- package/dist/connector/wire.js +0 -42
- package/dist/exit-codes.js +0 -13
- package/dist/font-subset.js +0 -116
- package/dist/herdr-api.js +0 -254
- package/dist/herdr-command.js +0 -245
- package/dist/herdr-plugin.js +0 -120
- package/dist/herdr-server.js +0 -190
- package/dist/i18n/en.js +0 -257
- package/dist/i18n/index.js +0 -48
- package/dist/i18n/zh.js +0 -256
- package/dist/keepalive/detached.js +0 -97
- package/dist/keepalive/environment.js +0 -44
- package/dist/keepalive/index.js +0 -129
- package/dist/keepalive/launchd.js +0 -117
- package/dist/keepalive/systemd.js +0 -112
- package/dist/keepalive/types.js +0 -1
- package/dist/keepalive/windows-service-setup.js +0 -138
- package/dist/keepalive/windows-service.js +0 -272
- package/dist/keepalive/windows-task.js +0 -191
- package/dist/lib/process.js +0 -132
- package/dist/lifecycle.js +0 -79
- package/dist/net-interfaces.js +0 -82
- package/dist/node-pty-loader.js +0 -80
- package/dist/pasted-files.js +0 -115
- package/dist/paths.js +0 -74
- package/dist/pty-session.js +0 -133
- package/dist/relay-client.js +0 -106
- package/dist/relay-urls.js +0 -99
- package/dist/runtime.js +0 -107
- package/dist/service.js +0 -267
- package/dist/settings-model.js +0 -298
- package/dist/socket-discovery.js +0 -70
- package/dist/supervisor.js +0 -323
- package/dist/terminal-font/deps.js +0 -83
- package/dist/terminal-font/files.js +0 -357
- package/dist/terminal-font/formats.js +0 -355
- package/dist/terminal-font/index.js +0 -187
- package/dist/terminal-font/readers.js +0 -373
- package/dist/terminal-font/terminals.js +0 -147
- package/dist/terminal-palette.js +0 -399
- package/dist/tui/App.js +0 -199
- package/dist/tui/api.js +0 -91
- package/dist/tui/components/TextField.js +0 -50
- package/dist/tui/components/common.js +0 -80
- package/dist/tui/index.js +0 -36
- package/dist/tui/mouse/index.js +0 -164
- package/dist/tui/mouse/protocol.js +0 -96
- package/dist/tui/mouse/source.js +0 -79
- package/dist/tui/screens/About.js +0 -172
- package/dist/tui/screens/Herdr.js +0 -75
- package/dist/tui/screens/Keepalive.js +0 -141
- package/dist/tui/screens/Overview.js +0 -36
- package/dist/tui/screens/Pair.js +0 -74
- package/dist/tui/screens/Relay.js +0 -243
- package/dist/tui/screens/Services.js +0 -63
- package/dist/tui/screens/Wizard.js +0 -241
- package/dist/tui/terminal.js +0 -39
- package/dist/tui/theme.js +0 -33
- package/dist/update-command.js +0 -118
- package/dist/updater/in-place.js +0 -83
- package/dist/updater/npm.js +0 -62
- package/dist/updater.js +0 -401
- package/node_modules/herdr-remote-relay/README.md +0 -76
- package/node_modules/herdr-remote-relay/README.zh-CN.md +0 -76
- package/node_modules/herdr-remote-relay/config.example.json +0 -18
- package/node_modules/herdr-remote-relay/deploy/cloudflared/config.yml.example +0 -9
- package/node_modules/herdr-remote-relay/deploy/docker/compose.yaml +0 -23
- package/node_modules/herdr-remote-relay/deploy/nginx/herdr-remote-relay.conf.example +0 -49
- package/node_modules/herdr-remote-relay/deploy/systemd/herdr-remote-relay.service +0 -41
- package/node_modules/herdr-remote-relay/deploy/systemd/relay.env.example +0 -25
- package/node_modules/herdr-remote-relay/dist/auth-store.d.ts +0 -114
- package/node_modules/herdr-remote-relay/dist/auth-store.js +0 -289
- package/node_modules/herdr-remote-relay/dist/metrics.d.ts +0 -76
- package/node_modules/herdr-remote-relay/dist/metrics.js +0 -249
- package/node_modules/herdr-remote-relay/dist/protocol/frames.d.ts +0 -27
- package/node_modules/herdr-remote-relay/dist/protocol/frames.js +0 -110
- package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.d.ts +0 -8
- package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.js +0 -34
- package/node_modules/herdr-remote-relay/dist/protocol/http.d.ts +0 -165
- package/node_modules/herdr-remote-relay/dist/protocol/http.js +0 -3
- package/node_modules/herdr-remote-relay/dist/protocol/index.d.ts +0 -7
- package/node_modules/herdr-remote-relay/dist/protocol/index.js +0 -9
- package/node_modules/herdr-remote-relay/dist/protocol/messages.d.ts +0 -362
- package/node_modules/herdr-remote-relay/dist/protocol/messages.js +0 -20
- package/node_modules/herdr-remote-relay/dist/protocol/paste.d.ts +0 -14
- package/node_modules/herdr-remote-relay/dist/protocol/paste.js +0 -50
- package/node_modules/herdr-remote-relay/dist/protocol/release.d.ts +0 -8
- package/node_modules/herdr-remote-relay/dist/protocol/release.js +0 -17
- package/node_modules/herdr-remote-relay/dist/protocol/terminal.d.ts +0 -79
- package/node_modules/herdr-remote-relay/dist/protocol/terminal.js +0 -150
- package/node_modules/herdr-remote-relay/dist/relay-config.d.ts +0 -74
- package/node_modules/herdr-remote-relay/dist/relay-config.js +0 -316
- package/node_modules/herdr-remote-relay/dist/relay-server.d.ts +0 -67
- package/node_modules/herdr-remote-relay/dist/relay-server.js +0 -196
- package/node_modules/herdr-remote-relay/dist/scroll-input.d.ts +0 -17
- package/node_modules/herdr-remote-relay/dist/scroll-input.js +0 -124
- package/node_modules/herdr-remote-relay/dist/server/client-channel.d.ts +0 -3
- package/node_modules/herdr-remote-relay/dist/server/client-channel.js +0 -396
- package/node_modules/herdr-remote-relay/dist/server/font-proxy.d.ts +0 -19
- package/node_modules/herdr-remote-relay/dist/server/font-proxy.js +0 -155
- package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.d.ts +0 -27
- package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.js +0 -83
- package/node_modules/herdr-remote-relay/dist/server/host-channel.d.ts +0 -3
- package/node_modules/herdr-remote-relay/dist/server/host-channel.js +0 -271
- package/node_modules/herdr-remote-relay/dist/server/http-api.d.ts +0 -4
- package/node_modules/herdr-remote-relay/dist/server/http-api.js +0 -219
- package/node_modules/herdr-remote-relay/dist/server/maintenance.d.ts +0 -5
- package/node_modules/herdr-remote-relay/dist/server/maintenance.js +0 -64
- package/node_modules/herdr-remote-relay/dist/server/requests.d.ts +0 -34
- package/node_modules/herdr-remote-relay/dist/server/requests.js +0 -142
- package/node_modules/herdr-remote-relay/dist/server/sessions.d.ts +0 -36
- package/node_modules/herdr-remote-relay/dist/server/sessions.js +0 -230
- package/node_modules/herdr-remote-relay/dist/server/sockets.d.ts +0 -12
- package/node_modules/herdr-remote-relay/dist/server/sockets.js +0 -64
- package/node_modules/herdr-remote-relay/dist/server/status.d.ts +0 -11
- package/node_modules/herdr-remote-relay/dist/server/status.js +0 -99
- package/node_modules/herdr-remote-relay/dist/server/transport.d.ts +0 -16
- package/node_modules/herdr-remote-relay/dist/server/transport.js +0 -110
- package/node_modules/herdr-remote-relay/dist/server/types.d.ts +0 -133
- package/node_modules/herdr-remote-relay/dist/server/types.js +0 -3
- package/node_modules/herdr-remote-relay/dist/server/version.d.ts +0 -2
- package/node_modules/herdr-remote-relay/dist/server/version.js +0 -5
- package/node_modules/herdr-remote-relay/dist/state.d.ts +0 -6
- package/node_modules/herdr-remote-relay/dist/state.js +0 -47
- package/node_modules/ws/LICENSE +0 -20
- package/node_modules/ws/README.md +0 -548
- package/node_modules/ws/browser.js +0 -8
- package/node_modules/ws/index.js +0 -22
- package/node_modules/ws/lib/buffer-util.js +0 -131
- package/node_modules/ws/lib/constants.js +0 -19
- package/node_modules/ws/lib/event-target.js +0 -292
- package/node_modules/ws/lib/extension.js +0 -203
- package/node_modules/ws/lib/limiter.js +0 -55
- package/node_modules/ws/lib/permessage-deflate.js +0 -530
- package/node_modules/ws/lib/receiver.js +0 -743
- package/node_modules/ws/lib/sender.js +0 -607
- package/node_modules/ws/lib/stream.js +0 -161
- package/node_modules/ws/lib/subprotocol.js +0 -62
- package/node_modules/ws/lib/validation.js +0 -152
- package/node_modules/ws/lib/websocket-server.js +0 -562
- package/node_modules/ws/lib/websocket.js +0 -1407
- package/node_modules/ws/package.json +0 -74
- package/node_modules/ws/wrapper.mjs +0 -21
package/dist/i18n/zh.js
DELETED
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
// 简体中文文案。键名必须与 en.ts 完全一致,由类型和 tests/i18n.test.js 校验。
|
|
2
|
-
const zh = {
|
|
3
|
-
'app.name': 'Herdr Remote',
|
|
4
|
-
'app.tagline': '在浏览器中使用 Herdr 工作区',
|
|
5
|
-
'common.yes': '是',
|
|
6
|
-
'common.no': '否',
|
|
7
|
-
'common.on': '开',
|
|
8
|
-
'common.off': '关',
|
|
9
|
-
'common.none': '无',
|
|
10
|
-
'common.auto': '自动',
|
|
11
|
-
'common.unknown': '未知',
|
|
12
|
-
'common.running': '运行中',
|
|
13
|
-
'common.stopped': '已停止',
|
|
14
|
-
'common.connected': '已连接',
|
|
15
|
-
'common.disconnected': '未连接',
|
|
16
|
-
'common.installed': '已安装',
|
|
17
|
-
'common.notInstalled': '未安装',
|
|
18
|
-
'common.enabled': '已启用',
|
|
19
|
-
'common.disabled': '已停用',
|
|
20
|
-
'common.loading': '处理中…',
|
|
21
|
-
'common.save': '保存设置',
|
|
22
|
-
'common.saved': '配置已保存至 {path}',
|
|
23
|
-
'common.back': '返回',
|
|
24
|
-
'common.cancel': '取消',
|
|
25
|
-
'common.confirm': '确认',
|
|
26
|
-
'common.pid': 'PID {pid}',
|
|
27
|
-
'common.notApplicable': '不适用',
|
|
28
|
-
'nav.overview': '概览',
|
|
29
|
-
'nav.pair': '配对设备',
|
|
30
|
-
'nav.services': '服务',
|
|
31
|
-
'nav.relay': 'Relay',
|
|
32
|
-
'nav.keepalive': '保活',
|
|
33
|
-
'nav.herdr': 'Herdr',
|
|
34
|
-
'nav.about': '语言与关于',
|
|
35
|
-
'mode.local': '仅本机',
|
|
36
|
-
'mode.lan': '局域网 / Tailscale',
|
|
37
|
-
'mode.official': '官方 Relay',
|
|
38
|
-
'mode.remote': '自建 Relay',
|
|
39
|
-
'mode.local.description': '监听 127.0.0.1,仅本机可访问。',
|
|
40
|
-
'mode.lan.description': '监听 0.0.0.0,局域网与 Tailscale 设备可访问。',
|
|
41
|
-
'mode.official.description': '使用官方 Relay 跨网访问,无需自建服务器。',
|
|
42
|
-
'mode.remote.description': '连接自建 Relay 跨网访问。',
|
|
43
|
-
'overview.title': '状态',
|
|
44
|
-
'overview.mode': '访问方式',
|
|
45
|
-
'overview.relay': 'Relay',
|
|
46
|
-
'overview.host': '主机连接器',
|
|
47
|
-
'overview.socket': 'Herdr 套接字',
|
|
48
|
-
'overview.webUrl': 'Web 界面',
|
|
49
|
-
'overview.keepalive': '保活服务',
|
|
50
|
-
'overview.devices': '浏览器',
|
|
51
|
-
'overview.hosts': '工作站',
|
|
52
|
-
'overview.uptime': 'Relay 运行时间',
|
|
53
|
-
'overview.relayLocal': '本地,{bind}:{port}',
|
|
54
|
-
'overview.relayRemote': '远程,{url}',
|
|
55
|
-
'overview.socketMissing': '未找到,请确认 Herdr 是否运行',
|
|
56
|
-
'overview.unreachable': '无法访问:{message}',
|
|
57
|
-
'overview.notStarted': '服务未运行,可前往「服务」启动。',
|
|
58
|
-
'pair.title': '配对设备',
|
|
59
|
-
'pair.generate': '生成配对码',
|
|
60
|
-
'pair.regenerate': '重新生成',
|
|
61
|
-
'pair.working': '生成配对码中…',
|
|
62
|
-
'pair.code': '配对码',
|
|
63
|
-
'pair.url': '访问地址',
|
|
64
|
-
'pair.expires': '剩余 {minutes}(至 {time})',
|
|
65
|
-
'pair.expired': '配对码已过期,需重新生成。',
|
|
66
|
-
'pair.instructions': '打开链接并输入配对码。',
|
|
67
|
-
'pair.qrHint': '扫描二维码打开链接。',
|
|
68
|
-
'pair.qrUnavailable': '终端宽度不足,无法显示二维码。',
|
|
69
|
-
'pair.failed': '生成配对码失败:{message}',
|
|
70
|
-
'pair.hostOffline': 'Relay 未连接工作站,需先启动服务。',
|
|
71
|
-
'services.title': '服务',
|
|
72
|
-
'services.start': '启动服务',
|
|
73
|
-
'services.stop': '停止服务',
|
|
74
|
-
'services.restart': '重启服务',
|
|
75
|
-
'services.started': '服务已启动。',
|
|
76
|
-
'services.stopped': '服务已停止。',
|
|
77
|
-
'services.restarted': '服务已重启。',
|
|
78
|
-
'services.startFailed': '启动服务失败:{message}',
|
|
79
|
-
'services.stopFailed': '停止服务失败:{message}',
|
|
80
|
-
'services.unsavedBlocked': '配置未保存,请先保存再重启。',
|
|
81
|
-
'services.managedNotice': '由 {manager} 托管,已通过保活服务执行。',
|
|
82
|
-
'services.logs': '最近日志',
|
|
83
|
-
'services.logEmpty': '暂无日志。',
|
|
84
|
-
'services.logRelay': 'Relay',
|
|
85
|
-
'services.logHost': '主机连接器',
|
|
86
|
-
'adminBroker.windowsOnly': '管理员终端 broker 只支持 Windows。',
|
|
87
|
-
'adminBroker.needsElevation': '请打开 Windows TUI 并选择“设置管理员终端”,使用运行 Herdr Remote 的同一账户批准一次 Windows 提权提示。',
|
|
88
|
-
'adminBroker.busy': '有管理员终端开着,请先关闭再重启 broker。',
|
|
89
|
-
'adminBroker.title': '管理员终端 broker',
|
|
90
|
-
'adminBroker.checking': '正在检查…',
|
|
91
|
-
'adminBroker.ready': '已就绪',
|
|
92
|
-
'adminBroker.notReady': '尚未设置或未运行',
|
|
93
|
-
'adminBroker.install': '设置管理员终端',
|
|
94
|
-
'adminBroker.uninstall': '移除管理员终端设置',
|
|
95
|
-
'adminBroker.installDone': '管理员终端 broker 任务已注册。',
|
|
96
|
-
'adminBroker.uninstallDone': '管理员终端 broker 任务已移除。',
|
|
97
|
-
'adminBroker.elevationCancelled': '未批准管理员权限,未进行更改。',
|
|
98
|
-
'adminBroker.setupFailed': '管理员终端 broker 设置失败。',
|
|
99
|
-
'adminBroker.tuiHint': '只需设置一次。Windows 会请求管理员授权;之后 WebUI 新建管理员终端不再弹出 UAC。',
|
|
100
|
-
'adminShell.needsTerminal': 'admin-shell 必须在终端中运行。',
|
|
101
|
-
'adminShell.brokerUnavailable': '管理员终端 broker 尚未运行。请打开 herdr-remote TUI,进入「保活」并选择「设置管理员终端」。',
|
|
102
|
-
'adminShell.tabLabel': '管理员',
|
|
103
|
-
'relay.title': 'Relay 设置',
|
|
104
|
-
'relay.settings': '设置',
|
|
105
|
-
'relay.locked': '固定',
|
|
106
|
-
'relay.officialFixed': '官方 relay 地址固定,不可修改。',
|
|
107
|
-
'relay.officialHint': '官方 relay:地址固定,无需密码。',
|
|
108
|
-
'relay.password': 'Relay 密码',
|
|
109
|
-
'relay.passwordHint': '须与 Relay 端的 RELAY_PASSWORD 一致。留空为公用 Relay。',
|
|
110
|
-
'relay.passwordSet': '已设置',
|
|
111
|
-
'relay.passwordEmpty': '未设置(公用)',
|
|
112
|
-
'relay.passwordSaved': 'Relay 密码已保存,重启服务生效。',
|
|
113
|
-
'relay.showPassword': '显示密码',
|
|
114
|
-
'relay.hidePassword': '隐藏密码',
|
|
115
|
-
'relay.identity': '工作站标识',
|
|
116
|
-
'relay.regenerate': '重新生成标识',
|
|
117
|
-
'relay.regenerated': '已生成新标识,重启后重新注册。',
|
|
118
|
-
'relay.envSnippet': 'Relay 启动命令',
|
|
119
|
-
'relay.envHint': '在 Relay 主机执行此命令。',
|
|
120
|
-
'relay.test': '测试 Relay 连接',
|
|
121
|
-
'relay.testOk': 'Relay 可访问:版本 {version},已连接 {hosts} 台工作站。',
|
|
122
|
-
'relay.testFailed': 'Relay 无法访问:{message}',
|
|
123
|
-
'relay.selectAddress': '选择公布地址',
|
|
124
|
-
'relay.listenAddress': '监听地址',
|
|
125
|
-
'relay.listenAddressHint': '服务器绑定地址;浏览器访问下方公布地址。',
|
|
126
|
-
'relay.addressTailscale': 'Tailscale',
|
|
127
|
-
'relay.addressLan': '局域网',
|
|
128
|
-
'relay.addressVirtual': '虚拟网卡',
|
|
129
|
-
'relay.addressLoopback': '回环',
|
|
130
|
-
'relay.noAddresses': '未找到非回环地址。',
|
|
131
|
-
'relay.docsHint': '自建指南:docs/self-hosted-relay.zh-CN.md',
|
|
132
|
-
'keepalive.title': '后台保活服务',
|
|
133
|
-
'keepalive.manager': '管理方式',
|
|
134
|
-
'keepalive.state': '状态',
|
|
135
|
-
'keepalive.install': '安装并启动',
|
|
136
|
-
'keepalive.uninstall': '停止并移除',
|
|
137
|
-
'keepalive.restart': '重启保活服务',
|
|
138
|
-
'keepalive.installed': '已通过 {manager} 安装保活服务。',
|
|
139
|
-
'keepalive.uninstalled': '保活服务已移除。',
|
|
140
|
-
'keepalive.restarted': '保活服务已重启。',
|
|
141
|
-
'keepalive.failed': '保活操作失败:{message}',
|
|
142
|
-
'keepalive.unitPath': '单元文件',
|
|
143
|
-
'keepalive.logsHint': '查看日志:{command}',
|
|
144
|
-
'keepalive.linger': '开机自启',
|
|
145
|
-
'keepalive.lingerEnabled': '已开启开机自启。',
|
|
146
|
-
'keepalive.lingerDisabled': '未开启开机自启(仅登录期间运行)。',
|
|
147
|
-
'keepalive.enableLinger': '开启开机自启',
|
|
148
|
-
'keepalive.lingerDone': '已为 {username} 开启开机自启。',
|
|
149
|
-
'keepalive.fallbackNote': '无系统服务管理器,使用后台守护进程,重启后不自动恢复。',
|
|
150
|
-
'keepalive.windowsTaskNote': '在你的账户登录后启动,无需密码或管理员权限;登录前不会运行。',
|
|
151
|
-
'keepalive.serviceNote': '以你的账户作为 Windows 服务运行,在登录前启动。安装或移除需要管理员权限,Windows 也可能要求账户密码。',
|
|
152
|
-
'keepalive.serviceWindowHint': '请在随后打开的管理员窗口中完成安装。',
|
|
153
|
-
'keepalive.elevationCancelled': '未授予管理员权限,未安装服务。',
|
|
154
|
-
'keepalive.passwordPrompt': '请输入 {account} 的 Windows 账户密码(不是 PIN;账户无密码时直接按 Enter):',
|
|
155
|
-
'keepalive.passwordRejected': 'Windows 未能使用这些凭据启动服务。请检查账户密码和“作为服务登录”策略。',
|
|
156
|
-
'keepalive.serviceInstalled': '服务 {id} 已安装并正在运行。',
|
|
157
|
-
'keepalive.wrongAccount': '请使用将运行 herdr-remote 的账户执行此操作,不要换用其他管理员账户。',
|
|
158
|
-
'keepalive.pressEnter': '按 Enter 关闭此窗口。',
|
|
159
|
-
'herdr.title': 'Herdr 集成',
|
|
160
|
-
'herdr.socketPath': '套接字路径',
|
|
161
|
-
'herdr.args': '附加参数',
|
|
162
|
-
'herdr.autoStartHint': '关:Herdr 只在你手动运行,或在浏览器里确认后才启动。开:herdr-remote 每次启动(包括开机)时一并启动 Herdr。下次 herdr-remote 启动时生效。',
|
|
163
|
-
'herdr.cliMissing': '未找到 herdr 命令,可将 HERDR_BIN_PATH 设为其完整路径。',
|
|
164
|
-
'herdr.version': 'Herdr 版本',
|
|
165
|
-
'herdr.versionOutdated': 'Herdr {version} 低于 {minimum},请运行 “herdr update”:在该版本之前,浏览器窗口的标题、大段粘贴、点按窗格与 Alt 快捷键都会出问题。',
|
|
166
|
-
'about.title': '语言与关于',
|
|
167
|
-
'about.language': '界面语言',
|
|
168
|
-
'about.languageAuto': '跟随系统({detected})',
|
|
169
|
-
'about.languageZh': '中文',
|
|
170
|
-
'about.languageEn': 'English',
|
|
171
|
-
'about.version': '版本',
|
|
172
|
-
'about.configPath': '配置文件',
|
|
173
|
-
'about.statePath': '状态目录',
|
|
174
|
-
'about.relayPackage': 'Relay 包',
|
|
175
|
-
'about.docs': '自建指南:docs/self-hosted-relay.zh-CN.md',
|
|
176
|
-
'wizard.title': '首次设置',
|
|
177
|
-
'wizard.step': '第 {current} / {total} 步',
|
|
178
|
-
'wizard.languageTitle': '选择语言',
|
|
179
|
-
'wizard.accessTitle': '选择访问方式',
|
|
180
|
-
'wizard.accessHint': '后续可在 Relay 页面修改。',
|
|
181
|
-
'wizard.addressTitle': '选择访问地址',
|
|
182
|
-
'wizard.relayTitle': 'Relay 服务器',
|
|
183
|
-
'wizard.relayUrlLabel': 'Relay 地址(wss://…)',
|
|
184
|
-
'wizard.relayHint': 'Relay 需先运行。搭建指南见 docs/self-hosted-relay.zh-CN.md。',
|
|
185
|
-
'wizard.passwordTitle': 'Relay 密码',
|
|
186
|
-
'wizard.passwordHint': 'Relay 的 RELAY_PASSWORD。未设留空。',
|
|
187
|
-
'wizard.finishTitle': '完成',
|
|
188
|
-
'wizard.finishHint': '配置将保存至 {path}。',
|
|
189
|
-
'wizard.startNow': '立即启动服务',
|
|
190
|
-
'wizard.installKeepalive': '后台保活',
|
|
191
|
-
'wizard.finish': '保存并完成',
|
|
192
|
-
'field.mode': '访问方式',
|
|
193
|
-
'field.port': 'Relay 端口',
|
|
194
|
-
'field.lanHost': '浏览器地址',
|
|
195
|
-
'field.remoteUrl': 'Relay 地址',
|
|
196
|
-
'field.publicUrl': '浏览器地址(覆盖)',
|
|
197
|
-
'field.socketPath': 'Herdr 套接字路径',
|
|
198
|
-
'field.herdrArgs': 'Herdr 参数',
|
|
199
|
-
'field.herdrAutoStart': '随服务启动 Herdr',
|
|
200
|
-
'field.language': '语言',
|
|
201
|
-
'field.keepalive': '保活管理方式',
|
|
202
|
-
'placeholder.autoDiscovered': '自动探测',
|
|
203
|
-
'placeholder.none': '无',
|
|
204
|
-
'error.invalidMode': '未知的访问方式。',
|
|
205
|
-
'error.invalidPort': '端口须为 1 到 65535 之间的数字。',
|
|
206
|
-
'error.invalidLanHost': '浏览器地址须为可访问局域网或 Tailscale 地址,不能为回环或 0.0.0.0。',
|
|
207
|
-
'error.invalidRelayUrl': 'Relay 地址须以 wss://、ws://、https:// 或 http:// 开头。',
|
|
208
|
-
'error.invalidPublicUrl': '浏览器地址须以 https:// 或 http:// 开头,不能为 0.0.0.0 或 ::。',
|
|
209
|
-
'error.invalidLanguage': '未知的语言。',
|
|
210
|
-
'error.invalidKeepalive': '未知的保活管理方式。',
|
|
211
|
-
'error.unknownField': '未知的设置项。',
|
|
212
|
-
'error.remoteUrlRequired': '自建 Relay 模式须填写 Relay 地址。',
|
|
213
|
-
'error.removedHerdrArg': 'Herdr 0.9.0 已移除 --no-session,请从附加参数中移除。',
|
|
214
|
-
'error.saveFailed': '保存配置失败:{message}',
|
|
215
|
-
'hint.navigate': '↑↓ 移动',
|
|
216
|
-
'hint.select': '↵ 选择',
|
|
217
|
-
'hint.edit': '↵ 编辑',
|
|
218
|
-
'hint.tabs': '← → 切换页面',
|
|
219
|
-
'hint.back': 'esc 返回',
|
|
220
|
-
'hint.quit': 'q 退出',
|
|
221
|
-
'hint.mouseOn': 'm 关闭鼠标',
|
|
222
|
-
'hint.mouseOff': 'm 开启鼠标',
|
|
223
|
-
'hint.mouseUnsupported': '不支持鼠标',
|
|
224
|
-
'hint.restartRequired': '重启服务生效。',
|
|
225
|
-
'hint.unsavedChanges': '有未保存修改(按 s 保存)。',
|
|
226
|
-
'hint.save': 's 保存',
|
|
227
|
-
'hint.editing': '↵ 确认 · esc 取消',
|
|
228
|
-
'update.title': '更新',
|
|
229
|
-
'update.banner': 'herdr-remote {latest} 已发布(当前 {current}),按 7 进入「语言与关于」,在更新一行按 Enter 安装。',
|
|
230
|
-
'update.check': '检查更新',
|
|
231
|
-
'update.checking': '查询 GitHub Releases 与 npm 中…',
|
|
232
|
-
'update.upToDate': '已是最新({version})',
|
|
233
|
-
'update.available': '新版本 {version} 可用(按 Enter 安装)',
|
|
234
|
-
'update.updating': '安装 {version} 中…',
|
|
235
|
-
'update.updatingRetry': '安装 {version} 中…(npm 尚未同步,第 {attempt} 次尝试)',
|
|
236
|
-
'update.done': '已更新至 {version}',
|
|
237
|
-
'update.restartHint': '更新已安装,重启 herdr-remote 生效。',
|
|
238
|
-
'update.errorNetwork': 'GitHub Releases 与所有 npm registry 均无法连接',
|
|
239
|
-
'update.errorNetworkDetail': '均无应答:{message}',
|
|
240
|
-
'update.errorFailed': '更新失败,可手动执行:npm install -g {url}',
|
|
241
|
-
'update.errorFailedDetail': 'npm:{message}',
|
|
242
|
-
'update.errorNotYetPublished': 'npm 还没同步到 {version}(刚发布的几分钟内常见),稍后按 Enter 重试。',
|
|
243
|
-
'update.errorNotApplied': 'npm 报告成功,但安装的仍是 {installed}',
|
|
244
|
-
'update.mirrorBehind': '{registries} 仍是旧版本,将从 {source} 安装',
|
|
245
|
-
'update.cannot.source': '源码目录运行,请使用 git 更新',
|
|
246
|
-
'update.cannot.linked': 'npm link 工作副本,由 npm link 管理',
|
|
247
|
-
'update.stoppingServices': '替换文件期间先停止服务…',
|
|
248
|
-
'update.restarting': '正在用新版本重启服务…',
|
|
249
|
-
'update.notRunning': '服务之前没有运行,保持停止。需要时执行:herdr-remote start',
|
|
250
|
-
'update.restartFailed': '已安装,但服务没能重启:{message}',
|
|
251
|
-
'update.brokerRestarted': '管理员终端 broker 已重启。',
|
|
252
|
-
'update.brokerRestartFailed': '管理员终端 broker 没有重新启动。重新登录 Windows,或运行 herdr-remote admin-broker restart。',
|
|
253
|
-
'update.brokerBusy': '有管理员终端开着,broker 继续运行旧版本。关闭所有管理员终端后运行 herdr-remote admin-broker restart。',
|
|
254
|
-
'update.errorBusy': "仍有进程占用 herdr-remote 的安装目录,旧版本设置的管理员终端 broker 会这样。先在管理员 PowerShell 中执行:Get-ScheduledTask -TaskName 'HerdrRemoteAdminBroker-*' | Stop-ScheduledTask,再回到这里重新运行 herdr-remote update,最后以管理员身份运行 herdr-remote admin-broker install。",
|
|
255
|
-
};
|
|
256
|
-
export default zh;
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
// The fallback where no service manager is usable: a detached copy of our own
|
|
2
|
-
// supervisor, tracked by a pid file. It does not survive a reboot.
|
|
3
|
-
import { spawn } from 'node:child_process';
|
|
4
|
-
import fs from 'node:fs';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { ensureDir, readJson, writeJsonAtomic } from 'herdr-remote-relay/state';
|
|
7
|
-
import { processStart, stillRunning } from '../lib/process.js';
|
|
8
|
-
import { logPath, stateDir } from '../paths.js';
|
|
9
|
-
import { cliEntryPoint, serviceEnvironment } from './environment.js';
|
|
10
|
-
function fallbackPidPath() {
|
|
11
|
-
return path.join(stateDir(), 'supervisor.pid');
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* The supervisor the pid file names, if it is still that process. The file
|
|
15
|
-
* outlives a reboot; its pid alone would then name something unrelated.
|
|
16
|
-
*/
|
|
17
|
-
function runningPid() {
|
|
18
|
-
const record = readJson(fallbackPidPath(), {});
|
|
19
|
-
return stillRunning([record]).length > 0 ? record.pid : null;
|
|
20
|
-
}
|
|
21
|
-
function start() {
|
|
22
|
-
const running = runningPid();
|
|
23
|
-
if (running !== null)
|
|
24
|
-
return { ok: true, alreadyRunning: true, pid: running };
|
|
25
|
-
ensureDir(stateDir());
|
|
26
|
-
const logFd = fs.openSync(logPath('supervisor'), 'a');
|
|
27
|
-
try {
|
|
28
|
-
const child = spawn(process.execPath, [cliEntryPoint(), 'run', '--daemon'], {
|
|
29
|
-
// Not PACKAGE_ROOT: npm renames it to update, which Windows refuses for a working directory.
|
|
30
|
-
cwd: stateDir(),
|
|
31
|
-
env: { ...process.env, HERDR_REMOTE_SERVICE: '1', ...serviceEnvironment() },
|
|
32
|
-
detached: true,
|
|
33
|
-
windowsHide: true,
|
|
34
|
-
stdio: ['ignore', logFd, logFd],
|
|
35
|
-
});
|
|
36
|
-
child.unref();
|
|
37
|
-
writeJsonAtomic(fallbackPidPath(), {
|
|
38
|
-
pid: child.pid,
|
|
39
|
-
processStart: child.pid ? processStart(child.pid) : null,
|
|
40
|
-
startedAt: new Date().toISOString(),
|
|
41
|
-
});
|
|
42
|
-
return { ok: true, pid: child.pid, note: 'this fallback does not survive a reboot' };
|
|
43
|
-
}
|
|
44
|
-
finally {
|
|
45
|
-
fs.closeSync(logFd);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function stopAndForget() {
|
|
49
|
-
const pid = runningPid();
|
|
50
|
-
if (pid !== null) {
|
|
51
|
-
try {
|
|
52
|
-
process.kill(pid, 'SIGTERM');
|
|
53
|
-
}
|
|
54
|
-
catch {
|
|
55
|
-
// Exited between the check and the signal.
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
fs.rmSync(fallbackPidPath(), { force: true });
|
|
59
|
-
return { ok: true, stoppedPid: pid };
|
|
60
|
-
}
|
|
61
|
-
export function detachedLogsHint(platform = process.platform, logFile = logPath('supervisor')) {
|
|
62
|
-
if (platform === 'win32')
|
|
63
|
-
return `Get-Content -Wait -Tail 40 "${logFile}"`;
|
|
64
|
-
return `tail -f ${logFile}`;
|
|
65
|
-
}
|
|
66
|
-
export const detached = {
|
|
67
|
-
name: 'supervisor',
|
|
68
|
-
status() {
|
|
69
|
-
const pid = runningPid();
|
|
70
|
-
const alive = pid !== null;
|
|
71
|
-
return {
|
|
72
|
-
manager: 'supervisor',
|
|
73
|
-
installed: alive,
|
|
74
|
-
active: alive,
|
|
75
|
-
enabled: false,
|
|
76
|
-
pid: alive ? pid : null,
|
|
77
|
-
unitPath: fallbackPidPath(),
|
|
78
|
-
state: alive ? 'running' : 'stopped',
|
|
79
|
-
};
|
|
80
|
-
},
|
|
81
|
-
async install() {
|
|
82
|
-
return start();
|
|
83
|
-
},
|
|
84
|
-
async uninstall() {
|
|
85
|
-
return stopAndForget();
|
|
86
|
-
},
|
|
87
|
-
restart() {
|
|
88
|
-
stopAndForget();
|
|
89
|
-
return start();
|
|
90
|
-
},
|
|
91
|
-
stop() {
|
|
92
|
-
stopAndForget();
|
|
93
|
-
},
|
|
94
|
-
logsHint() {
|
|
95
|
-
return detachedLogsHint();
|
|
96
|
-
},
|
|
97
|
-
};
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
// What a supervised copy needs from the shell that installed it.
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { findHerdrCommand } from '../herdr-command.js';
|
|
5
|
-
import { PACKAGE_ROOT } from '../paths.js';
|
|
6
|
-
export function cliEntryPoint() {
|
|
7
|
-
return path.join(PACKAGE_ROOT, 'bin', 'herdr-remote.js');
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* `PATH` for the unit: the one that is resolving commands right now, plus the
|
|
11
|
-
* directory Herdr was found in.
|
|
12
|
-
*
|
|
13
|
-
* A service manager does not inherit the shell's environment. `systemd --user`
|
|
14
|
-
* hands a unit something close to `/usr/local/bin:/usr/bin:/bin` and `launchd`
|
|
15
|
-
* is no more generous, so a copy that works from a terminal loses `~/.local/bin`
|
|
16
|
-
* — and with it `herdr` — the moment it is installed as a service. Freezing the
|
|
17
|
-
* installing shell's `PATH` into the unit keeps the supervised copy able to find
|
|
18
|
-
* Herdr, and Herdr able to find the tools it spawns in turn.
|
|
19
|
-
*/
|
|
20
|
-
export function servicePath({ env = process.env, herdrCommand = null, } = {}) {
|
|
21
|
-
const entries = String(env.PATH || '')
|
|
22
|
-
.split(path.delimiter)
|
|
23
|
-
.map((entry) => entry.trim())
|
|
24
|
-
.filter(Boolean);
|
|
25
|
-
// The override already pins the exact binary; this is only so the session
|
|
26
|
-
// itself can still reach it by name.
|
|
27
|
-
if (herdrCommand)
|
|
28
|
-
entries.push(path.dirname(herdrCommand));
|
|
29
|
-
return [...new Set(entries)].join(path.delimiter);
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* What has to be written into the unit file for a supervised start to behave
|
|
33
|
-
* like the one the user just ran by hand.
|
|
34
|
-
*/
|
|
35
|
-
export function serviceEnvironment({ env = process.env, home = os.homedir(), directories, } = {}) {
|
|
36
|
-
const environment = {};
|
|
37
|
-
const herdr = findHerdrCommand({ env, home, ...(directories ? { directories } : {}) });
|
|
38
|
-
if (herdr.found)
|
|
39
|
-
environment.HERDR_BIN_PATH = herdr.command;
|
|
40
|
-
const searchPath = servicePath({ env, herdrCommand: herdr.found ? herdr.command : null });
|
|
41
|
-
if (searchPath)
|
|
42
|
-
environment.PATH = searchPath;
|
|
43
|
-
return environment;
|
|
44
|
-
}
|
package/dist/keepalive/index.js
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
// Keep-alive integration.
|
|
2
|
-
//
|
|
3
|
-
// "Start the services" and "keep the services running" are different problems:
|
|
4
|
-
// a detached spawn dies with the first crash and never comes back. This module
|
|
5
|
-
// hands supervision to the platform's manager where one exists (systemd --user,
|
|
6
|
-
// launchd, or Task Scheduler) and falls back to a detached supervisor elsewhere.
|
|
7
|
-
import { spawnSync } from 'node:child_process';
|
|
8
|
-
import fs from 'node:fs';
|
|
9
|
-
import os from 'node:os';
|
|
10
|
-
import path from 'node:path';
|
|
11
|
-
import { loadConfig } from '../config.js';
|
|
12
|
-
import { detached } from './detached.js';
|
|
13
|
-
import { launchd } from './launchd.js';
|
|
14
|
-
import { systemd } from './systemd.js';
|
|
15
|
-
import { windowsService } from './windows-service.js';
|
|
16
|
-
import { windowsTask } from './windows-task.js';
|
|
17
|
-
export { serviceEnvironment, servicePath } from './environment.js';
|
|
18
|
-
export { escapeXml, renderLaunchdPlist } from './launchd.js';
|
|
19
|
-
export { renderSystemdUnit, systemdEnvironmentLine } from './systemd.js';
|
|
20
|
-
/**
|
|
21
|
-
* Look for an executable on PATH without going through a shell: `spawnSync`
|
|
22
|
-
* with `shell: true` concatenates rather than escapes its arguments, and Node
|
|
23
|
-
* now warns about it on every call.
|
|
24
|
-
*/
|
|
25
|
-
function commandExists(command) {
|
|
26
|
-
return (process.env.PATH || '')
|
|
27
|
-
.split(path.delimiter)
|
|
28
|
-
.filter(Boolean)
|
|
29
|
-
.some((directory) => {
|
|
30
|
-
try {
|
|
31
|
-
fs.accessSync(path.join(directory, command), fs.constants.X_OK);
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
catch {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
/** Which manager to use given the platform, the user's preference and reality. */
|
|
40
|
-
export function detectManager(preference = 'auto', platform = process.platform) {
|
|
41
|
-
if (preference && preference !== 'auto')
|
|
42
|
-
return preference;
|
|
43
|
-
if (platform === 'win32')
|
|
44
|
-
return 'windows-task';
|
|
45
|
-
if (platform === 'linux') {
|
|
46
|
-
// `systemctl --user` needs a user bus; containers and bare TTY logins often
|
|
47
|
-
// have systemd installed but no session bus, where it would fail at runtime.
|
|
48
|
-
if (commandExists('systemctl') &&
|
|
49
|
-
(process.env.DBUS_SESSION_BUS_ADDRESS || process.env.XDG_RUNTIME_DIR)) {
|
|
50
|
-
return 'systemd';
|
|
51
|
-
}
|
|
52
|
-
return 'supervisor';
|
|
53
|
-
}
|
|
54
|
-
if (platform === 'darwin') {
|
|
55
|
-
return commandExists('launchctl') ? 'launchd' : 'supervisor';
|
|
56
|
-
}
|
|
57
|
-
return 'supervisor';
|
|
58
|
-
}
|
|
59
|
-
function managerOf(config) {
|
|
60
|
-
return detectManager(config.keepalive?.manager || 'auto');
|
|
61
|
-
}
|
|
62
|
-
/** The backend for `config`. With keep-alive off, uninstalling still cleans up the fallback. */
|
|
63
|
-
function configuredBackend(manager) {
|
|
64
|
-
if (manager === 'systemd')
|
|
65
|
-
return systemd;
|
|
66
|
-
if (manager === 'launchd')
|
|
67
|
-
return launchd;
|
|
68
|
-
if (manager === 'windows-task')
|
|
69
|
-
return windowsTask;
|
|
70
|
-
if (manager === 'windows-service')
|
|
71
|
-
return windowsService;
|
|
72
|
-
return detached;
|
|
73
|
-
}
|
|
74
|
-
function backendOf(config) {
|
|
75
|
-
const manager = managerOf(config);
|
|
76
|
-
if (manager === 'windows-task') {
|
|
77
|
-
if (windowsTask.status().installed)
|
|
78
|
-
return windowsTask;
|
|
79
|
-
if (windowsService.status().installed)
|
|
80
|
-
return windowsService;
|
|
81
|
-
}
|
|
82
|
-
return configuredBackend(manager);
|
|
83
|
-
}
|
|
84
|
-
export function status(config = loadConfig()) {
|
|
85
|
-
if (managerOf(config) === 'none')
|
|
86
|
-
return { manager: 'none', installed: false, active: false, enabled: false };
|
|
87
|
-
return backendOf(config).status();
|
|
88
|
-
}
|
|
89
|
-
export async function install(config = loadConfig()) {
|
|
90
|
-
if (managerOf(config) === 'none')
|
|
91
|
-
throw new Error('keep-alive is disabled in the configuration');
|
|
92
|
-
const backend = configuredBackend(managerOf(config));
|
|
93
|
-
return { manager: backend.name, ...(await backend.install()) };
|
|
94
|
-
}
|
|
95
|
-
export async function uninstall(config = loadConfig()) {
|
|
96
|
-
const backend = backendOf(config);
|
|
97
|
-
return { manager: backend.name, ...(await backend.uninstall()) };
|
|
98
|
-
}
|
|
99
|
-
/** Restart whatever manages the services, so config edits take effect. */
|
|
100
|
-
export function restart(config = loadConfig()) {
|
|
101
|
-
const backend = backendOf(config);
|
|
102
|
-
return { manager: backend.name, ...backend.restart() };
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Stop the managed services. Killing the child pids directly would only make
|
|
106
|
-
* the service manager start them again, so an installed manager is asked to
|
|
107
|
-
* stop instead.
|
|
108
|
-
*/
|
|
109
|
-
export function stopManaged(config = loadConfig()) {
|
|
110
|
-
const current = status(config);
|
|
111
|
-
if (current.manager === 'none' || (!current.installed && !current.active))
|
|
112
|
-
return { managed: false };
|
|
113
|
-
backendOf(config).stop();
|
|
114
|
-
return { managed: true, manager: current.manager };
|
|
115
|
-
}
|
|
116
|
-
export function enableLinger() {
|
|
117
|
-
const username = os.userInfo().username;
|
|
118
|
-
const result = spawnSync('loginctl', ['enable-linger', username], {
|
|
119
|
-
encoding: 'utf8',
|
|
120
|
-
windowsHide: true,
|
|
121
|
-
});
|
|
122
|
-
if (result.status !== 0) {
|
|
123
|
-
throw new Error(String(result.stderr || '').trim() || 'loginctl enable-linger failed');
|
|
124
|
-
}
|
|
125
|
-
return { ok: true, username };
|
|
126
|
-
}
|
|
127
|
-
export function logsHint(config = loadConfig()) {
|
|
128
|
-
return backendOf(config).logsHint();
|
|
129
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
// launchd: a LaunchAgent that runs `herdr-remote run` and keeps it alive.
|
|
2
|
-
import { spawnSync } from 'node:child_process';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import { ensureDir } from 'herdr-remote-relay/state';
|
|
7
|
-
import { logPath, stateDir } from '../paths.js';
|
|
8
|
-
import { cliEntryPoint, serviceEnvironment } from './environment.js';
|
|
9
|
-
const LAUNCHD_LABEL = 'dev.herdr.remote';
|
|
10
|
-
function launchdPlistPath() {
|
|
11
|
-
return path.join(os.homedir(), 'Library', 'LaunchAgents', `${LAUNCHD_LABEL}.plist`);
|
|
12
|
-
}
|
|
13
|
-
export function escapeXml(value) {
|
|
14
|
-
return String(value)
|
|
15
|
-
.replace(/&/g, '&')
|
|
16
|
-
.replace(/</g, '<')
|
|
17
|
-
.replace(/>/g, '>')
|
|
18
|
-
.replace(/"/g, '"')
|
|
19
|
-
.replace(/'/g, ''');
|
|
20
|
-
}
|
|
21
|
-
export function renderLaunchdPlist({ nodePath = process.execPath, entryPoint = cliEntryPoint(), label = LAUNCHD_LABEL, outLog = logPath('supervisor'), errLog = logPath('supervisor'), environment = {}, } = {}) {
|
|
22
|
-
const environmentEntries = Object.entries(environment)
|
|
23
|
-
.map(([key, value]) => ` <key>${escapeXml(key)}</key>\n <string>${escapeXml(value)}</string>`)
|
|
24
|
-
.join('\n');
|
|
25
|
-
const environmentBlock = environmentEntries
|
|
26
|
-
? ` <key>EnvironmentVariables</key>\n <dict>\n${environmentEntries}\n </dict>\n`
|
|
27
|
-
: '';
|
|
28
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
29
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
30
|
-
<plist version="1.0">
|
|
31
|
-
<dict>
|
|
32
|
-
<key>Label</key>
|
|
33
|
-
<string>${escapeXml(label)}</string>
|
|
34
|
-
<key>ProgramArguments</key>
|
|
35
|
-
<array>
|
|
36
|
-
<string>${escapeXml(nodePath)}</string>
|
|
37
|
-
<string>${escapeXml(entryPoint)}</string>
|
|
38
|
-
<string>run</string>
|
|
39
|
-
</array>
|
|
40
|
-
<key>RunAtLoad</key>
|
|
41
|
-
<true/>
|
|
42
|
-
<key>KeepAlive</key>
|
|
43
|
-
<true/>
|
|
44
|
-
<key>ProcessType</key>
|
|
45
|
-
<string>Background</string>
|
|
46
|
-
${environmentBlock} <key>StandardOutPath</key>
|
|
47
|
-
<string>${escapeXml(outLog)}</string>
|
|
48
|
-
<key>StandardErrorPath</key>
|
|
49
|
-
<string>${escapeXml(errLog)}</string>
|
|
50
|
-
</dict>
|
|
51
|
-
</plist>
|
|
52
|
-
`;
|
|
53
|
-
}
|
|
54
|
-
function domainTarget() {
|
|
55
|
-
return `gui/${process.getuid ? process.getuid() : ''}`;
|
|
56
|
-
}
|
|
57
|
-
function serviceTarget() {
|
|
58
|
-
return `${domainTarget()}/${LAUNCHD_LABEL}`;
|
|
59
|
-
}
|
|
60
|
-
function launchctl(args) {
|
|
61
|
-
return spawnSync('launchctl', args, { encoding: 'utf8', windowsHide: true });
|
|
62
|
-
}
|
|
63
|
-
/** A launchctl call whose outcome does not matter (bootout of nothing, say). */
|
|
64
|
-
function launchctlQuietly(args) {
|
|
65
|
-
spawnSync('launchctl', args, { stdio: 'ignore', windowsHide: true });
|
|
66
|
-
}
|
|
67
|
-
export const launchd = {
|
|
68
|
-
name: 'launchd',
|
|
69
|
-
status() {
|
|
70
|
-
const plistPath = launchdPlistPath();
|
|
71
|
-
if (!fs.existsSync(plistPath))
|
|
72
|
-
return { manager: 'launchd', installed: false, active: false, enabled: false };
|
|
73
|
-
const result = launchctl(['print', serviceTarget()]);
|
|
74
|
-
const output = String(result.stdout || '');
|
|
75
|
-
return {
|
|
76
|
-
manager: 'launchd',
|
|
77
|
-
installed: true,
|
|
78
|
-
active: result.status === 0 && /state = running/.test(output),
|
|
79
|
-
enabled: result.status === 0,
|
|
80
|
-
unitPath: plistPath,
|
|
81
|
-
state: result.status === 0 ? 'loaded' : 'not loaded',
|
|
82
|
-
};
|
|
83
|
-
},
|
|
84
|
-
async install() {
|
|
85
|
-
const plistPath = launchdPlistPath();
|
|
86
|
-
ensureDir(path.dirname(plistPath));
|
|
87
|
-
ensureDir(stateDir());
|
|
88
|
-
fs.writeFileSync(plistPath, renderLaunchdPlist({ environment: serviceEnvironment() }), {
|
|
89
|
-
mode: 0o644,
|
|
90
|
-
});
|
|
91
|
-
// bootout first so a re-install picks up the rewritten plist.
|
|
92
|
-
launchctlQuietly(['bootout', serviceTarget()]);
|
|
93
|
-
const result = launchctl(['bootstrap', domainTarget(), plistPath]);
|
|
94
|
-
if (result.status !== 0) {
|
|
95
|
-
throw new Error(`launchctl bootstrap failed: ${String(result.stderr || '').trim()}`);
|
|
96
|
-
}
|
|
97
|
-
launchctlQuietly(['enable', serviceTarget()]);
|
|
98
|
-
return { ok: true, unitPath: plistPath };
|
|
99
|
-
},
|
|
100
|
-
async uninstall() {
|
|
101
|
-
const plistPath = launchdPlistPath();
|
|
102
|
-
launchctlQuietly(['bootout', serviceTarget()]);
|
|
103
|
-
fs.rmSync(plistPath, { force: true });
|
|
104
|
-
return { ok: true, unitPath: plistPath };
|
|
105
|
-
},
|
|
106
|
-
restart() {
|
|
107
|
-
launchctlQuietly(['kickstart', '-k', serviceTarget()]);
|
|
108
|
-
return { ok: true };
|
|
109
|
-
},
|
|
110
|
-
stop() {
|
|
111
|
-
launchctlQuietly(['bootout', serviceTarget()]);
|
|
112
|
-
},
|
|
113
|
-
// The plist sends the supervisor's output to its log file.
|
|
114
|
-
logsHint() {
|
|
115
|
-
return `tail -f ${logPath('supervisor')}`;
|
|
116
|
-
},
|
|
117
|
-
};
|