dsh-deeppilot 0.9.7 → 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/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",