dsh-deeppilot 0.9.8 → 0.9.9
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 +23 -3
- package/README.zh-CN.md +15 -2
- package/bin/SHA256SUMS +6 -6
- package/bin/darwin-amd64/dsh-deeppilot-tunnel +0 -0
- package/bin/darwin-arm64/dsh-deeppilot-tunnel +0 -0
- package/bin/linux-amd64/dsh-deeppilot-tunnel +0 -0
- package/bin/linux-arm64/dsh-deeppilot-tunnel +0 -0
- package/bin/windows-amd64/dsh-deeppilot-tunnel.exe +0 -0
- package/bin/windows-arm64/dsh-deeppilot-tunnel.exe +0 -0
- package/docs/DSH_RELEASE_MEMORY.md +30 -8
- package/docs/NPM_PUBLISHING.md +11 -0
- package/lib/client.js +152 -4
- package/lib/client.js.map +1 -1
- package/lib/index.d.ts +3 -0
- package/lib/index.js +634 -48
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -125,10 +125,30 @@ system-reported name; saving an empty value restores the iPhone-reported
|
|
|
125
125
|
default.
|
|
126
126
|
|
|
127
127
|
The settings page's **Testing & troubleshooting** group has a `debug` toggle.
|
|
128
|
-
Turning it on prints
|
|
128
|
+
Turning it on prints per-frame and push diagnostics to the DSH host's terminal
|
|
129
129
|
output (for example, the actual capability bits sent in `s2c.welcome`); it
|
|
130
|
-
never prints pairing tokens, APNs tokens, or message content
|
|
131
|
-
|
|
130
|
+
never prints pairing tokens, APNs tokens, or message content. The toggle is
|
|
131
|
+
snapshotted per connection, so an already-open connection only picks up the new
|
|
132
|
+
value after it reconnects.
|
|
133
|
+
|
|
134
|
+
**Connection failures do not need that toggle.** The plugin always records
|
|
135
|
+
admission rejections, authentication failures (4401 distinguishes an unknown
|
|
136
|
+
device, a revoked device, a bad signature, an expired challenge and clock skew),
|
|
137
|
+
TLS handshake failures and pairing failures. They show up in the terminal as
|
|
138
|
+
`[deeppilot] <time> WARN <event> <reason> src=<source hash> attempt=<attempt ID>`
|
|
139
|
+
and are appended under `diagnostics/` in the plugin data directory (7 days,
|
|
140
|
+
5 MB cap). The settings page's **Connection diagnostics** group lists the most
|
|
141
|
+
recent failures with a copy button, and `/phone/health` additionally returns the
|
|
142
|
+
local transport phase and port, the data-plane phase, whether the TLS identity
|
|
143
|
+
was rebuilt, the start time and the last failure, so the phone can locate the
|
|
144
|
+
cause even while it cannot connect. Records contain only source hashes — never
|
|
145
|
+
raw IPs, pairing codes, keys, APNs tokens or message content.
|
|
146
|
+
|
|
147
|
+
Each connection attempt from the phone carries a random `attemptId` (the `?at=`
|
|
148
|
+
query parameter on the WebSocket upgrade, or the `X-DeepPilot-Attempt` header on
|
|
149
|
+
pairing and health checks). The plugin writes it into the log lines and
|
|
150
|
+
diagnostic events above, so a user reporting that ID lets you find the exact
|
|
151
|
+
attempt in the host log.
|
|
132
152
|
|
|
133
153
|
Offline push is optional. Relay mode sends only the target APNs device token
|
|
134
154
|
and a limited notification payload; full conversation history and live output
|
package/README.zh-CN.md
CHANGED
|
@@ -108,8 +108,21 @@ DeepPilot 设置页提供“每个公网来源的连接上限”,默认 `8`,
|
|
|
108
108
|
也不会被覆盖。留空保存可恢复 iPhone 上报的默认名称。
|
|
109
109
|
|
|
110
110
|
设置页的“测试与故障排查”分组下有一个 `debug` 开关,打开后会在 DSH host 的
|
|
111
|
-
|
|
112
|
-
不会打印配对令牌、APNs token
|
|
111
|
+
终端输出里打印逐帧与推送等诊断信息(例如 `s2c.welcome` 里实际发出的能力位),
|
|
112
|
+
不会打印配对令牌、APNs token 或消息内容。该开关按连接快照,已建立的连接需要
|
|
113
|
+
重连一次才按新值工作。
|
|
114
|
+
|
|
115
|
+
**连不上时不需要打开这个开关**:插件会无条件记录准入拒绝、认证失败(4401 会区分
|
|
116
|
+
设备不存在、已撤销、签名错、挑战过期、时钟偏差等原因)、TLS 握手失败与配对失败,
|
|
117
|
+
在终端输出为 `[deeppilot] <时间> WARN <事件> <原因> src=<来源哈希> attempt=<尝试 ID>`,
|
|
118
|
+
并落盘到插件数据目录的 `diagnostics/`(保留 7 天、上限 5 MB)。设置页“连接诊断”
|
|
119
|
+
分组可查看最近失败并一键复制;`/phone/health` 额外返回本地传输相位与端口、数据面
|
|
120
|
+
相位、是否重建过 TLS 身份、启动时间与最近一次失败,手机端在连不上时也能据此定位。
|
|
121
|
+
记录只含来源哈希,不含原始 IP、配对码、密钥、APNs token 或消息内容。
|
|
122
|
+
|
|
123
|
+
手机端每次连接尝试会带一个随机的 `attemptId`(WebSocket 升级的 `?at=` 查询参数,
|
|
124
|
+
或配对/健康检查的 `X-DeepPilot-Attempt` 请求头)。插件把它写进上面的日志与诊断
|
|
125
|
+
事件;用户反馈时报告这个 ID,就能在主机日志里定位到同一次尝试。
|
|
113
126
|
|
|
114
127
|
离线推送是可选功能。中继模式只发送目标 APNs 设备 Token 和有限的通知内容;
|
|
115
128
|
完整会话历史与实时输出不会经过中继。启用远程访问或推送前,请阅读
|
package/bin/SHA256SUMS
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
4e83f82d5c623115d121f58264ca84706e5fc935dadc0747b37ae886d102dc6a ./darwin-amd64/dsh-deeppilot-tunnel
|
|
2
|
+
7707999a5b745d11afeb4ab1868c85680e4ff5567202d97015d72d810817a134 ./darwin-arm64/dsh-deeppilot-tunnel
|
|
3
|
+
8a83351568cb223854efbe8f54ca9a403fc2b152382d4e7c952d372b0b0cb3e9 ./linux-amd64/dsh-deeppilot-tunnel
|
|
4
|
+
dfa4eb4dd50b386aadf40d9767ac0df8151308b620fa395d0a560498f01eb0cb ./linux-arm64/dsh-deeppilot-tunnel
|
|
5
|
+
9c734ea42bc6dd5763930991db52f910d5138e9b12df0e826d6964ceb271e215 ./windows-amd64/dsh-deeppilot-tunnel.exe
|
|
6
|
+
e7db4a0e62f04266d97a5bad38e1a21961474440e7231fd5b24980053589faa0 ./windows-arm64/dsh-deeppilot-tunnel.exe
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -87,12 +87,34 @@
|
|
|
87
87
|
`0.2.0-rc.2` this branch builds and typechecks against.
|
|
88
88
|
- **Protocol verdict: no phone-protocol change.** Protocol v2, pairing state, and
|
|
89
89
|
device records are untouched by this release; no phone needs to re-pair.
|
|
90
|
-
- **
|
|
91
|
-
verification
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
90
|
+
- **The limits this entry originally stated are now closed.** The positive half of
|
|
91
|
+
the rc.2 verification ran before publication: on an isolated `0.2.1-rc.2` host
|
|
92
|
+
carrying the widened declaration, `--dump-config` wrote nothing to stderr, the
|
|
93
|
+
composed tree contained `# == dsh-deeppilot` (line 1233) together with
|
|
94
|
+
`# == @deepseek-ai/dsh-web-app, patched by dsh-deeppilot` (line 449), the running
|
|
95
|
+
host logged four `[deeppilot]` lines, and the plugin's own LAN listener on port
|
|
96
|
+
3098 answered `/phone/health` with `200` and `serverVersion` `0.9.8`. A tamper
|
|
97
|
+
control removed the `0.2.1-rc.2` token from a copy of `engines.dsh`: the host
|
|
98
|
+
refused again with the single `engines.dsh` requirement (`exempted: false`) and
|
|
99
|
+
accepted it once restored. Independent verification of the whole release tree —
|
|
100
|
+
`npm ci`, 581 tests (580 pass, 0 fail, 1 pre-existing TODO), typecheck, build
|
|
101
|
+
with a byte-identical regenerated `lib/`, `check:config-schema`, the Go helper
|
|
102
|
+
tests, and all six `bin/SHA256SUMS` entries — is recorded in
|
|
103
|
+
`.rc2-verify/VERIFICATION.md`; that pass also caught a real defect, a version
|
|
104
|
+
bump that had left `tests/apply-baseline.snapshot.json` stale.
|
|
105
|
+
- **Published as 0.9.8 on 2026-10-18.** The annotated tag `v0.9.8` dereferences to
|
|
106
|
+
`f4551b61602e5e60b11565ae31ccba3257d3dc9e` on `main`, CI and CodeQL were green on
|
|
107
|
+
that commit, and `publish.yml` ran on the tag (run `39048037503`, 24/24 steps).
|
|
108
|
+
The registry records `latest: 0.9.8`, published `2026-10-17T16:12:59.699Z`, with a
|
|
109
|
+
`https://slsa.dev/provenance/v1` attestation naming that commit as its source. The
|
|
110
|
+
published tarball's SHA1 `8288977711e04679d077b970f863e726631fc309` and its SHA512
|
|
111
|
+
match the registry, its 68 entries and 130,945,335 unpacked bytes match the
|
|
112
|
+
pre-publication pack, and every packaged file compared byte-identically with the
|
|
113
|
+
verified tree. A clean isolated profile then installed `dsh-deeppilot@0.9.8` from
|
|
114
|
+
npm, composed it on a real `0.2.1-rc.2` host, and answered `/phone/health` with
|
|
115
|
+
`200` and `serverVersion: "0.9.8"`; the GitHub Release was made public only after
|
|
116
|
+
that install verification. The same release shipped the mobile skills, host
|
|
117
|
+
commands, and catalog-cache work recorded below.
|
|
96
118
|
|
|
97
119
|
### Previous decision — `dsh-v0.2.1-alpha.5`
|
|
98
120
|
|
|
@@ -1756,7 +1778,7 @@ release notes.
|
|
|
1756
1778
|
|
|
1757
1779
|
## 2026-10-15 — 移动端技能发现扩展
|
|
1758
1780
|
|
|
1759
|
-
新增可选的 `skills` 手机能力,使用官方 `sessionSkillCatalog.list({sessionId}, AbortSignal)` 服务,已对照本机 `0.2.0-rc.2` 和 `0.2.1-alpha.3`
|
|
1781
|
+
新增可选的 `skills` 手机能力,使用官方 `sessionSkillCatalog.list({sessionId}, AbortSignal)` 服务,已对照本机 `0.2.0-rc.2` 和 `0.2.1-alpha.3` 发行声明。缺少服务只关闭该特性,晚挂载服务会重新探测,不缓存缺席。未修改宿主版本范围、鉴权、包版本或命令派发契约。手机只接收元数据,技能注册表和会话预设、工作目录变化触发目录失效。完整文档清单与四类差异表见《技能提示对接说明》。此源码改动随 0.9.8(2026-10-18)发布;主机侧集成已在本机隔离真实宿主上验收,真机技能加载仍未验收。
|
|
1760
1782
|
|
|
1761
1783
|
## 2026-10-16 — 移动端系统指令扩展
|
|
1762
1784
|
|
|
@@ -1770,4 +1792,4 @@ release notes.
|
|
|
1770
1792
|
1. 安装与主机 API:本轮不改安装范围或依赖;读取沿用现有技能、命令和模型接口。工作区另有 alpha.4 兼容改动,命令对接已重新枚举该版 29 包、144 个接口,相关主机实现与 alpha.3 相同;不据此宣称真实业务验收。
|
|
1771
1793
|
2. 手机协议:模型当前选择投影新增 `s2c.models.changed`,受 `sessions.read` 控制;原生权限执行可附加 `expectedDefinitionId`,重读目录核对来源,旧客户端字段缺省兼容。无新增周期轮询或第二层主机时间缓存。
|
|
1772
1794
|
3. 客户端:内存五分钟、磁盘最长七天,重启旧目录先展示并校验,按配对身份/主机/会话隔离;当前模型、权限和执行结果不落盘。具体约定和格式重建见[移动端目录缓存](移动端目录缓存.md)。
|
|
1773
|
-
4. 验证:插件 577 项通过、1
|
|
1795
|
+
4. 验证:插件 577 项通过、1 项原有待办,类型检查、生成产物构建和使用本机固定版本的离线配置校验通过;两端缓存单元与模拟器证据见各自文档。随 0.9.8(2026-10-18)发布;真机与系统分享仍未验收。
|
package/docs/NPM_PUBLISHING.md
CHANGED
|
@@ -105,6 +105,17 @@
|
|
|
105
105
|
本轮还确认了一个易漏点:升版本号必须同步重录
|
|
106
106
|
`tests/apply-baseline.snapshot.json`,否则 `npm test` 会失败——已写入
|
|
107
107
|
`AGENTS.md` 的发布流程第 1 步。
|
|
108
|
+
8. `0.9.8` 的实际发布结果(2026-10-18):附注标签 `v0.9.8` 指向
|
|
109
|
+
`f4551b61602e5e60b11565ae31ccba3257d3dc9e`,该提交的 CI 与 CodeQL 均为绿;
|
|
110
|
+
`publish.yml` 在该标签上运行(运行号 `39048037503`,24 个步骤全部成功),
|
|
111
|
+
注册表 `latest` 为 `0.9.8`。逐项核验:来源证明类型为
|
|
112
|
+
`https://slsa.dev/provenance/v1`,其 `gitCommit` 正是该提交;压缩包 SHA1
|
|
113
|
+
`8288977711e04679d077b970f863e726631fc309` 与 SHA512 与注册表一致;注册表
|
|
114
|
+
条目数 68、解包 130,945,335 字节与发布前的打包核对一致,包内逐个文件与已验证
|
|
115
|
+
的工作树逐字节相同。随后在全新隔离 profile 中从 npm 安装
|
|
116
|
+
`dsh-deeppilot@0.9.8`,在真实 `0.2.1-rc.2` 宿主上完成组合与启动,插件自有端口
|
|
117
|
+
3098 的 `/phone/health` 返回 200 且 `serverVersion` 为 `0.9.8`;GitHub Release
|
|
118
|
+
在这一步通过后才公开并标记 Latest。
|
|
108
119
|
|
|
109
120
|
## 五、官方资料
|
|
110
121
|
|
package/lib/client.js
CHANGED
|
@@ -245,6 +245,40 @@ window.__ModuleLoader__.load({
|
|
|
245
245
|
results
|
|
246
246
|
};
|
|
247
247
|
}
|
|
248
|
+
function parseDiagnosticFailure(value) {
|
|
249
|
+
const s = rec(value, "diagnostics.recentFailures[]");
|
|
250
|
+
const kind = str(s, "kind", "diagnostics.kind");
|
|
251
|
+
if (!/^[a-z][a-z0-9-]{0,47}$/.test(kind)) reject("diagnostics.kind");
|
|
252
|
+
const reason = str(s, "reason", "diagnostics.reason");
|
|
253
|
+
if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(reason)) reject("diagnostics.reason");
|
|
254
|
+
const attemptId = s.attemptId;
|
|
255
|
+
if (attemptId !== void 0 && (typeof attemptId !== "string" || !/^[A-Za-z0-9_-]{1,16}$/.test(attemptId))) reject("diagnostics.attemptId");
|
|
256
|
+
const source = s.source;
|
|
257
|
+
if (source !== void 0 && (typeof source !== "string" || !/^[0-9a-f]{8,64}$/.test(source))) reject("diagnostics.source");
|
|
258
|
+
const closeCode = s.closeCode;
|
|
259
|
+
if (closeCode !== void 0 && (typeof closeCode !== "number" || !Number.isInteger(closeCode))) reject("diagnostics.closeCode");
|
|
260
|
+
const durationMs = s.durationMs;
|
|
261
|
+
if (durationMs !== void 0 && (typeof durationMs !== "number" || !Number.isFinite(durationMs) || durationMs < 0)) reject("diagnostics.durationMs");
|
|
262
|
+
return {
|
|
263
|
+
ts: int(s, "ts", "diagnostics.ts"),
|
|
264
|
+
kind,
|
|
265
|
+
reason,
|
|
266
|
+
...typeof attemptId === "string" ? { attemptId } : {},
|
|
267
|
+
...typeof source === "string" ? { source } : {},
|
|
268
|
+
...typeof closeCode === "number" ? { closeCode } : {},
|
|
269
|
+
...typeof durationMs === "number" ? { durationMs: Math.round(durationMs) } : {}
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function parseDiagnostics(value) {
|
|
273
|
+
const s = rec(value, "diagnostics");
|
|
274
|
+
const failures = s.recentFailures;
|
|
275
|
+
if (!Array.isArray(failures)) reject("diagnostics.recentFailures");
|
|
276
|
+
if (failures.length > 20) reject("diagnostics.recentFailures");
|
|
277
|
+
return {
|
|
278
|
+
startedAt: int(s, "startedAt", "diagnostics.startedAt"),
|
|
279
|
+
recentFailures: failures.map(parseDiagnosticFailure)
|
|
280
|
+
};
|
|
281
|
+
}
|
|
248
282
|
function parseReport(value) {
|
|
249
283
|
const s = rec(value, "report");
|
|
250
284
|
const devices = s.devices;
|
|
@@ -266,7 +300,8 @@ window.__ModuleLoader__.load({
|
|
|
266
300
|
lanAddresses,
|
|
267
301
|
local: parseLocal(s.local),
|
|
268
302
|
remote: parseRemote(s.remote),
|
|
269
|
-
devices: devices.map(parseDevice)
|
|
303
|
+
devices: devices.map(parseDevice),
|
|
304
|
+
...s.diagnostics !== void 0 ? { diagnostics: parseDiagnostics(s.diagnostics) } : {}
|
|
270
305
|
};
|
|
271
306
|
}
|
|
272
307
|
const reportSchema = { parse: parseReport };
|
|
@@ -458,6 +493,31 @@ window.__ModuleLoader__.load({
|
|
|
458
493
|
"troubleshooting.description": "仅在排查连接或推送问题时使用。",
|
|
459
494
|
"troubleshooting.debugTitle": "调试日志",
|
|
460
495
|
"troubleshooting.debugDescription": "打开后在 DSH host 的终端输出里打印握手、推送等诊断信息;不会打印配对令牌、APNs token 或消息内容。改动立即生效,无需重启。",
|
|
496
|
+
"diagnostics.title": "连接诊断",
|
|
497
|
+
"diagnostics.description": "最近的连接失败事件;可一键复制后发给维护者排查。",
|
|
498
|
+
"diagnostics.recent": "最近失败",
|
|
499
|
+
"diagnostics.empty": "最近没有失败记录",
|
|
500
|
+
"diagnostics.staleHost": "宿主插件版本较旧,没有连接诊断数据,请更新 dsh-deeppilot。",
|
|
501
|
+
"diagnostics.copy": "复制诊断信息",
|
|
502
|
+
"diagnostics.copied": "诊断信息已复制",
|
|
503
|
+
"diagnostics.copyFailed": "复制诊断信息失败:",
|
|
504
|
+
"diagnostics.item": "{time} · {kind} · {reason}",
|
|
505
|
+
"diagnostics.attempt": "尝试 {id}",
|
|
506
|
+
"diagnostics.source": "来源 {label}",
|
|
507
|
+
"diagnostics.closeCode": "关闭码 {code}",
|
|
508
|
+
"diagnostics.duration": "耗时 {ms}ms",
|
|
509
|
+
"diagnostics.startedAt": "启动于 {time}",
|
|
510
|
+
"diagnostics.reportTitle": "DeepPilot 诊断信息",
|
|
511
|
+
"diagnostics.reportGeneratedAt": "生成时间",
|
|
512
|
+
"diagnostics.reportVersion": "插件版本",
|
|
513
|
+
"diagnostics.reportEnabled": "总开关",
|
|
514
|
+
"diagnostics.reportLocal": "局域网",
|
|
515
|
+
"diagnostics.reportRemote": "公网",
|
|
516
|
+
"diagnostics.reportConnections": "当前连接",
|
|
517
|
+
"diagnostics.reportFailureHeader": "最近失败",
|
|
518
|
+
"diagnostics.reportNone": "(无)",
|
|
519
|
+
"diagnostics.on": "已启用",
|
|
520
|
+
"diagnostics.off": "已关闭",
|
|
461
521
|
"system.summary": "系统信息",
|
|
462
522
|
"system.title": "运行信息",
|
|
463
523
|
"system.description": "插件、协议与连接端点的技术信息。",
|
|
@@ -676,6 +736,31 @@ window.__ModuleLoader__.load({
|
|
|
676
736
|
"troubleshooting.description": "Use these only when diagnosing connection or push issues.",
|
|
677
737
|
"troubleshooting.debugTitle": "Debug logging",
|
|
678
738
|
"troubleshooting.debugDescription": "When on, prints handshake and push diagnostics to the DSH host’s terminal output; never prints pairing tokens, APNs tokens, or message content. Takes effect immediately, no restart needed.",
|
|
739
|
+
"diagnostics.title": "Connection diagnostics",
|
|
740
|
+
"diagnostics.description": "Recent connection failures; copy them for troubleshooting.",
|
|
741
|
+
"diagnostics.recent": "Recent failures",
|
|
742
|
+
"diagnostics.empty": "No recent failures",
|
|
743
|
+
"diagnostics.staleHost": "This host is too old to report connection diagnostics. Update dsh-deeppilot.",
|
|
744
|
+
"diagnostics.copy": "Copy diagnostics",
|
|
745
|
+
"diagnostics.copied": "Diagnostics copied",
|
|
746
|
+
"diagnostics.copyFailed": "Copying diagnostics failed: ",
|
|
747
|
+
"diagnostics.item": "{time} · {kind} · {reason}",
|
|
748
|
+
"diagnostics.attempt": "attempt {id}",
|
|
749
|
+
"diagnostics.source": "source {label}",
|
|
750
|
+
"diagnostics.closeCode": "close code {code}",
|
|
751
|
+
"diagnostics.duration": "{ms}ms",
|
|
752
|
+
"diagnostics.startedAt": "Started {time}",
|
|
753
|
+
"diagnostics.reportTitle": "DeepPilot diagnostics",
|
|
754
|
+
"diagnostics.reportGeneratedAt": "Generated",
|
|
755
|
+
"diagnostics.reportVersion": "Plugin version",
|
|
756
|
+
"diagnostics.reportEnabled": "Master switch",
|
|
757
|
+
"diagnostics.reportLocal": "LAN",
|
|
758
|
+
"diagnostics.reportRemote": "Funnel",
|
|
759
|
+
"diagnostics.reportConnections": "Active connections",
|
|
760
|
+
"diagnostics.reportFailureHeader": "Recent failures",
|
|
761
|
+
"diagnostics.reportNone": "(none)",
|
|
762
|
+
"diagnostics.on": "enabled",
|
|
763
|
+
"diagnostics.off": "disabled",
|
|
679
764
|
"system.summary": "System information",
|
|
680
765
|
"system.title": "Runtime information",
|
|
681
766
|
"system.description": "Technical details about the plugin, protocol, and endpoints.",
|
|
@@ -3877,9 +3962,11 @@ window.__ModuleLoader__.load({
|
|
|
3877
3962
|
const remote = dotState(input.remoteDot);
|
|
3878
3963
|
const attention = local.attention || remote.attention;
|
|
3879
3964
|
const online = local.online || remote.online;
|
|
3965
|
+
const hasSignal = (dot) => dot !== void 0 && dot !== "";
|
|
3966
|
+
const anySignal = hasSignal(input.localDot) || hasSignal(input.remoteDot);
|
|
3880
3967
|
return {
|
|
3881
|
-
kind: !input.switchReady ? "loading" : !input.enabled ? "off" : attention ? "attention" : online ? "ready" : "loading",
|
|
3882
|
-
dot: !input.enabled ? "" : attention ? " pbb-dotBad" : online ? " pbb-dotOk" : " pbb-dotWarn"
|
|
3968
|
+
kind: !input.switchReady ? "loading" : !input.enabled ? "off" : attention ? "attention" : online ? "ready" : anySignal ? "loading" : "off",
|
|
3969
|
+
dot: !input.enabled || !attention && !online && !anySignal ? "" : attention ? " pbb-dotBad" : online ? " pbb-dotOk" : " pbb-dotWarn"
|
|
3883
3970
|
};
|
|
3884
3971
|
}
|
|
3885
3972
|
const LOCAL_PORT_RANGE = {
|
|
@@ -4072,6 +4159,11 @@ window.__ModuleLoader__.load({
|
|
|
4072
4159
|
const [deviceMessage, setDeviceMessage] = (0, react.useState)("");
|
|
4073
4160
|
const [editingDeviceId, setEditingDeviceId] = (0, react.useState)(null);
|
|
4074
4161
|
const [deviceNameDraft, setDeviceNameDraft] = (0, react.useState)("");
|
|
4162
|
+
/** 复制诊断信息的结果提示;failed 决定是否染红。 */
|
|
4163
|
+
const [diagnosticsCopy, setDiagnosticsCopy] = (0, react.useState)({
|
|
4164
|
+
message: "",
|
|
4165
|
+
failed: false
|
|
4166
|
+
});
|
|
4075
4167
|
(0, react.useEffect)(() => {
|
|
4076
4168
|
if (!can("refresh")) return;
|
|
4077
4169
|
const refresh = props.refresh;
|
|
@@ -4481,6 +4573,62 @@ window.__ModuleLoader__.load({
|
|
|
4481
4573
|
});
|
|
4482
4574
|
const serviceStatusText = translateWith(props.t, SERVICE_STATUS_KEY[service.kind]);
|
|
4483
4575
|
const serviceDot = service.dot;
|
|
4576
|
+
/** 一条失败事件的单行文本:时间 · 类型 · 分因,附带可选排查字段。 */
|
|
4577
|
+
const diagnosticLine = (failure) => {
|
|
4578
|
+
const base = translateWith(props.t, "diagnostics.item", {
|
|
4579
|
+
time: new Date(failure.ts).toLocaleString(),
|
|
4580
|
+
kind: failure.kind,
|
|
4581
|
+
reason: failure.reason
|
|
4582
|
+
});
|
|
4583
|
+
const extras = [];
|
|
4584
|
+
if (failure.attemptId !== void 0) extras.push(translateWith(props.t, "diagnostics.attempt", { id: failure.attemptId }));
|
|
4585
|
+
if (failure.closeCode !== void 0) extras.push(translateWith(props.t, "diagnostics.closeCode", { code: failure.closeCode }));
|
|
4586
|
+
if (failure.durationMs !== void 0) extras.push(translateWith(props.t, "diagnostics.duration", { ms: failure.durationMs }));
|
|
4587
|
+
if (failure.source !== void 0) extras.push(translateWith(props.t, "diagnostics.source", { label: failure.source }));
|
|
4588
|
+
return extras.length === 0 ? base : base + " · " + extras.join(" · ");
|
|
4589
|
+
};
|
|
4590
|
+
/**
|
|
4591
|
+
* 复制诊断信息:报告里的结构化事实(版本 / 相位 / 连接数 / 最近失败)。
|
|
4592
|
+
* 走既有的 report 通道与 i18n;内容不含配对码、公钥、APNs token 与消息正文。
|
|
4593
|
+
*/
|
|
4594
|
+
const copyDiagnostics = () => {
|
|
4595
|
+
if (report === null) return;
|
|
4596
|
+
const enabledText = report.enabled ? translateWith(props.t, "diagnostics.on") : translateWith(props.t, "diagnostics.off");
|
|
4597
|
+
const failures = report.diagnostics?.recentFailures ?? [];
|
|
4598
|
+
const lines = [
|
|
4599
|
+
translateWith(props.t, "diagnostics.reportTitle"),
|
|
4600
|
+
translateWith(props.t, "diagnostics.reportGeneratedAt") + ": " + (/* @__PURE__ */ new Date()).toLocaleString(),
|
|
4601
|
+
translateWith(props.t, "diagnostics.reportVersion") + ": v" + report.pluginVersion + " (" + translateWith(props.t, "advanced.protocolVersion") + " v" + String(report.protocolVersion) + ")",
|
|
4602
|
+
translateWith(props.t, "diagnostics.reportEnabled") + ": " + enabledText,
|
|
4603
|
+
translateWith(props.t, "diagnostics.reportLocal") + ": " + report.local.phase + (report.local.phase === "online" ? " :" + String(report.local.port) : ""),
|
|
4604
|
+
translateWith(props.t, "diagnostics.reportRemote") + ": " + report.remote.phase,
|
|
4605
|
+
translateWith(props.t, "diagnostics.reportConnections") + ": " + String(report.activeConnections)
|
|
4606
|
+
];
|
|
4607
|
+
if (report.diagnostics !== void 0) lines.push(translateWith(props.t, "diagnostics.startedAt", { time: new Date(report.diagnostics.startedAt).toLocaleString() }));
|
|
4608
|
+
lines.push(translateWith(props.t, "diagnostics.reportFailureHeader") + ":");
|
|
4609
|
+
if (failures.length === 0) lines.push("- " + translateWith(props.t, "diagnostics.reportNone"));
|
|
4610
|
+
else for (const failure of failures.slice(-5).reverse()) lines.push("- " + diagnosticLine(failure));
|
|
4611
|
+
writeClipboard(props.t, lines.join("\n")).then(() => {
|
|
4612
|
+
setDiagnosticsCopy({
|
|
4613
|
+
message: translateWith(props.t, "diagnostics.copied"),
|
|
4614
|
+
failed: false
|
|
4615
|
+
});
|
|
4616
|
+
}, (error) => {
|
|
4617
|
+
setDiagnosticsCopy({
|
|
4618
|
+
message: translateWith(props.t, "diagnostics.copyFailed") + (error instanceof Error ? error.message : String(error)),
|
|
4619
|
+
failed: true
|
|
4620
|
+
});
|
|
4621
|
+
});
|
|
4622
|
+
};
|
|
4623
|
+
const diagnosticsSection = report === null ? null : (0, react.createElement)("div", { className: "pbb-helpSection" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("div", { className: "pbb-switchText" }, (0, react.createElement)("span", { className: "pbb-testTitle" }, translateWith(props.t, "diagnostics.title")), (0, react.createElement)("span", { className: "pbb-testDescription" }, translateWith(props.t, "diagnostics.description"))), (0, react.createElement)("button", {
|
|
4624
|
+
type: "button",
|
|
4625
|
+
className: "pbb-action",
|
|
4626
|
+
onClick: copyDiagnostics,
|
|
4627
|
+
"data-testid": "diagnosticsCopy"
|
|
4628
|
+
}, translateWith(props.t, "diagnostics.copy"))), diagnosticsCopy.message ? (0, react.createElement)("p", { className: "pbb-diag" + (diagnosticsCopy.failed ? " pbb-diagBad" : "") }, diagnosticsCopy.message) : null, report.diagnostics === void 0 ? (0, react.createElement)("p", { className: "pbb-diag" }, translateWith(props.t, "diagnostics.staleHost")) : report.diagnostics.recentFailures.length === 0 ? (0, react.createElement)("p", { className: "pbb-diag" }, translateWith(props.t, "diagnostics.empty")) : (0, react.createElement)("div", null, (0, react.createElement)("span", { className: "pbb-label" }, translateWith(props.t, "diagnostics.recent")), report.diagnostics.recentFailures.slice(-5).reverse().map((failure, index) => (0, react.createElement)("p", {
|
|
4629
|
+
className: "pbb-diag",
|
|
4630
|
+
key: String(index)
|
|
4631
|
+
}, diagnosticLine(failure)))));
|
|
4484
4632
|
return (0, react.createElement)("div", { className: "pbb-section" }, (0, react.createElement)("header", { className: "pbb-pageHeader" }, (0, react.createElement)("div", null, (0, react.createElement)("h2", { className: "pbb-title" }, "DeepPilot"), (0, react.createElement)("p", { className: "pbb-intro" }, translateWith(props.t, "meta.intro"))), (0, react.createElement)("button", {
|
|
4485
4633
|
type: "button",
|
|
4486
4634
|
className: "pbb-refresh",
|
|
@@ -4631,7 +4779,7 @@ window.__ModuleLoader__.load({
|
|
|
4631
4779
|
}, (step.ok ? "✓ " : "✗ ") + (step.id === "health" ? translateWith(props.t, "push.relayStep.health") : translateWith(props.t, "push.relayStep.enroll")) + (step.latencyMs !== void 0 ? ` (${String(step.latencyMs)}ms)` : "") + " — " + step.message))), pushTestError ? (0, react.createElement)("p", { className: "pbb-diag pbb-diagBad" }, pushTestError) : null, pushTestResult === null ? (0, react.createElement)("p", { className: "pbb-diag" }, translateWith(props.t, "push.pushDefault")) : (0, react.createElement)("div", { className: "pbb-helpBody" }, (0, react.createElement)("div", { className: "pbb-row" }, (0, react.createElement)("code", { className: "pbb-token " + (pushTestResult.overall === "sent" ? "pbb-ok" : pushTestResult.overall === "failed" ? "pbb-bad" : "") }, pushTestResult.overall === "sent" ? translateWith(props.t, "push.pushSent") : pushTestResult.overall === "failed" ? translateWith(props.t, "push.pushFailed") : pushTestResult.overall === "no-targets" ? translateWith(props.t, "push.pushNoTargets") : translateWith(props.t, "push.pushNotEnabled"))), pushTestResult.message ? (0, react.createElement)("p", { className: "pbb-diag" }, pushTestResult.message) : null, pushTestResult.results.map((r, index) => (0, react.createElement)("p", {
|
|
4632
4780
|
className: "pbb-diag",
|
|
4633
4781
|
key: String(index)
|
|
4634
|
-
}, (r.outcome === "sent" ? "✓ " : "✗ ") + r.name + " [" + r.environment + "] — " + r.outcome + (r.reason ? "(" + r.reason + ")" : "") + (r.tokenFingerprint ? " token:" + r.tokenFingerprint + "…" : "")))))), report === null ? null : (0, react.createElement)("details", { className: "pbb-card pbb-disclosure" }, (0, react.createElement)("summary", null, (0, react.createElement)("span", { className: "pbb-disclosureTitle" }, translateWith(props.t, "system.summary")), (0, react.createElement)("span", { className: "pbb-disclosureMeta" }, "v" + report.pluginVersion)), (0, react.createElement)("div", { className: "pbb-disclosureBody" }, (0, react.createElement)("div", { className: "pbb-sectionIntro" }, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "system.title")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "system.description"))), (0, react.createElement)("div", { className: "pbb-runtimeList" }, (0, react.createElement)("div", { className: "pbb-runtimeRow" }, (0, react.createElement)("span", { className: "pbb-label" }, "DeepPilot"), (0, react.createElement)("span", { className: "pbb-value" }, "v" + report.pluginVersion)), advancedRows, report.updateAvailable === true ? (0, react.createElement)("div", { className: "pbb-updateRow" }, (0, react.createElement)("span", null, translateWith(props.t, "update.badge")), (0, react.createElement)("a", {
|
|
4782
|
+
}, (r.outcome === "sent" ? "✓ " : "✗ ") + r.name + " [" + r.environment + "] — " + r.outcome + (r.reason ? "(" + r.reason + ")" : "") + (r.tokenFingerprint ? " token:" + r.tokenFingerprint + "…" : "")))), diagnosticsSection)), report === null ? null : (0, react.createElement)("details", { className: "pbb-card pbb-disclosure" }, (0, react.createElement)("summary", null, (0, react.createElement)("span", { className: "pbb-disclosureTitle" }, translateWith(props.t, "system.summary")), (0, react.createElement)("span", { className: "pbb-disclosureMeta" }, "v" + report.pluginVersion)), (0, react.createElement)("div", { className: "pbb-disclosureBody" }, (0, react.createElement)("div", { className: "pbb-sectionIntro" }, (0, react.createElement)("h3", { className: "pbb-cardTitle" }, translateWith(props.t, "system.title")), (0, react.createElement)("p", { className: "pbb-cardDescription" }, translateWith(props.t, "system.description"))), (0, react.createElement)("div", { className: "pbb-runtimeList" }, (0, react.createElement)("div", { className: "pbb-runtimeRow" }, (0, react.createElement)("span", { className: "pbb-label" }, "DeepPilot"), (0, react.createElement)("span", { className: "pbb-value" }, "v" + report.pluginVersion)), advancedRows, report.updateAvailable === true ? (0, react.createElement)("div", { className: "pbb-updateRow" }, (0, react.createElement)("span", null, translateWith(props.t, "update.badge")), (0, react.createElement)("a", {
|
|
4635
4783
|
className: "pbb-action pbb-buttonPrimary",
|
|
4636
4784
|
href: typeof report.releaseUrl === "string" && /^https:\/\//.test(report.releaseUrl) ? report.releaseUrl : "https://github.com/Mars-Sea/dsh-deeppilot/releases",
|
|
4637
4785
|
target: "_blank",
|