dsh-mobile-plugin 0.0.0-stage → 0.1.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +128 -2
  2. package/config/agent-presets/standard/agent.cordis.yml +170 -0
  3. package/config/agent-presets/standard/preset.yml +3 -0
  4. package/cordis.mobile-host.yml +48 -0
  5. package/cordis.patch.yml +21 -0
  6. package/dsh-mobile-credentials/lib/credentials.js +135 -0
  7. package/dsh-mobile-credentials/package.json +13 -0
  8. package/host.js +128 -0
  9. package/lib/auto-archive.cjs +98 -0
  10. package/lib/client.js +690 -0
  11. package/lib/desktop-half-stats.cjs +101 -0
  12. package/lib/desktop-view.cjs +109 -0
  13. package/lib/host-guard.cjs +230 -0
  14. package/lib/host-patches.cjs +46 -0
  15. package/lib/http/package.json +3 -0
  16. package/lib/http/routing.js +84 -0
  17. package/lib/impl.js +406 -0
  18. package/lib/mirror/pusher.cjs +706 -0
  19. package/lib/mirror-snapshot.cjs +39 -0
  20. package/lib/mobile/agent-presets.js +182 -0
  21. package/lib/mobile/attachments.js +116 -0
  22. package/lib/mobile/auth.js +78 -0
  23. package/lib/mobile/completion.js +114 -0
  24. package/lib/mobile/credentials.js +150 -0
  25. package/lib/mobile/directory-browse.js +295 -0
  26. package/lib/mobile/envelope.js +51 -0
  27. package/lib/mobile/events.js +293 -0
  28. package/lib/mobile/history-derive.js +608 -0
  29. package/lib/mobile/host-bridge/endpoints.js +189 -0
  30. package/lib/mobile/host-bridge/event-synthesis.js +232 -0
  31. package/lib/mobile/host-bridge/shims.js +218 -0
  32. package/lib/mobile/host-bridge/transport.js +168 -0
  33. package/lib/mobile/host-bridge.js +71 -0
  34. package/lib/mobile/host-settings.js +282 -0
  35. package/lib/mobile/idempotency.js +78 -0
  36. package/lib/mobile/jobs.js +97 -0
  37. package/lib/mobile/local-images.js +153 -0
  38. package/lib/mobile/package.json +3 -0
  39. package/lib/mobile/plugins.js +149 -0
  40. package/lib/mobile/questions.js +71 -0
  41. package/lib/mobile/references.js +209 -0
  42. package/lib/mobile/rpc.js +76 -0
  43. package/lib/mobile/session-cache.js +263 -0
  44. package/lib/mobile/session-goal.js +44 -0
  45. package/lib/mobile/session-log.js +94 -0
  46. package/lib/mobile/session-mirror.js +177 -0
  47. package/lib/mobile/session-models.js +153 -0
  48. package/lib/mobile/session-presets.js +52 -0
  49. package/lib/mobile/session-search.js +65 -0
  50. package/lib/mobile/session-send.js +278 -0
  51. package/lib/mobile/session-snapshot.js +142 -0
  52. package/lib/mobile/sessions.js +462 -0
  53. package/lib/mobile/skills.js +80 -0
  54. package/lib/mobile/stream.js +268 -0
  55. package/lib/mobile/subagents.js +265 -0
  56. package/lib/mobile/workspaces.js +214 -0
  57. package/lib/panel/index.html +33 -0
  58. package/lib/read-receipt.cjs +124 -0
  59. package/lib/reconnect-scheduler.cjs +37 -0
  60. package/lib/relay/core.js +77 -0
  61. package/lib/relay/lan-network.cjs +85 -0
  62. package/lib/relay/lan-proxy.js +593 -0
  63. package/lib/relay/package.json +3 -0
  64. package/lib/relay/pairing-policy.js +45 -0
  65. package/lib/relay/port-allocator.cjs +60 -0
  66. package/lib/relay/preview.js +374 -0
  67. package/lib/relay/protocol.js +177 -0
  68. package/lib/relay/status.js +145 -0
  69. package/lib/relay/tunnel.js +721 -0
  70. package/lib/routes/control-plane.js +311 -0
  71. package/lib/routes/mobile-api.js +208 -0
  72. package/lib/routes/package.json +3 -0
  73. package/lib/session-cleanup.cjs +153 -0
  74. package/lib/shared/credentials-entry.js +48 -0
  75. package/lib/shared/dshtoken.js +62 -0
  76. package/lib/shared/fresh-request.js +35 -0
  77. package/lib/shared/http-util.js +71 -0
  78. package/lib/shared/list-revision.js +51 -0
  79. package/lib/shared/list-signal.js +87 -0
  80. package/lib/shared/package.json +3 -0
  81. package/lib/shared/paths.js +27 -0
  82. package/lib/shared/session-signal.js +110 -0
  83. package/lib/shared/workspace-guard.js +127 -0
  84. package/lib/sidecar-facade-worker.cjs +353 -0
  85. package/lib/sidecar-facade.cjs +141 -0
  86. package/lib/sidecar-lifecycle.cjs +246 -0
  87. package/package.json +74 -4
