@linxin666/dsh-remote-web-ui 0.3.2 → 0.3.4

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 (78) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +22 -8
  3. package/README.zh.md +12 -7
  4. package/assets/mobile-service-worker.js +1 -2
  5. package/lib/client.js +177 -19
  6. package/lib/client.js.map +1 -1
  7. package/lib/index.js +222 -15
  8. package/lib/mobile.js +64 -13
  9. package/lib/mobile.js.map +1 -1
  10. package/lib/types/client/PluginSettingsCard.d.ts +2 -0
  11. package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
  12. package/lib/types/client/PluginSettingsCard.js +2 -2
  13. package/lib/types/client/RemoteEntry.js +1 -1
  14. package/lib/types/client/UpdatePanel.d.ts.map +1 -1
  15. package/lib/types/client/UpdatePanel.js +20 -3
  16. package/lib/types/client/index.d.ts +1 -1
  17. package/lib/types/client/index.d.ts.map +1 -1
  18. package/lib/types/client/index.js +4 -0
  19. package/lib/types/client/locales.d.ts +12 -0
  20. package/lib/types/client/locales.d.ts.map +1 -1
  21. package/lib/types/client/locales.js +22 -10
  22. package/lib/types/client/settings-form.d.ts +1 -1
  23. package/lib/types/client/settings-form.js +1 -1
  24. package/lib/types/client/telemetry.d.ts +27 -0
  25. package/lib/types/client/telemetry.d.ts.map +1 -0
  26. package/lib/types/client/telemetry.js +97 -0
  27. package/lib/types/dsh-home.d.ts.map +1 -1
  28. package/lib/types/dsh-home.js +9 -3
  29. package/lib/types/http.d.ts +1 -1
  30. package/lib/types/http.js +1 -1
  31. package/lib/types/index.d.ts.map +1 -1
  32. package/lib/types/index.js +34 -5
  33. package/lib/types/loopback.d.ts +1 -1
  34. package/lib/types/loopback.js +1 -1
  35. package/lib/types/mobile/api.d.ts +7 -0
  36. package/lib/types/mobile/api.d.ts.map +1 -1
  37. package/lib/types/mobile/api.js +9 -3
  38. package/lib/types/mobile/mobile-styles.d.ts +1 -1
  39. package/lib/types/mobile/mobile-styles.d.ts.map +1 -1
  40. package/lib/types/mobile/mobile-styles.js +10 -0
  41. package/lib/types/mobile/views/ChatView.d.ts +1 -0
  42. package/lib/types/mobile/views/ChatView.d.ts.map +1 -1
  43. package/lib/types/mobile/views/ChatView.js +32 -5
  44. package/lib/types/mobile/views/ChatView.stream.test.js +28 -2
  45. package/lib/types/mobile/views/ChatView.test.js +152 -2
  46. package/lib/types/mobile-api.d.ts +14 -0
  47. package/lib/types/mobile-api.d.ts.map +1 -1
  48. package/lib/types/mobile-api.js +56 -1
  49. package/lib/types/mobile-routes.d.ts.map +1 -1
  50. package/lib/types/mobile-routes.js +25 -2
  51. package/lib/types/mount-once.d.ts +1 -1
  52. package/lib/types/mount-once.js +2 -2
  53. package/lib/types/update.d.ts +35 -4
  54. package/lib/types/update.d.ts.map +1 -1
  55. package/lib/types/update.js +98 -4
  56. package/package.json +4 -2
  57. package/src/client/PluginSettingsCard.tsx +4 -2
  58. package/src/client/RemoteEntry.tsx +1 -1
  59. package/src/client/UpdatePanel.tsx +43 -3
  60. package/src/client/index.ts +6 -1
  61. package/src/client/locales.ts +22 -10
  62. package/src/client/remote.module.css +56 -3
  63. package/src/client/settings-card.module.css +5 -0
  64. package/src/client/settings-form.ts +1 -1
  65. package/src/client/telemetry.ts +105 -0
  66. package/src/dsh-home.ts +9 -3
  67. package/src/http.ts +1 -1
  68. package/src/index.ts +36 -4
  69. package/src/loopback.ts +1 -1
  70. package/src/mobile/api.ts +10 -3
  71. package/src/mobile/mobile-styles.ts +10 -0
  72. package/src/mobile/views/ChatView.stream.test.tsx +34 -2
  73. package/src/mobile/views/ChatView.test.tsx +177 -2
  74. package/src/mobile/views/ChatView.tsx +44 -6
  75. package/src/mobile-api.ts +61 -0
  76. package/src/mobile-routes.ts +24 -2
  77. package/src/mount-once.ts +2 -2
  78. package/src/update.ts +117 -4
@@ -22,6 +22,8 @@ export interface PluginSettingsCardProps<TKey extends string = string> {
22
22
  titleKey: TKey;
23
23
  /** Locale key of the line describing what this plugin's settings govern. */
24
24
  descriptionKey: TKey;
25
+ /** Optional rich header description rendered instead of the plain t(descriptionKey) text (the plain text stays as the tooltip). */
26
+ descriptionNode?: ReactNode;
25
27
  /** The card's form state: availability, writability, and what a save would do. */
26
28
  state: CardShell;
27
29
  /** Write every staged edit. */
@@ -1 +1 @@
1
- {"version":3,"file":"PluginSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PluginSettingsCard.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PluginSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PluginSettingsCard.tsx"],"names":[],"mappings":"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"}
@@ -40,8 +40,8 @@ export function PluginSettingsCard(props) {
40
40
  // With alwaysOpen the nav entry already provides the selection, so the
41
41
  // header is a static title row instead of a disclosure button.
42
42
  const header = alwaysOpen === true
43
- ? (_jsxs("div", { className: css.headerStatic, children: [_jsxs("span", { className: css.headText, children: [_jsx("span", { className: css.name, title: title, children: title }), _jsx("span", { className: css.description, title: description, children: description })] }), state.dirty ? _jsx("span", { className: css.pending, title: props.t('settings.unsaved'), children: props.t('settings.unsaved') }) : null] }))
44
- : (_jsxs("button", { type: "button", className: css.header, "aria-expanded": open, "aria-label": `${props.t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`, onClick: () => { setOpen(!open); }, children: [_jsxs("span", { className: css.headText, children: [_jsx("span", { className: css.name, title: title, children: title }), _jsx("span", { className: css.description, title: description, children: description })] }), state.dirty ? _jsx("span", { className: css.pending, title: props.t('settings.unsaved'), children: props.t('settings.unsaved') }) : null, _jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: open ? `${css.chevron} ${css.chevronOpen}` : css.chevron, children: _jsx("path", { d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z", fill: "currentColor" }) })] }));
43
+ ? (_jsxs("div", { className: css.headerStatic, children: [_jsxs("span", { className: css.headText, children: [_jsx("span", { className: css.name, title: title, children: title }), _jsx("span", { className: css.description, title: description, children: props.descriptionNode ?? description })] }), state.dirty ? _jsx("span", { className: css.pending, title: props.t('settings.unsaved'), children: props.t('settings.unsaved') }) : null] }))
44
+ : (_jsxs("button", { type: "button", className: css.header, "aria-expanded": open, "aria-label": `${props.t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`, onClick: () => { setOpen(!open); }, children: [_jsxs("span", { className: css.headText, children: [_jsx("span", { className: css.name, title: title, children: title }), _jsx("span", { className: css.description, title: description, children: props.descriptionNode ?? description })] }), state.dirty ? _jsx("span", { className: css.pending, title: props.t('settings.unsaved'), children: props.t('settings.unsaved') }) : null, _jsx("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: open ? `${css.chevron} ${css.chevronOpen}` : css.chevron, children: _jsx("path", { d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z", fill: "currentColor" }) })] }));
45
45
  // The namespace exists but the Host does not serve it to this client (the
46
46
  // official settings allowlist omits third-party namespaces): show a card
47
47
  // that explains the gap instead of vanishing, so a missing card never
@@ -4,7 +4,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
4
4
  * trigger beside the settings button, and the pairing panel modal. Owns the
5
5
  * panel behavior — token minting on open, the status SSE subscription,
6
6
  * stop/refresh/copy — and renders the pure {@link RemotePanel} body. The
7
- * update seat (the dsh-web-ui self-update flow) rides the same footer row,
7
+ * update seat (the dsh-web self-update flow) rides the same footer row,
8
8
  * rendered by {@link UpdateEntry}. Component-local state per the client
9
9
  * stack rules: nothing here survives remounts or crosses entries.
10
10
  */
@@ -1 +1 @@
1
- {"version":3,"file":"UpdatePanel.d.ts","sourceRoot":"","sources":["../../../src/client/UpdatePanel.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAEnE,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAGjE,0DAA0D;AAC1D,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvD,qDAAqD;AACrD,MAAM,WAAW,gBAAgB;IAC/B,CAAC,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAA;IACxB,IAAI,EAAE,UAAU,CAAA;IAChB,OAAO,IAAI,IAAI,CAAA;IACf,8CAA8C;IAC9C,SAAS,IAAI,IAAI,CAAA;IACjB,2EAA2E;IAC3E,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAA;CAC1C;AAYD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,EAAE,gBAAgB,+BAiD3F"}
1
+ {"version":3,"file":"UpdatePanel.d.ts","sourceRoot":"","sources":["../../../src/client/UpdatePanel.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAEnE,OAAO,KAAK,EAAsB,eAAe,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAGrF,0DAA0D;AAC1D,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,YAAY,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,eAAe,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvD,qDAAqD;AACrD,MAAM,WAAW,gBAAgB;IAC/B,CAAC,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAA;IACxB,IAAI,EAAE,UAAU,CAAA;IAChB,OAAO,IAAI,IAAI,CAAA;IACf,8CAA8C;IAC9C,SAAS,IAAI,IAAI,CAAA;IACjB,2EAA2E;IAC3E,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAAA;CAC1C;AAYD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,EAAE,gBAAgB,+BAiD3F"}
@@ -18,7 +18,7 @@ export function UpdatePanel({ t, view, onClose, onRecheck, onStartUpdate }) {
18
18
  const status = view.kind === "result" || view.kind === "updating" ? view.status : undefined;
19
19
  const title = view.kind === "done" && view.result.ok ? t("update.done") : t("update.title");
20
20
  const subtitle = subtitleOf(t, view);
21
- return (_jsxs("div", { className: css.panel, role: "dialog", "aria-modal": "true", "aria-label": title, children: [_jsxs("div", { className: css.header, children: [_jsxs("div", { className: css.heading, children: [_jsx("h2", { className: css.title, children: title }), subtitle !== undefined && _jsx("p", { className: css.subtitle, children: subtitle })] }), _jsx("button", { type: "button", className: css.close, "aria-label": t("update.close"), onClick: onClose, children: _jsx(IconCloseOutline16, {}) })] }), view.kind === "checking" && _jsx("p", { className: css.updateStatus, children: t("update.checking") }), view.kind === "result" && status !== undefined && _jsx(ResultBody, { t: t, status: status }), view.kind === "result" && status !== undefined && status.mode === "npm" && status.outdated && status.error === undefined && (_jsx("div", { className: css.updateActions, children: _jsx("button", { type: "button", className: css.updateRetry, onClick: () => onStartUpdate(status), children: t("update.start") }) })), view.kind === "updating" && status !== undefined && (_jsxs("div", { children: [_jsx("p", { className: css.updateStatus, children: t("update.updating", { name: anchorName(status) ?? "", version: anchorLatest(status) ?? "" }) }), _jsx("p", { className: css.updateDetail, children: t("update.cooldownNotice") }), _jsx(PackageList, { status: status })] })), view.kind === "done" && _jsx(DoneBody, { t: t, result: view.result }), view.kind === "error" && (_jsxs("div", { children: [_jsx("p", { className: css.updateError, children: view.message }), view.detail !== undefined && _jsx("pre", { className: css.updateOutput, children: view.detail })] })), (view.kind === "done" || view.kind === "error") && (_jsx("div", { className: css.updateActions, children: _jsxs("button", { type: "button", className: css.updateRetry, onClick: onRecheck, children: [_jsx(IconRefreshOutline16, {}), " ", t("update.retry")] }) }))] }));
21
+ return (_jsxs("div", { className: css.panel, role: "dialog", "aria-modal": "true", "aria-label": title, children: [_jsxs("div", { className: css.header, children: [_jsxs("div", { className: css.heading, children: [_jsx("h2", { className: css.title, children: title }), subtitle !== undefined && _jsx("p", { className: css.subtitle, children: subtitle })] }), _jsx("button", { type: "button", className: css.close, "aria-label": t("update.close"), onClick: onClose, children: _jsx(IconCloseOutline16, {}) })] }), view.kind === "checking" && _jsx("p", { className: css.updateStatus, children: t("update.checking") }), view.kind === "result" && status !== undefined && _jsx(ResultBody, { t: t, status: status }), view.kind === "result" && status !== undefined && status.mode === "npm" && status.outdated && status.error === undefined && (_jsx("div", { className: css.updateActions, children: _jsx("button", { type: "button", className: css.updateRetry, onClick: () => onStartUpdate(status), children: t("update.start") }) })), view.kind === "updating" && status !== undefined && (_jsxs("div", { children: [_jsx("p", { className: css.updateStatus, children: t("update.updating", { name: anchorName(status) ?? "", version: anchorLatest(status) ?? "" }) }), _jsx("p", { className: css.updateDetail, children: t("update.cooldownNotice") }), _jsx(PackageSummary, { t: t, status: status })] })), view.kind === "done" && _jsx(DoneBody, { t: t, result: view.result }), view.kind === "error" && (_jsxs("div", { children: [_jsx("p", { className: css.updateError, children: view.message }), view.detail !== undefined && _jsx("pre", { className: css.updateOutput, children: view.detail })] })), (view.kind === "done" || view.kind === "error") && (_jsx("div", { className: css.updateActions, children: _jsxs("button", { type: "button", className: css.updateRetry, onClick: onRecheck, children: [_jsx(IconRefreshOutline16, {}), " ", t("update.retry")] }) }))] }));
22
22
  }
23
23
  /** The subtitle copy per view state (absent on plain results). */
24
24
  function subtitleOf(t, view) {
@@ -50,9 +50,26 @@ function ResultBody({ t, status }) {
50
50
  return (_jsxs("div", { children: [_jsx("p", { className: css.updateStatus, children: t("update.offline") }), _jsx("p", { className: css.updateDetail, children: t("update.offlineDetail") })] }));
51
51
  }
52
52
  if (status.outdated) {
53
- return (_jsxs("div", { children: [_jsx("p", { className: css.updateStatus, children: t("update.found") }), _jsx("p", { className: css.updateDetail, children: anchor !== undefined ? t("update.foundDetail", { name: anchor, version: latest ?? "" }) : "" }), _jsx("p", { className: css.updateDetail, children: t("update.cooldownNotice") })] }));
53
+ return (_jsxs("div", { children: [_jsx("p", { className: css.updateStatus, children: t("update.found") }), _jsx("p", { className: css.updateDetail, children: anchor !== undefined ? t("update.foundDetail", { name: anchor, version: latest ?? "" }) : "" }), _jsx("p", { className: css.updateDetail, children: t("update.cooldownNotice") }), _jsx(PackageSummary, { t: t, status: status })] }));
54
54
  }
55
- return (_jsxs("div", { children: [_jsx("p", { className: css.updateStatus, children: t("update.upToDate") }), anchor !== undefined && latest !== undefined && (_jsx("p", { className: css.updateDetail, children: t("update.upToDateDetail", { name: anchor, version: latest }) })), _jsx(PackageList, { status: status })] }));
55
+ return (_jsxs("div", { children: [_jsx("p", { className: css.updateStatus, children: t("update.upToDate") }), anchor !== undefined && latest !== undefined && (_jsx("p", { className: css.updateDetail, children: t("update.upToDateDetail", { name: anchor, version: latest }) })), _jsx(PackageSummary, { t: t, status: status })] }));
56
+ }
57
+ /** Release-note summary when available; otherwise fall back to the package list. */
58
+ function PackageSummary({ t, status }) {
59
+ if (status.notes === undefined)
60
+ return _jsx(PackageList, { status: status });
61
+ return (_jsxs("div", { children: [_jsx(ReleaseNotes, { t: t, notes: status.notes }), _jsxs("details", { className: css.updateVersions, children: [_jsx("summary", { className: css.updateVersionsSummary, children: t("update.componentVersions") }), _jsx(PackageList, { status: status })] })] }));
62
+ }
63
+ /** Render GitHub Release sections as a compact three-group list. */
64
+ function ReleaseNotes({ t, notes }) {
65
+ const sections = [
66
+ { key: 'features', title: t("update.releaseFeatures"), items: notes.features },
67
+ { key: 'fixes', title: t("update.releaseFixes"), items: notes.fixes },
68
+ { key: 'other', title: t("update.releaseOther"), items: notes.other },
69
+ ].filter(section => section.items.length > 0);
70
+ if (sections.length === 0)
71
+ return _jsx("p", { className: css.updateDetail, children: t("update.releaseUnavailable") });
72
+ return (_jsxs("div", { className: css.updateNotes, children: [_jsx("h3", { className: css.updateNotesTitle, children: t("update.releaseNotes", { version: notes.version }) }), sections.map(section => (_jsxs("section", { className: css.updateNoteSection, children: [_jsx("h4", { className: css.updateNoteHeading, children: section.title }), _jsx("ul", { className: css.updateNoteList, children: section.items.map((item, index) => (_jsx("li", { className: css.updateNoteItem, children: item }, index))) })] }, section.key)))] }));
56
73
  }
57
74
  /** The per-package current → latest comparison list. */
58
75
  function PackageList({ status }) {
@@ -49,7 +49,7 @@ export interface SettingsPluginItemOwnerProps {
49
49
  declare module '@deepseek-ai/cordis' {
50
50
  interface Context {
51
51
  /**
52
- * Optional rc.6 compatibility binder provided by dsh-web-ui-settings;
52
+ * Optional rc.6 compatibility binder provided by dsh-web-settings;
53
53
  * absent when that group plugin is not installed, so callers fall back to
54
54
  * the official settings scope.
55
55
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;AAa7G,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AAarD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACrE,YAAY,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AACnE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC7C,YAAY,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAC/F,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAErE,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,0CAA0C;QAC1C,MAAM,EAAE,SAAS,CAAA;KAClB;IAED,UAAU,OAAO;QACf;;;;WAIG;QACH,gBAAgB,EAAE;YAAE,IAAI,EAAE,QAAQ,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,uBAAuB,CAAA;SAAE,CAAA;QACnF;;;;;WAKG;QACH,oBAAoB,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,4BAA4B,CAAA;SAAE,CAAA;KAC3F;CACF;AAED,4GAA4G;AAC5G,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,IAAI,EAAE,OAAO,CAAA;CACd;AAED,mEAAmE;AACnE,MAAM,WAAW,4BAA4B;IAC3C,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,KAAK,CAAA;CACjB;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf;;;;WAIG;QACH,aAAa,CAAC,EAAE;YAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;SAAE,CAAA;KAC1E;CACF;AAYD,wCAAwC;AACxC,eAAO,MAAM,MAAM,UAA+D,CAAA;AAElF;;;GAGG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAoO9C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;AAa7G,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AAcrD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACrE,YAAY,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AACnE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC7C,YAAY,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAC/F,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAErE,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,0CAA0C;QAC1C,MAAM,EAAE,SAAS,CAAA;KAClB;IAED,UAAU,OAAO;QACf;;;;WAIG;QACH,gBAAgB,EAAE;YAAE,IAAI,EAAE,QAAQ,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,uBAAuB,CAAA;SAAE,CAAA;QACnF;;;;;WAKG;QACH,oBAAoB,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,4BAA4B,CAAA;SAAE,CAAA;KAC3F;CACF;AAED,4GAA4G;AAC5G,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,IAAI,EAAE,OAAO,CAAA;CACd;AAED,mEAAmE;AACnE,MAAM,WAAW,4BAA4B;IAC3C,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,KAAK,CAAA;CACjB;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf;;;;WAIG;QACH,aAAa,CAAC,EAAE;YAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;SAAE,CAAA;KAC1E;CACF;AAYD,wCAAwC;AACxC,eAAO,MAAM,MAAM,UAA+D,CAAA;AAElF;;;GAGG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAwO9C"}
@@ -17,6 +17,7 @@ import { PAIR_FAILED_MARKER, runPairBootFlow } from "./deep-link.js";
17
17
  import { readPairGatePolicy, sendHeartbeat } from "./pair-api.js";
18
18
  import { channelTransition, installRemoteChannel, isLoopbackHostname, remoteChannelRequired, REMOTE_CHANNEL_BOOT_GLOBAL, } from "./remote-channel.js";
19
19
  import { FenceNotice } from "./FenceNotice.js";
20
+ import { reportDailyHeartbeat } from "./telemetry.js";
20
21
  /** Dictionary namespace owned by this plugin. */
21
22
  const NS = 'remote';
22
23
  /** Settings namespace the remote-control card edits (the Host plugin registers it). */
@@ -30,6 +31,9 @@ export const inject = ['slots', 'locale', 'connection', 'settingsScope', 'remote
30
31
  * @param ctx - client root context.
31
32
  */
32
33
  export function apply(ctx) {
34
+ // Anonymous install heartbeat (docs/telemetry.md): one beat per browser per
35
+ // UTC day, package name only, silent failure.
36
+ reportDailyHeartbeat([{ name: '@linxin666/dsh-remote-web-ui' }]);
33
37
  ctx.effect(() => {
34
38
  try {
35
39
  return ctx.locale.register(NS, { zh, en });
@@ -120,6 +120,12 @@ export declare const zh: {
120
120
  'update.unmountedDetail': string;
121
121
  'update.error': string;
122
122
  'update.packages': string;
123
+ 'update.releaseNotes': string;
124
+ 'update.releaseFeatures': string;
125
+ 'update.releaseFixes': string;
126
+ 'update.releaseOther': string;
127
+ 'update.releaseUnavailable': string;
128
+ 'update.componentVersions': string;
123
129
  'update.retry': string;
124
130
  'update.close': string;
125
131
  'update.error.pnpmMissing': string;
@@ -256,6 +262,12 @@ export declare const en: {
256
262
  'update.unmountedDetail': string;
257
263
  'update.error': string;
258
264
  'update.packages': string;
265
+ 'update.releaseNotes': string;
266
+ 'update.releaseFeatures': string;
267
+ 'update.releaseFixes': string;
268
+ 'update.releaseOther': string;
269
+ 'update.releaseUnavailable': string;
270
+ 'update.componentVersions': string;
259
271
  'update.retry': string;
260
272
  'update.close': string;
261
273
  'update.error.pnpmMissing': string;
@@ -1 +1 @@
1
- {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAEvE,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoImB,CAAA;AAElC,sCAAsC;AACtC,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,EAAE,CAAA;AAEvC,wDAAwD;AACxD,MAAM,MAAM,eAAe,GAAG,SAAS,CAAA;AAEvC,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoIsB,CAAA"}
1
+ {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAEvE,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0ImB,CAAA;AAElC,sCAAsC;AACtC,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,EAAE,CAAA;AAEvC,wDAAwD;AACxD,MAAM,MAAM,eAAe,GAAG,SAAS,CAAA;AAEvC,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0IsB,CAAA"}
@@ -113,21 +113,27 @@ export const zh = {
113
113
  'update.doneDetail': '所有组件已更新到最新版本。',
114
114
  'update.restartHint': '请重启 dsh web 使新版本生效:在终端按 Ctrl+C 停止后重新运行 dsh web。',
115
115
  'update.linkMode': '当前为本地开发模式',
116
- 'update.linkModeDetail': 'dsh-web-ui 或其组件通过本地链接安装(开发模式),无法自动更新。npm 上最新版本:{version}。请同步本地仓库,或移除与全家桶重复的独立链接。',
117
- 'update.missing': '未找到 dsh-web-ui 安装',
118
- 'update.missingDetail': '无法定位已安装的 dsh-web-ui 聚合包。请先运行 dsh plugin --profile <profile> add @linxin666/dsh-web-ui-all。',
116
+ 'update.linkModeDetail': 'dsh-web 或其组件通过本地链接安装(开发模式),无法自动更新。npm 上最新版本:{version}。请同步本地仓库,或移除与全家桶重复的独立链接。',
117
+ 'update.missing': '未找到 dsh-web 安装',
118
+ 'update.missingDetail': '无法定位已安装的 dsh-web 聚合包。请先运行 dsh plugin --profile <profile> add @linxin666/dsh-web-all。',
119
119
  'update.offline': '无法连接更新源',
120
120
  'update.offlineDetail': '网络不可用或更新服务异常,请稍后重试。',
121
121
  'update.unmounted': '更新服务未加载',
122
122
  'update.unmountedDetail': '当前 dsh web 进程运行的插件版本不含更新服务,请重启 dsh web(Ctrl+C 后重新运行)后再试。',
123
123
  'update.error': '更新失败',
124
124
  'update.packages': '组件版本',
125
+ 'update.releaseNotes': 'v{version} 更新说明',
126
+ 'update.releaseFeatures': '新增功能',
127
+ 'update.releaseFixes': '修复',
128
+ 'update.releaseOther': '其他变动',
129
+ 'update.releaseUnavailable': '本次发布说明暂不可用,可前往 GitHub Release 查看。',
130
+ 'update.componentVersions': '查看组件版本',
125
131
  'update.retry': '重新检查',
126
132
  'update.close': '关闭更新面板',
127
133
  'update.error.pnpmMissing': '未找到 pnpm,已尝试 pnpm / corepack / npx 仍不可用。请安装 pnpm 后重启应用(Windows: npm install -g pnpm 或 corepack enable;macOS: brew install pnpm 或 corepack enable)。',
128
134
  'update.error.timeout': '更新超时,已终止安装进程,请重试。',
129
- 'update.error.notFound': '未找到 dsh-web-ui 聚合包安装,无法更新。',
130
- 'update.error.link': 'dsh-web-ui 或其组件使用本地链接,无法自动更新;请同步本地仓库,或移除与全家桶重复的独立链接。',
135
+ 'update.error.notFound': '未找到 dsh-web 聚合包安装,无法更新。',
136
+ 'update.error.link': 'dsh-web 或其组件使用本地链接,无法自动更新;请同步本地仓库,或移除与全家桶重复的独立链接。',
131
137
  'update.error.pnpmFailed': '更新执行失败(pnpm 退出码 {code}),详见下方输出。',
132
138
  'update.error.stale': '更新命令已执行,但安装版本未变化。常见原因:pnpm 11 的 minimumReleaseAge 门禁会静默跳过发布不足 24 小时的新版本。请在 profile 目录(~/.dsh/profiles/<profile>)的 pnpm-workspace.yaml 中配置 minimumReleaseAgeExclude(例如 "@linxin666/*")或 minimumReleaseAge: 0 后重新点击更新。',
133
139
  'update.error.verifyFailed': '更新命令已执行,但无法核对安装后的版本(可能是 registry 探测失败或安装路径变化)。请查看下方输出,或稍后重试。',
@@ -247,21 +253,27 @@ export const en = {
247
253
  'update.doneDetail': 'All components are up to date.',
248
254
  'update.restartHint': 'Restart dsh web for the new version to take effect: stop it with Ctrl+C in the terminal, then run dsh web again.',
249
255
  'update.linkMode': 'Local development mode',
250
- 'update.linkModeDetail': 'dsh-web-ui or one of its components is installed via a local link and cannot be updated remotely. Latest npm release: {version}. Sync the local checkout, or remove a standalone link duplicated by the aggregate.',
251
- 'update.missing': 'dsh-web-ui not found',
252
- 'update.missingDetail': 'Could not locate the installed dsh-web-ui aggregate package. Install it with: dsh plugin --profile <profile> add @linxin666/dsh-web-ui-all',
256
+ 'update.linkModeDetail': 'dsh-web or one of its components is installed via a local link and cannot be updated remotely. Latest npm release: {version}. Sync the local checkout, or remove a standalone link duplicated by the aggregate.',
257
+ 'update.missing': 'dsh-web not found',
258
+ 'update.missingDetail': 'Could not locate the installed dsh-web aggregate package. Install it with: dsh plugin --profile <profile> add @linxin666/dsh-web-all',
253
259
  'update.offline': 'Cannot reach the update source',
254
260
  'update.offlineDetail': 'The network is unavailable or the update service is failing; try again later.',
255
261
  'update.unmounted': 'The update service is not loaded',
256
262
  'update.unmountedDetail': 'The running dsh web process loads a plugin build without the update service; restart dsh web (Ctrl+C, then run dsh web again) and retry.',
257
263
  'update.error': 'Update failed',
258
264
  'update.packages': 'Component versions',
265
+ 'update.releaseNotes': 'v{version} release notes',
266
+ 'update.releaseFeatures': 'New Features',
267
+ 'update.releaseFixes': 'Bug Fixes',
268
+ 'update.releaseOther': 'Other Changes',
269
+ 'update.releaseUnavailable': 'Release notes are unavailable for this version; see the GitHub release instead.',
270
+ 'update.componentVersions': 'View component versions',
259
271
  'update.retry': 'Check again',
260
272
  'update.close': 'Close update panel',
261
273
  'update.error.pnpmMissing': 'pnpm was not found (tried pnpm / corepack / npx). Install pnpm and restart the app (Windows: npm install -g pnpm or corepack enable; macOS: brew install pnpm or corepack enable).',
262
274
  'update.error.timeout': 'The update timed out and the install process was killed; try again.',
263
- 'update.error.notFound': 'The dsh-web-ui aggregate package is not installed; nothing to update.',
264
- 'update.error.link': 'dsh-web-ui or one of its components uses a local link. Sync the checkout, or remove a standalone link duplicated by the aggregate.',
275
+ 'update.error.notFound': 'The dsh-web aggregate package is not installed; nothing to update.',
276
+ 'update.error.link': 'dsh-web or one of its components uses a local link. Sync the checkout, or remove a standalone link duplicated by the aggregate.',
265
277
  'update.error.pnpmFailed': 'The update failed (pnpm exited with code {code}); see the output below.',
266
278
  'update.error.stale': 'The update command ran but the installed versions did not change. Likely cause: the pnpm 11 minimumReleaseAge gate silently skips releases published less than 24 hours ago. Add minimumReleaseAgeExclude (e.g. "@linxin666/*") or set minimumReleaseAge: 0 in pnpm-workspace.yaml under the profile directory (~/.dsh/profiles/<profile>), then run the update again.',
267
279
  'update.error.verifyFailed': 'The update command ran but the installed versions could not be verified afterwards (registry probe failed or the install path changed). Inspect the output below, or retry later.',
@@ -173,7 +173,7 @@ export declare class CardForm<T> {
173
173
  /**
174
174
  * Write every staged edit, then re-seed from what the Host accepted.
175
175
  *
176
- * When the scope carries the optional batch surface (the dsh-web-ui
176
+ * When the scope carries the optional batch surface (the dsh-web
177
177
  * bridge scope), every planned write rides one mutation so cross-field
178
178
  * validate hooks (baseURL+model) judge the batch as a unit instead of
179
179
  * deadlocking on per-field writes. Otherwise the per-field loop runs.
@@ -170,7 +170,7 @@ export class CardForm {
170
170
  /**
171
171
  * Write every staged edit, then re-seed from what the Host accepted.
172
172
  *
173
- * When the scope carries the optional batch surface (the dsh-web-ui
173
+ * When the scope carries the optional batch surface (the dsh-web
174
174
  * bridge scope), every planned write rides one mutation so cross-field
175
175
  * validate hooks (baseURL+model) judge the batch as a unit instead of
176
176
  * deadlocking on per-field writes. Otherwise the per-field loop runs.
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Anonymous install telemetry shared by the DSH Web UI family plugins.
3
+ *
4
+ * Once per UTC day per browser, each wired plugin sends one heartbeat to
5
+ * the dsh-market edge API listing its package name (and version when known).
6
+ * The payload carries no conversation data and no identifiers beyond a random
7
+ * UUID generated in localStorage; the server hashes it with a deployment salt
8
+ * before storage and never persists IP addresses. Sends are fire-and-forget:
9
+ * failures stay silent and simply retry on a later mount or day. The system
10
+ * is documented in docs/telemetry.md.
11
+ */
12
+ export type TelemetryChannel = 'market' | 'npm' | 'unknown';
13
+ export interface TelemetryItem {
14
+ /** npm package name or asset id, e.g. "@linxin666/dsh-pet" or "skin:harbor". */
15
+ name: string;
16
+ /** Installed version when known; omitted otherwise. */
17
+ version?: string;
18
+ /** Install channel when determinable (market = Workshop install). */
19
+ channel?: TelemetryChannel;
20
+ }
21
+ /**
22
+ * Fire the daily heartbeat for the given items at most once per UTC day per
23
+ * browser. Never throws and never blocks the caller. Items without an explicit
24
+ * version inherit the bundle's baked build version.
25
+ */
26
+ export declare function reportDailyHeartbeat(items: readonly TelemetryItem[]): void;
27
+ //# sourceMappingURL=telemetry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.d.ts","sourceRoot":"","sources":["../../../src/client/telemetry.ts"],"names":[],"mappings":"AACA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,KAAK,GAAG,SAAS,CAAA;AAE3D,MAAM,WAAW,aAAa;IAC5B,gFAAgF;IAChF,IAAI,EAAE,MAAM,CAAA;IACZ,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,qEAAqE;IACrE,OAAO,CAAC,EAAE,gBAAgB,CAAA;CAC3B;AA+CD;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,GAAG,IAAI,CA8B1E"}
@@ -0,0 +1,97 @@
1
+ // Generated by scripts/sync-shared.mjs from shared/client/telemetry.ts. Do not edit this copy; edit the shared source and run "node scripts/sync-shared.mjs".
2
+ /**
3
+ * Anonymous install telemetry shared by the DSH Web UI family plugins.
4
+ *
5
+ * Once per UTC day per browser, each wired plugin sends one heartbeat to
6
+ * the dsh-market edge API listing its package name (and version when known).
7
+ * The payload carries no conversation data and no identifiers beyond a random
8
+ * UUID generated in localStorage; the server hashes it with a deployment salt
9
+ * before storage and never persists IP addresses. Sends are fire-and-forget:
10
+ * failures stay silent and simply retry on a later mount or day. The system
11
+ * is documented in docs/telemetry.md.
12
+ */
13
+ const VISITOR_KEY = 'dsh-web-ui-telemetry-visitor';
14
+ const DAY_KEY_PREFIX = 'dsh-web-ui-telemetry-day:';
15
+ const ENDPOINT = 'https://dsh-market.com/api/telemetry/event';
16
+ /** The building package's version, when the bundle carries it. */
17
+ function bakedVersion() {
18
+ try {
19
+ return typeof __DSH_PKG_VERSION__ === 'string' && __DSH_PKG_VERSION__ !== ''
20
+ ? __DSH_PKG_VERSION__
21
+ : undefined;
22
+ }
23
+ catch {
24
+ return undefined;
25
+ }
26
+ }
27
+ /** Read or lazily create the anonymous visitor id; null when storage is unavailable. */
28
+ function visitorId() {
29
+ try {
30
+ const existing = localStorage.getItem(VISITOR_KEY);
31
+ if (existing && /^[A-Za-z0-9_-]{16,64}$/.test(existing))
32
+ return existing;
33
+ const fresh = crypto.randomUUID().replaceAll('-', '');
34
+ localStorage.setItem(VISITOR_KEY, fresh);
35
+ return fresh;
36
+ }
37
+ catch {
38
+ // Storage unavailable (privacy mode, sandboxed frame): report nothing.
39
+ return null;
40
+ }
41
+ }
42
+ /** Drop stale per-day dedup keys so localStorage does not grow forever. */
43
+ function pruneDayKeys(today) {
44
+ try {
45
+ for (let index = localStorage.length - 1; index >= 0; index -= 1) {
46
+ const key = localStorage.key(index);
47
+ if (key !== null && key.startsWith(DAY_KEY_PREFIX) && key !== DAY_KEY_PREFIX + today) {
48
+ localStorage.removeItem(key);
49
+ }
50
+ }
51
+ }
52
+ catch { /* best effort */ }
53
+ }
54
+ /**
55
+ * Fire the daily heartbeat for the given items at most once per UTC day per
56
+ * browser. Never throws and never blocks the caller. Items without an explicit
57
+ * version inherit the bundle's baked build version.
58
+ */
59
+ export function reportDailyHeartbeat(items) {
60
+ try {
61
+ if (items.length === 0)
62
+ return;
63
+ const today = new Date().toISOString().slice(0, 10);
64
+ // Automation contexts (CDP-driven QA browsers, fresh isolated profiles)
65
+ // mint a new visitor id per run and would inflate instance counts.
66
+ if (navigator.webdriver)
67
+ return;
68
+ if (localStorage.getItem(DAY_KEY_PREFIX + today) !== null)
69
+ return;
70
+ const visitor = visitorId();
71
+ if (visitor === null)
72
+ return;
73
+ pruneDayKeys(today);
74
+ const payloadItems = items.map((item) => {
75
+ const out = { name: item.name };
76
+ const version = item.version ?? bakedVersion();
77
+ if (version !== undefined)
78
+ out.version = version;
79
+ if (item.channel !== undefined)
80
+ out.channel = item.channel;
81
+ return out;
82
+ });
83
+ const body = JSON.stringify({ kind: 'heartbeat', visitor, items: payloadItems });
84
+ void fetch(ENDPOINT, {
85
+ method: 'POST',
86
+ headers: { 'content-type': 'application/json' },
87
+ body,
88
+ keepalive: true,
89
+ }).then((response) => {
90
+ // Mark the day only after an accepted send, so offline browsers retry
91
+ // on a later mount instead of going dark until tomorrow.
92
+ if (response.ok)
93
+ localStorage.setItem(DAY_KEY_PREFIX + today, '1');
94
+ }).catch(() => { });
95
+ }
96
+ catch { /* never break the host UI over telemetry */ }
97
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"dsh-home.d.ts","sourceRoot":"","sources":["../../src/dsh-home.ts"],"names":[],"mappings":"AACA;;;;GAIG;AAKH,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAkB,GAAG,MAAM,CAIzE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,EAAE,IAAI,GAAE,MAAkB,GAAG,MAAM,CAOrG;AAED,gEAAgE;AAChE,wBAAgB,OAAO,IAAI,MAAM,CAEhC"}
1
+ {"version":3,"file":"dsh-home.d.ts","sourceRoot":"","sources":["../../src/dsh-home.ts"],"names":[],"mappings":"AACA;;;;GAIG;AAMH,+DAA+D;AAC/D,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,MAAkB,GAAG,MAAM,CAMzE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,EAAE,IAAI,GAAE,MAAkB,GAAG,MAAM,CAUrG;AAED,gEAAgE;AAChE,wBAAgB,OAAO,IAAI,MAAM,CAEhC"}
@@ -6,12 +6,15 @@
6
6
  */
7
7
  import { homedir } from 'node:os';
8
8
  import { isAbsolute, join } from 'node:path';
9
+ import { isAbsolute as posixIsAbsolute, join as posixJoin } from 'node:path/posix';
9
10
  /** Expand a leading ~ (or ~user) in a path, platform-style. */
10
11
  export function expandHome(path, home = homedir()) {
12
+ const isPosix = home.startsWith('/');
13
+ const j = isPosix ? posixJoin : join;
11
14
  if (path === '~')
12
15
  return home;
13
16
  if (path.startsWith('~/') || path.startsWith('~\\'))
14
- return join(home, path.slice(2));
17
+ return j(home, path.slice(2));
15
18
  return path;
16
19
  }
17
20
  /**
@@ -21,12 +24,15 @@ export function expandHome(path, home = homedir()) {
21
24
  * @returns the absolute DSH home path.
22
25
  */
23
26
  export function resolveDshHome(env = process.env, home = homedir()) {
27
+ const isPosix = home.startsWith('/');
28
+ const j = isPosix ? posixJoin : join;
29
+ const isAbs = isPosix ? posixIsAbsolute : isAbsolute;
24
30
  const raw = env.DSH_HOME;
25
31
  if (raw !== undefined && raw.trim() !== '') {
26
32
  const expanded = expandHome(raw.trim(), home);
27
- return isAbsolute(expanded) ? expanded : join(process.cwd(), expanded);
33
+ return isAbs(expanded) ? expanded : j(process.cwd(), expanded);
28
34
  }
29
- return join(home, '.dsh');
35
+ return j(home, '.dsh');
30
36
  }
31
37
  /** Resolve the DSH home directory from the live environment. */
32
38
  export function dshHome() {
@@ -10,7 +10,7 @@
10
10
  * edit this shared source and re-run the sync instead of editing a copy.
11
11
  * Consumer code is migrated onto it in follow-up waves; no call site changes
12
12
  * belong in the same change as its introduction.
13
- * @module dsh-web-ui-shared/host/http
13
+ * @module dsh-web-shared/host/http
14
14
  */
15
15
  import type { IncomingMessage, OutgoingHttpHeaders, ServerResponse } from 'node:http';
16
16
  /**
package/lib/types/http.js CHANGED
@@ -11,7 +11,7 @@
11
11
  * edit this shared source and re-run the sync instead of editing a copy.
12
12
  * Consumer code is migrated onto it in follow-up waves; no call site changes
13
13
  * belong in the same change as its introduction.
14
- * @module dsh-web-ui-shared/host/http
14
+ * @module dsh-web-shared/host/http
15
15
  */
16
16
  /** Default body cap for readJsonBody: 64 KiB. */
17
17
  const DEFAULT_JSON_BODY_MAX_BYTES = 64 * 1024;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,CAAC,MAAM,aAAa,CAAA;AAE3B,OAAO,EAA0C,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AA0BzF,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,MAAM;QACd;;;;;WAKG;QACH,UAAU,CACR,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GACrC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;KAC9B;CACF;AAED,iCAAiC;AACjC,eAAO,MAAM,IAAI,kBAAkB,CAAA;AAEnC,+DAA+D;AAC/D,eAAO,MAAM,MAAM,UAA4B,CAAA;AAE/C;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,uDAAqC,CAAA;AAElF,qEAAqE;AACrE,MAAM,WAAW,MAAM;IACrB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,2EAA2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAY3B,CAAA;AAKF;;;;GAIG;AACH,KAAK,cAAc,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,GAAG,aAAa,CAAC,CAAC,GAAG;IAC9E,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,IAAI,CAC5C,cAAc,EACd,YAAY,GAAG,gBAAgB,GAAG,YAAY,GAAG,cAAc,GAAG,YAAY,GAAG,aAAa,CAC/F,GAAG,aAAa,CAShB;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,IAAI,GAAE,MAAkB,GAAG,MAAM,CAEnE;AAiBD;;;;GAIG;AACH,eAAO,MAAM,KAAK,kBAAuD,CAAA;AAEzE,iBAAS,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAuTtD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,CAAC,MAAM,aAAa,CAAA;AAI3B,OAAO,EAA0C,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AA6BzF,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,MAAM;QACd;;;;;WAKG;QACH,UAAU,CACR,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GACrC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;KAC9B;CACF;AAED,iCAAiC;AACjC,eAAO,MAAM,IAAI,kBAAkB,CAAA;AAEnC,+DAA+D;AAC/D,eAAO,MAAM,MAAM,UAAkD,CAAA;AAErE;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,uDAAqC,CAAA;AAElF,qEAAqE;AACrE,MAAM,WAAW,MAAM;IACrB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,2EAA2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAY3B,CAAA;AAKF;;;;GAIG;AACH,KAAK,cAAc,GAAG,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,eAAe,GAAG,aAAa,CAAC,CAAC,GAAG;IAC9E,aAAa,EAAE,MAAM,GAAG,SAAS,CAAA;IACjC,WAAW,EAAE,MAAM,CAAA;CACpB,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,IAAI,CAC5C,cAAc,EACd,YAAY,GAAG,gBAAgB,GAAG,YAAY,GAAG,cAAc,GAAG,YAAY,GAAG,aAAa,CAC/F,GAAG,aAAa,CAShB;AAED,4EAA4E;AAC5E,wBAAgB,kBAAkB,CAAC,IAAI,GAAE,MAAkB,GAAG,MAAM,CAEnE;AAiBD;;;;GAIG;AACH,eAAO,MAAM,KAAK,kBAAuD,CAAA;AAEzE,iBAAS,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAkVtD"}
@@ -25,7 +25,7 @@ import { makeRemoteApiRoutes, makeRemoteApiUpgradeRoutes } from "./remote-api.js
25
25
  import { claimPostureKey, postureTargets, probePosture, releasePostureKey } from "./posture.js";
26
26
  import { lanIPv4Addresses } from "./lan.js";
27
27
  import { TunnelManager } from "./tunnel.js";
28
- import { checkUpdates, fetchLatestVersion, resolveAnchorManifest, resolveUpdateTarget, runUpdateVerified, } from "./update.js";
28
+ import { checkUpdates, fetchGitHubReleaseNotes, fetchLatestVersion, RELEASE_NOTES_CACHE_TTL_MS, resolveAnchorManifest, resolveUpdateTarget, runUpdateVerified, } from "./update.js";
29
29
  import { makeUpdateRoutes } from "./update-routes.js";
30
30
  import { mountOnce } from "./mount-once.js";
31
31
  import { REMOTE_CHANNEL_BOOT_SCRIPT } from "./remote-channel-boot.js";
@@ -33,7 +33,7 @@ import { UUID_POLYFILL_SCRIPT } from "./uuid-polyfill.js";
33
33
  /** Stable cordis plugin name. */
34
34
  export const name = 'remote-web-ui';
35
35
  /** Services required before the pairing surfaces can mount. */
36
- export const inject = ['webServer', 'apiProxy'];
36
+ export const inject = ['webServer', 'apiProxy', 'commands', 'agents'];
37
37
  /**
38
38
  * Settings namespace of the remote-control capability — the section the web
39
39
  * settings surface edits. Spelled here rather than imported: the browser
@@ -186,7 +186,7 @@ function applyImpl(ctx, config) {
186
186
  console.warn('remote-web-ui: apiProxy service unavailable — the mobile data channel is disabled');
187
187
  }
188
188
  // ── remote update ────────────────────────────────────────────────────────
189
- // The dsh-web-ui self-update surface: probe the npm registry for family
189
+ // The dsh-web self-update surface: probe the npm registry for family
190
190
  // releases and run `pnpm update --latest` in the owning profile. Resolutions
191
191
  // anchor on the host process's own module graph, so the update always
192
192
  // targets the profile the running web GUI was booted from. The anchor path
@@ -202,6 +202,15 @@ function applyImpl(ctx, config) {
202
202
  return undefined;
203
203
  }
204
204
  });
205
+ const releaseNotesCache = new Map();
206
+ const fetchReleaseNotesCached = async (version) => {
207
+ const cached = releaseNotesCache.get(version);
208
+ if (cached !== undefined && Date.now() - cached.at < RELEASE_NOTES_CACHE_TTL_MS)
209
+ return cached.notes;
210
+ const notes = await fetchGitHubReleaseNotes(version, fetch);
211
+ releaseNotesCache.set(version, { at: Date.now(), notes });
212
+ return notes;
213
+ };
205
214
  const updateRoutes = makeUpdateRoutes({
206
215
  // Control endpoints are host-surface only: a LAN/phone origin must never
207
216
  // trigger a real install on this machine.
@@ -217,6 +226,7 @@ function applyImpl(ctx, config) {
217
226
  }
218
227
  },
219
228
  fetchLatest: name => fetchLatestVersion(name, fetch),
229
+ fetchReleaseNotes: fetchReleaseNotesCached,
220
230
  }),
221
231
  run: async () => {
222
232
  const target = resolveUpdateTarget({ anchorManifestPath: resolveAnchorPath() });
@@ -226,7 +236,7 @@ function applyImpl(ctx, config) {
226
236
  ok: false,
227
237
  exitCode: null,
228
238
  output: '',
229
- error: code === 'not-found' ? 'dsh-web-ui aggregate not installed' : 'local link install — update unavailable',
239
+ error: code === 'not-found' ? 'dsh-web aggregate not installed' : 'local link install — update unavailable',
230
240
  errorCode: code,
231
241
  };
232
242
  }
@@ -247,6 +257,7 @@ function applyImpl(ctx, config) {
247
257
  }
248
258
  },
249
259
  fetchLatest: name => fetchLatestVersion(name, fetch),
260
+ fetchReleaseNotes: fetchReleaseNotesCached,
250
261
  },
251
262
  });
252
263
  },
@@ -255,7 +266,25 @@ function applyImpl(ctx, config) {
255
266
  ...makeRoutes({ service, lanAddresses, requirePairingForLan: () => resolve().requirePairingForLan }),
256
267
  ...makeMobileRoutes(),
257
268
  ...(apiProxy !== undefined
258
- ? makeMobileApiRoutes({ service, apiProxy, pendingTracker: new PendingTracker(), mobileEnterToSend: () => resolve().mobileEnterToSend })
269
+ ? makeMobileApiRoutes({
270
+ service,
271
+ apiProxy,
272
+ pendingTracker: new PendingTracker(),
273
+ mobileEnterToSend: () => resolve().mobileEnterToSend,
274
+ commandDispatcher: ctx.commands !== undefined && ctx.agents !== undefined
275
+ ? {
276
+ async execute(sessionId, line, signal) {
277
+ const agent = ctx.agents.get(sessionId);
278
+ if (agent === undefined)
279
+ return { error: 'session-not-found' };
280
+ const execution = await ctx.commands.execute(agent, line, [], signal);
281
+ if (execution === undefined)
282
+ return { error: 'unknown-command' };
283
+ return { ok: true, result: execution.result };
284
+ },
285
+ }
286
+ : undefined,
287
+ })
259
288
  : []),
260
289
  ...(apiProxy !== undefined ? makePairedModelCatalogRoutes({ service, apiProxy, lanAddresses }) : []),
261
290
  // The remote desktop channel: policy-gated `/remote` prefix that
@@ -7,7 +7,7 @@
7
7
  * Semantics: RFC 5735 IPv4 127/8, ::1, IPv4-mapped ::ffff:127/8 (matching the
8
8
  * remote-web-ui gate), localhost hostnames, plus the browser same-origin
9
9
  * markers (sec-fetch-site and Origin) for the request-level fence.
10
- * @module dsh-web-ui-shared/host/loopback
10
+ * @module dsh-web-shared/host/loopback
11
11
  */
12
12
  import type { IncomingMessage } from 'node:http';
13
13
  /** IPv4 127/8 predicate (four decimal octets, first == 127). */
@@ -8,7 +8,7 @@
8
8
  * Semantics: RFC 5735 IPv4 127/8, ::1, IPv4-mapped ::ffff:127/8 (matching the
9
9
  * remote-web-ui gate), localhost hostnames, plus the browser same-origin
10
10
  * markers (sec-fetch-site and Origin) for the request-level fence.
11
- * @module dsh-web-ui-shared/host/loopback
11
+ * @module dsh-web-shared/host/loopback
12
12
  */
13
13
  /** IPv4 127/8 predicate (four decimal octets, first == 127). */
14
14
  export function isIPv4Loopback(v4) {
@@ -64,6 +64,13 @@ export declare function history(sessionId: string, beforeSeq?: number, maxMessag
64
64
  export declare function prompt(sessionId: string, text: string): Promise<void>;
65
65
  /** Send one slash command line (e.g. `/permission workspace-write`). */
66
66
  export declare function sendCommand(sessionId: string, line: string): Promise<unknown>;
67
+ /**
68
+ * Stop the session's active turn (the mobile stop button). Pending queued
69
+ * work is preserved and resumes in FIFO order once cancellation settles.
70
+ */
71
+ export declare function cancelSession(sessionId: string): Promise<{
72
+ accepted: true;
73
+ }>;
67
74
  /** Fresh advisory model directory for one session (current + groups + failures). */
68
75
  export declare function models(sessionId: string): Promise<SessionModels>;
69
76
  /** Select the complete model selection (provider/model/reasoning effort) for a session. */