@@ -0,0 +1,145 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * 中转状态视图:把「此刻这台宿主到底怎么样」拼成一份 JSON。
5
+ *
6
+ * 这份 JSON 有三个消费方,形状必须一模一样:桌面端设置页(`client.js`)轮询
7
+ * `/api/mobile/v1/status`,手机端首屏也读同一条,而二维码里嵌的也是它的子集。
8
+ * 以前它是 apply() 闭包里的一坨内联函数,混在 sidecar 生命周期与镜像推送之间——想核对
9
+ * 某个字段拼对没有,只能通读 1000 行装配代码。抽出来之后可以直接 require 单元测试,
10
+ * 也让 index.js 只剩装配。
11
+ *
12
+ * 注意 `TUNNEL_ADMISSION`:它靠**正则匹配中转返回的错误文案**来判断隧道为什么被拒。
13
+ * 这是个脆弱契约(中转改一个字就漏判),但在没有更好信号之前只能这样——错误文案是
14
+ * 当前唯一穿过隧道边界回来的信息。判定放宽到只认关键词,见下。
15
+ */
16
+
17
+ const os = require('node:os')
18
+
19
+ // 版本号只有一个来源:包自己的 package.json。以前 index.js 与 hostMetadata() 各拼一次。
20
+ const { version: pluginVersion } = require('../../package.json')
21
+
22
+ // 客户端半边(桌面页面里的 lib/client.js)的活体台账,只读它的计数快照。
23
+ const desktopHalf = require('../desktop-half-stats.cjs')
24
+
25
+ const TUNNEL_ADMISSION = [
26
+ {
27
+ // 中转关连接时用的文案是 'bad registration secret'(server.js 里拼的),
28
+ // 而 lib/tunnel-registration.js 内部的 reason 拼作 'bad register secret'。
29
+ // 两种拼法都出现过,所以正则认词干不认具体字母:regist + 任意尾巴 + secret。
30
+ pattern: /bad regist\w*\s+secret/i,
31
+ flag: 'registerSecretRequired',
32
+ // 这两条拒绝都由 server.js 的准入判定给出(lib/tunnel-registration.js),
33
+ // 文案变了这里就会漏判,所以判定正则放宽到只认关键词。
34
+ //
35
+ // 插件每 5 秒重试一次,而状态里只有一句「隧道关闭: 1008 bad registration secret」。
36
+ // 那句话不指向任何可执行动作——第一次上手的人(以及帮他的人)只能看着它干瞪眼:
37
+ // 值从哪儿来、去哪儿填,代码里一个字都没有。
38
+ //
39
+ // 这里必须说清楚一件不好听的事:注册密钥是**中转服务器的部署参数**,只存在于它的
40
+ // systemd unit(Environment=TUNNEL_REGISTER_SECRET=…)或 instances.json 里,
41
+ // 中转不通过任何接口把它公开出去。所以客户端能给出的最好答案就是「向部署者索取,
42
+ // 填进设置页的『注册密钥』」——拿不到就说拿不到,不假装有自助路径。
43
+ hint: 'register-secret',
44
+ },
45
+ {
46
+ pattern: /unknown instance/i,
47
+ flag: 'instanceNotAllowed',
48
+ hint: 'instance-allowlist',
49
+ },
50
+ ]
51
+
52
+ /** 给隧道快照补上「为什么被拒」的可执行线索;正常状态原样返回。 */
53
+ function describeTunnel(snapshot) {
54
+ const base = snapshot || { state: 'off', error: null }
55
+ if (base.state !== 'error' || !base.error) return base
56
+ const hit = TUNNEL_ADMISSION.find((entry) => entry.pattern.test(String(base.error)))
57
+ return hit ? { ...base, [hit.flag]: true, admissionHint: hit.hint } : base
58
+ }
59
+
60
+ function hostMetadata() {
61
+ return {
62
+ os: process.platform,
63
+ // `os` is the legacy field name; `platform` is canonical so mobile clients
64
+ // never confuse the host's OS with their own.
65
+ platform: process.platform,
66
+ osVersion: os.release(),
67
+ arch: process.arch,
68
+ appVersion: pluginVersion,
69
+ }
70
+ }
71
+
72
+ /**
73
+ * @param {object} deps
74
+ * @param {object} deps.state 内存里的中转状态(const 对象,就地改)
75
+ * @param {number} deps.webServerPort
76
+ * @param {() => object} deps.getSidecarSnapshot 取值器,不是对象:sidecarSnapshot
77
+ * 会被 applySidecarConfig 整体替换,闭包捕获对象会永远读到旧快照。
78
+ * @param {() => number} deps.lanPortInUse 本进程解析出的实际端口
79
+ * 其余都是 apply() 闭包里会被重新赋值的 let,用取值器读。
80
+ */
81
+ function createStatusView(deps) {
82
+ const {
83
+ state,
84
+ webServerPort,
85
+ getSidecarSnapshot,
86
+ lanPortInUse,
87
+ getLanPortAdjusted,
88
+ getSidecarState,
89
+ getSidecarRestarts,
90
+ getSidecarRestartedAt,
91
+ getSidecarRestartError,
92
+ getMirrorPush,
93
+ } = deps
94
+
95
+ return function status() {
96
+ const sidecarSnapshot = getSidecarSnapshot()
97
+ const lanPortAdjusted = getLanPortAdjusted()
98
+ const mirrorPush = getMirrorPush()
99
+ return {
100
+ enabled: state.enabled,
101
+ instanceId: state.instanceId,
102
+ pairingCode: state.pairingCode,
103
+ relayUrl: state.relayUrl,
104
+ name: state.name,
105
+ pairingPolicy: state.pairingPolicy === 'deny' ? 'deny' : 'code',
106
+ // 首次接入引导走完没有。面板(lib/panel 里那份 React 页面)据此决定打开时
107
+ // 先走引导还是直接进主页;`enabled` 也算「配过了」,所以老机器升级上来不会
108
+ // 突然被拦一次引导(它们的 relay.json 里没有这个键,读到的是 undefined)。
109
+ onboarded: state.onboarded === true,
110
+ ...hostMetadata(),
111
+ localPort: webServerPort,
112
+ tunnel: describeTunnel(sidecarSnapshot.tunnel),
113
+ // 以本进程解析出的实际端口为准:sidecar 的快照里 `port` 才是最终 bind 成功的
114
+ // 那个(它在 listen 之后才拿得到),而这里在探测阶段就已经知道了,两个值一致
115
+ // 才不会让状态接口和二维码互相打架。
116
+ lan: {
117
+ ...(sidecarSnapshot.lan || {}),
118
+ port: lanPortInUse(),
119
+ adjusted: lanPortAdjusted,
120
+ requestedPort: state.lanPort,
121
+ },
122
+ sidecar: {
123
+ state: getSidecarState(),
124
+ // 自重启能换掉哪一层、换不掉哪一层,写在这里而不是只写在文档里:
125
+ // 「重启了」不等于「所有改动都生效」,这个区分必须能在状态接口上直接看到。
126
+ scope: {
127
+ worker: 'restartable',
128
+ facade: 'restartable',
129
+ // 宿主侧模块(lib/index.js、lib/client.js、lib/mobile/*)已挂在 fiber 上,
130
+ // 摘不掉,只能重启 DSH 宿主。
131
+ host: 'needs-host-restart',
132
+ },
133
+ restarts: getSidecarRestarts(),
134
+ restartedAt: getSidecarRestartedAt() || 0,
135
+ restartError: getSidecarRestartError(),
136
+ },
137
+ mirror: { pushedAt: mirrorPush.pushedAt || 0, ok: mirrorPush.ok, error: mirrorPush.error },
138
+ // 桌面页面里的客户端半边「有没有来敲过门」:绿点两条同步通道都是单向轮询 + 无回执,
139
+ // 这里的三组计数是唯一的活体证据(对照关系见 lib/desktop-half-stats.cjs 的文件头)。
140
+ desktopHalf: desktopHalf.snapshot(),
141
+ }
142
+ }
143
+ }
144
+
145
+ module.exports = { TUNNEL_ADMISSION, describeTunnel, hostMetadata, createStatusView }