@linxin666/dsh-remote-web-ui 0.2.9 → 0.3.1

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 (83) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +46 -8
  3. package/README.zh.md +7 -2
  4. package/lib/client.js +103 -58
  5. package/lib/client.js.map +1 -1
  6. package/lib/index.js +704 -61
  7. package/lib/mobile.js +874 -64
  8. package/lib/mobile.js.map +1 -1
  9. package/lib/types/client/index.d.ts.map +1 -1
  10. package/lib/types/client/index.js +71 -19
  11. package/lib/types/client/remote-channel.d.ts +4 -5
  12. package/lib/types/client/remote-channel.d.ts.map +1 -1
  13. package/lib/types/client/remote-channel.js +13 -28
  14. package/lib/types/http.d.ts +37 -8
  15. package/lib/types/http.d.ts.map +1 -1
  16. package/lib/types/http.js +76 -11
  17. package/lib/types/index.d.ts.map +1 -1
  18. package/lib/types/index.js +25 -1
  19. package/lib/types/mobile/api.d.ts +57 -0
  20. package/lib/types/mobile/api.d.ts.map +1 -1
  21. package/lib/types/mobile/api.js +29 -0
  22. package/lib/types/mobile/markdown.d.ts +7 -1
  23. package/lib/types/mobile/markdown.d.ts.map +1 -1
  24. package/lib/types/mobile/markdown.js +16 -7
  25. package/lib/types/mobile/markdown.test.js +8 -0
  26. package/lib/types/mobile/mobile-styles.d.ts +1 -1
  27. package/lib/types/mobile/mobile-styles.d.ts.map +1 -1
  28. package/lib/types/mobile/mobile-styles.js +325 -6
  29. package/lib/types/mobile/views/ChatView.d.ts +10 -0
  30. package/lib/types/mobile/views/ChatView.d.ts.map +1 -1
  31. package/lib/types/mobile/views/ChatView.js +205 -8
  32. package/lib/types/mobile/views/ChatView.stream.test.d.ts +2 -0
  33. package/lib/types/mobile/views/ChatView.stream.test.d.ts.map +1 -0
  34. package/lib/types/mobile/views/ChatView.stream.test.js +205 -0
  35. package/lib/types/mobile/views/WorkspaceView.d.ts.map +1 -1
  36. package/lib/types/mobile/views/WorkspaceView.js +46 -5
  37. package/lib/types/mobile/views/WorkspaceView.test.js +129 -2
  38. package/lib/types/mobile-api.d.ts +9 -3
  39. package/lib/types/mobile-api.d.ts.map +1 -1
  40. package/lib/types/mobile-api.js +61 -16
  41. package/lib/types/mobile-pending.d.ts +38 -0
  42. package/lib/types/mobile-pending.d.ts.map +1 -0
  43. package/lib/types/mobile-pending.js +59 -0
  44. package/lib/types/paired-model-catalog.d.ts +17 -0
  45. package/lib/types/paired-model-catalog.d.ts.map +1 -0
  46. package/lib/types/paired-model-catalog.js +311 -0
  47. package/lib/types/pairing.d.ts +13 -4
  48. package/lib/types/pairing.d.ts.map +1 -1
  49. package/lib/types/pairing.js +13 -5
  50. package/lib/types/remote-api.d.ts.map +1 -1
  51. package/lib/types/remote-channel-boot.d.ts +26 -0
  52. package/lib/types/remote-channel-boot.d.ts.map +1 -0
  53. package/lib/types/remote-channel-boot.js +128 -0
  54. package/lib/types/remote-channel-rules.d.ts +40 -0
  55. package/lib/types/remote-channel-rules.d.ts.map +1 -0
  56. package/lib/types/remote-channel-rules.js +32 -0
  57. package/lib/types/routes.d.ts.map +1 -1
  58. package/lib/types/routes.js +12 -23
  59. package/lib/types/uuid-polyfill.d.ts +10 -0
  60. package/lib/types/uuid-polyfill.d.ts.map +1 -0
  61. package/lib/types/uuid-polyfill.js +27 -0
  62. package/package.json +1 -1
  63. package/src/client/index.ts +72 -18
  64. package/src/client/remote-channel.ts +18 -28
  65. package/src/http.ts +87 -12
  66. package/src/index.ts +27 -2
  67. package/src/mobile/api.ts +91 -0
  68. package/src/mobile/markdown.test.ts +9 -0
  69. package/src/mobile/markdown.ts +17 -7
  70. package/src/mobile/mobile-styles.ts +326 -6
  71. package/src/mobile/views/ChatView.stream.test.tsx +231 -0
  72. package/src/mobile/views/ChatView.tsx +328 -10
  73. package/src/mobile/views/WorkspaceView.test.tsx +163 -2
  74. package/src/mobile/views/WorkspaceView.tsx +152 -26
  75. package/src/mobile-api.ts +59 -17
  76. package/src/mobile-pending.ts +88 -0
  77. package/src/paired-model-catalog.ts +272 -0
  78. package/src/pairing.ts +13 -5
  79. package/src/remote-api.ts +0 -1
  80. package/src/remote-channel-boot.ts +131 -0
  81. package/src/remote-channel-rules.ts +62 -0
  82. package/src/routes.ts +13 -24
  83. package/src/uuid-polyfill.ts +28 -0
@@ -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;AAMrD,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,CAqL9C"}
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"}
@@ -15,7 +15,7 @@ import { RemoteSettingsCard, RemoteSettingsCardController } from "./RemoteSettin
15
15
  import { en, zh } from "./locales.js";
16
16
  import { PAIR_FAILED_MARKER, runPairBootFlow } from "./deep-link.js";
17
17
  import { readPairGatePolicy, sendHeartbeat } from "./pair-api.js";
18
- import { channelTransition, installRemoteChannel, isLoopbackHostname, remoteChannelRequired } from "./remote-channel.js";
18
+ import { channelTransition, installRemoteChannel, isLoopbackHostname, remoteChannelRequired, REMOTE_CHANNEL_BOOT_GLOBAL, } from "./remote-channel.js";
19
19
  import { FenceNotice } from "./FenceNotice.js";
20
20
  /** Dictionary namespace owned by this plugin. */
21
21
  const NS = 'remote';
@@ -30,7 +30,14 @@ export const inject = ['slots', 'locale', 'connection', 'settingsScope', 'remote
30
30
  * @param ctx - client root context.
31
31
  */
32
32
  export function apply(ctx) {
33
- ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'remote-web-ui: dictionaries');
33
+ ctx.effect(() => {
34
+ try {
35
+ return ctx.locale.register(NS, { zh, en });
36
+ }
37
+ catch {
38
+ return () => { };
39
+ }
40
+ }, 'remote-web-ui: dictionaries');
34
41
  const t = ctx.locale.bind(NS);
35
42
  const binder = ctx.get('webUiSettings') ?? ctx.settingsScope;
36
43
  const settingsScope = binder.bind({ namespace: REMOTE_WEB_UI_NS });
@@ -49,7 +56,12 @@ export function apply(ctx) {
49
56
  let disposeEntry;
50
57
  const syncEntry = () => {
51
58
  if (enabled() && disposeEntry === undefined) {
52
- disposeEntry = ctx.slots.register({ name: 'sidebar.remote', locale: NS }, RemoteEntry);
59
+ try {
60
+ disposeEntry = ctx.slots.register({ name: 'sidebar.remote', locale: NS }, RemoteEntry);
61
+ }
62
+ catch {
63
+ // ignore registration collision
64
+ }
53
65
  }
54
66
  else if (!enabled() && disposeEntry !== undefined) {
55
67
  disposeEntry();
@@ -71,7 +83,12 @@ export function apply(ctx) {
71
83
  let disposeEntry;
72
84
  const syncEntry = () => {
73
85
  if (enabled() && disposeEntry === undefined) {
74
- disposeEntry = ctx.slots.register({ name: 'sidebar.footer.action', id: 'remote-web-ui', locale: NS }, FooterRemoteEntry);
86
+ try {
87
+ disposeEntry = ctx.slots.register({ name: 'sidebar.footer.action', id: 'remote-web-ui', locale: NS }, FooterRemoteEntry);
88
+ }
89
+ catch {
90
+ // ignore registration collision
91
+ }
75
92
  }
76
93
  else if (!enabled() && disposeEntry !== undefined) {
77
94
  disposeEntry();
@@ -89,17 +106,22 @@ export function apply(ctx) {
89
106
  // settings namespace, contributed to the Web UI plugin group.
90
107
  const remoteSettings = new RemoteSettingsCardController(settingsScope);
91
108
  ctx.slots.inject('web-ui.plugin.item', () => {
92
- const unregister = ctx.slots.register({
93
- name: 'web-ui.plugin.item',
94
- id: 'remote-web-ui',
95
- order: 90,
96
- locale: NS,
97
- inject: () => remoteSettings.inject(),
98
- }, RemoteSettingsCard);
99
- return () => {
100
- remoteSettings.dispose();
101
- unregister();
102
- };
109
+ try {
110
+ const unregister = ctx.slots.register({
111
+ name: 'web-ui.plugin.item',
112
+ id: 'remote-web-ui',
113
+ order: 90,
114
+ locale: NS,
115
+ inject: () => remoteSettings.inject(),
116
+ }, RemoteSettingsCard);
117
+ return () => {
118
+ remoteSettings.dispose();
119
+ unregister();
120
+ };
121
+ }
122
+ catch {
123
+ return () => { };
124
+ }
103
125
  });
104
126
  // Phone-side boot flow + heartbeats. Loopback pages (the desktop) never
105
127
  // heartbeat; the server ignores unpaired heartbeats anyway. Both run only
@@ -154,17 +176,42 @@ export function apply(ctx) {
154
176
  showFenceNotice();
155
177
  };
156
178
  const channelActive = () => remoteChannelRequired(window.location.hostname, settingsScope.getSnapshot(), hostPairingPolicy);
179
+ // The parse-time boot patch (issue #987), when the served index carried
180
+ // it: already installed before any boot entry ran, so adopting its seat
181
+ // beats patching a second time (which would double-rewrite onto
182
+ // /remote/remote/...).
183
+ const bootSeat = () => window[REMOTE_CHANNEL_BOOT_GLOBAL];
157
184
  const syncChannel = () => {
158
185
  const transition = channelTransition(channelActive(), disposeChannel !== undefined);
159
186
  if (transition === 'install') {
160
- disposeChannel = ctx.effect(() => {
161
- const restore = installRemoteChannel(window, { onUnpaired: handleUnpaired, onPaired: hideFenceNotice });
162
- return restore;
163
- }, 'remote-web-ui: remote desktop channel');
187
+ const seat = bootSeat();
188
+ if (seat !== undefined) {
189
+ seat.onUnpaired = handleUnpaired;
190
+ seat.onPaired = hideFenceNotice;
191
+ // Replay a signal raised before adoption (early unpaired responses).
192
+ if (seat.pendingUnpaired) {
193
+ seat.pendingUnpaired = false;
194
+ handleUnpaired();
195
+ }
196
+ disposeChannel = ctx.effect(() => () => {
197
+ seat.onUnpaired = null;
198
+ seat.onPaired = null;
199
+ }, 'remote-web-ui: remote desktop channel (boot patch)');
200
+ }
201
+ else {
202
+ disposeChannel = ctx.effect(() => {
203
+ const restore = installRemoteChannel(window, { onUnpaired: handleUnpaired, onPaired: hideFenceNotice });
204
+ return restore;
205
+ }, 'remote-web-ui: remote desktop channel');
206
+ }
164
207
  }
165
208
  else if (transition === 'retire' && disposeChannel !== undefined) {
166
209
  disposeChannel();
167
210
  disposeChannel = undefined;
211
+ // Retire the provisional parse-time install with the channel: the
212
+ // desktop now rides plain /api, so the rewrite must go (its seat
213
+ // removes the global; a later re-activation patches afresh).
214
+ bootSeat()?.restore();
168
215
  // Retire the notice with the channel: once requirePairingForLan turns
169
216
  // off (or the plugin is disabled) the desktop rides plain /api again,
170
217
  // so an unpaired notice raised while the channel was briefly active
@@ -173,6 +220,11 @@ export function apply(ctx) {
173
220
  // the channel gone nothing can re-raise it (issue #808).
174
221
  hideFenceNotice();
175
222
  }
223
+ else if (transition === 'none' && !channelActive()) {
224
+ // The channel was never adopted (policy settled to off before apply
225
+ // ran): the provisional boot patch still retires.
226
+ bootSeat()?.restore();
227
+ }
176
228
  };
177
229
  settingsScope.subscribe(syncChannel);
178
230
  syncChannel();
@@ -23,10 +23,10 @@
23
23
  * Pure helpers are exported for unit tests; `installRemoteChannel` patches
24
24
  * the given window and returns their restore.
25
25
  */
26
- /** The gated mirror prefix (must match src/remote-methods.ts). */
27
- export declare const REMOTE_PREFIX = "/remote";
28
- /** Connection-plugin method prefix under the gated channel. */
29
- export declare const REMOTE_API_PREFIX = "/remote/api";
26
+ import { REMOTE_API_PREFIX, REMOTE_PREFIX } from '../remote-channel-rules.ts';
27
+ export { REMOTE_API_PREFIX, REMOTE_PREFIX };
28
+ export type { RemoteChannelBootSeat } from '../remote-channel-rules.ts';
29
+ export { REMOTE_CHANNEL_BOOT_GLOBAL } from '../remote-channel-rules.ts';
30
30
  /** Minimal settings snapshot used by the remote channel decision. */
31
31
  export interface RemoteChannelSettingsSnapshot {
32
32
  status: 'ready' | 'loading' | 'unavailable' | string;
@@ -114,5 +114,4 @@ export type ChannelTransition = 'install' | 'retire' | 'none';
114
114
  * @returns the transition to apply.
115
115
  */
116
116
  export declare function channelTransition(active: boolean, installed: boolean): ChannelTransition;
117
- export {};
118
117
  //# sourceMappingURL=remote-channel.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"remote-channel.d.ts","sourceRoot":"","sources":["../../../src/client/remote-channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,kEAAkE;AAClE,eAAO,MAAM,aAAa,YAAY,CAAA;AAEtC,+DAA+D;AAC/D,eAAO,MAAM,iBAAiB,gBAAyB,CAAA;AAkBvD,qEAAqE;AACrE,MAAM,WAAW,6BAA6B;IAC5C,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,aAAa,GAAG,MAAM,CAAA;IACpD,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,oBAAoB,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAC9D;AAED,qFAAqF;AACrF,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,6BAA6B,EACvC,iBAAiB,EAAE,OAAO,GAAG,SAAS,GACrC,OAAO,CAQT;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAI5D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAUhE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAcnF;AAED,wEAAwE;AACxE,UAAU,cAAc;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IAC9B,SAAS,EAAE,OAAO,SAAS,CAAA;IAC3B,WAAW,CAAC,EAAE,OAAO,WAAW,CAAA;IAChC,gBAAgB,CAAC,EAAE,cAAc,CAAA;IACjC,iBAAiB,CAAC,EAAE,cAAc,CAAA;IAClC,iBAAiB,CAAC,EAAE,cAAc,CAAA;IAClC,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAC3C;AAED,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACnC,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB;AAoBD;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAO3E;AA0BD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,IAAI,CAwE1G;AAED;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAA;AAE7D;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,GAAG,iBAAiB,CAIxF"}
1
+ {"version":3,"file":"remote-channel.d.ts","sourceRoot":"","sources":["../../../src/client/remote-channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EACL,iBAAiB,EAEjB,aAAa,EACd,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,CAAA;AAC3C,YAAY,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AACvE,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAA;AAIvE,qEAAqE;AACrE,MAAM,WAAW,6BAA6B;IAC5C,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,aAAa,GAAG,MAAM,CAAA;IACpD,KAAK,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAC;QAAC,oBAAoB,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAC9D;AAED,qFAAqF;AACrF,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,6BAA6B,EACvC,iBAAiB,EAAE,OAAO,GAAG,SAAS,GACrC,OAAO,CAQT;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAI5D;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAUhE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAcnF;AAED,wEAAwE;AACxE,UAAU,cAAc;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IAC9B,SAAS,EAAE,OAAO,SAAS,CAAA;IAC3B,WAAW,CAAC,EAAE,OAAO,WAAW,CAAA;IAChC,gBAAgB,CAAC,EAAE,cAAc,CAAA;IACjC,iBAAiB,CAAC,EAAE,cAAc,CAAA;IAClC,iBAAiB,CAAC,EAAE,cAAc,CAAA;IAClC,QAAQ,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAC3C;AAED,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACnC,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB;AAoBD;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAO3E;AA0BD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,IAAI,CAwE1G;AAED;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAA;AAE7D;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,GAAG,iBAAiB,CAIxF"}
@@ -23,25 +23,10 @@
23
23
  * Pure helpers are exported for unit tests; `installRemoteChannel` patches
24
24
  * the given window and returns their restore.
25
25
  */
26
- /** The gated mirror prefix (must match src/remote-methods.ts). */
27
- export const REMOTE_PREFIX = '/remote';
28
- /** Connection-plugin method prefix under the gated channel. */
29
- export const REMOTE_API_PREFIX = `${REMOTE_PREFIX}/api`;
30
- const API_PREFIX = '/api/';
31
- const PAIR_PREFIX = '/api/pair/';
32
- const UPDATE_PREFIX = '/api/update/';
33
- const DESKTOP_LAUNCHER_PREFIX = '/api/dsh-desktop-launcher';
34
- const SETTINGS_BRIDGE_PREFIX = '/api/dsh-web-ui-settings';
35
- const SIDEBAR_PREFIX = '/sidebar/';
36
- const GIT_PREFIX = '/git/';
37
- const PET_PREFIX = '/pet/';
38
- const WS_PATHS = new Set([
39
- '/api/events.mux',
40
- '/api/events.host',
41
- '/sidebar/ws/terminal',
42
- '/sidebar/ws/agent-terminals',
43
- '/api/dsh-ssh/terminal',
44
- ]);
26
+ import { REMOTE_API_PREFIX, REMOTE_CHANNEL_RULES, REMOTE_PREFIX, } from "../remote-channel-rules.js";
27
+ export { REMOTE_API_PREFIX, REMOTE_PREFIX };
28
+ export { REMOTE_CHANNEL_BOOT_GLOBAL } from "../remote-channel-rules.js";
29
+ const RULES = REMOTE_CHANNEL_RULES;
45
30
  /** Decide whether a remote desktop channel is required from local or host policy. */
46
31
  export function remoteChannelRequired(hostname, snapshot, hostPairingPolicy) {
47
32
  if (isLoopbackHostname(hostname))
@@ -71,21 +56,21 @@ export function isLoopbackHostname(hostname) {
71
56
  * @param pathname - the request URL pathname.
72
57
  */
73
58
  export function shouldRewriteFetchPath(pathname) {
74
- if (pathname.startsWith(PAIR_PREFIX))
59
+ if (pathname.startsWith(RULES.pairPrefix))
75
60
  return false;
76
- if (pathname.startsWith(UPDATE_PREFIX))
61
+ if (pathname.startsWith(RULES.updatePrefix))
77
62
  return false;
78
- if (pathname === DESKTOP_LAUNCHER_PREFIX || pathname.startsWith(`${DESKTOP_LAUNCHER_PREFIX}/`))
63
+ if (pathname === RULES.desktopLauncherPrefix || pathname.startsWith(`${RULES.desktopLauncherPrefix}/`))
79
64
  return false;
80
- if (pathname === SETTINGS_BRIDGE_PREFIX || pathname.startsWith(`${SETTINGS_BRIDGE_PREFIX}/`))
65
+ if (pathname === RULES.settingsBridgePrefix || pathname.startsWith(`${RULES.settingsBridgePrefix}/`))
81
66
  return false;
82
- if (pathname.startsWith(API_PREFIX))
67
+ if (pathname.startsWith(RULES.apiPrefix))
83
68
  return true;
84
- if (pathname.startsWith(SIDEBAR_PREFIX) || pathname === '/sidebar')
69
+ if (pathname.startsWith(RULES.sidebarPrefix) || pathname === '/sidebar')
85
70
  return true;
86
- if (pathname.startsWith(GIT_PREFIX) || pathname === '/git')
71
+ if (pathname.startsWith(RULES.gitPrefix) || pathname === '/git')
87
72
  return true;
88
- if (pathname.startsWith(PET_PREFIX) || pathname === '/pet')
73
+ if (pathname.startsWith(RULES.petPrefix) || pathname === '/pet')
89
74
  return true;
90
75
  return false;
91
76
  }
@@ -94,7 +79,7 @@ export function shouldRewriteFetchPath(pathname) {
94
79
  * @param pathname - the WebSocket URL pathname.
95
80
  */
96
81
  export function shouldRewriteWsPath(pathname) {
97
- return WS_PATHS.has(pathname);
82
+ return RULES.wsPaths.includes(pathname);
98
83
  }
99
84
  /** The gated twin of one fenced path (`/remote` + original pathname). */
100
85
  export function rewritePath(pathname) {
@@ -1,14 +1,43 @@
1
1
  /**
2
- * Shared HTTP helpers for the route families: one JSON writer and one
3
- * bounded JSON body reader. Previously copy-pasted across routes.ts,
4
- * update-routes.ts, and mobile-api.ts with drifting failure contracts.
2
+ * Shared JSON body/response helpers for the host route families: one strict
3
+ * bounded body reader, one lenient bounded body reader, one JSON object
4
+ * narrow, and one JSON writer. Previously these were copy-pasted across the
5
+ * package route files (routes.ts, update-routes.ts, mobile-api.ts, and each
6
+ * family's route module) with drifting contracts: body caps ranging 4 KiB to
7
+ * 1 MiB and four distinct overflow behaviors (reject, undefined, null, throw).
8
+ *
9
+ * Packages receive this file as a generated copy via scripts/sync-shared.mjs;
10
+ * edit this shared source and re-run the sync instead of editing a copy.
11
+ * Consumer code is migrated onto it in follow-up waves; no call site changes
12
+ * belong in the same change as its introduction.
13
+ * @module dsh-web-ui-shared/host/http
5
14
  */
6
- import type { IncomingMessage, ServerResponse } from 'node:http';
7
- /** One JSON response. */
8
- export declare function writeJson(res: ServerResponse, status: number, body: unknown): void;
15
+ import type { IncomingMessage, OutgoingHttpHeaders, ServerResponse } from 'node:http';
9
16
  /**
10
- * Read a request body up to maxBytes and parse it as JSON.
11
- * @throws 'body too large' beyond the cap, or the JSON.parse error.
17
+ * Strict bounded body reader: parse a request body of at most maxBytes as
18
+ * JSON.
19
+ * @throws 'body too large' past the cap, or the JSON.parse error for an
20
+ * invalid or empty payload.
12
21
  */
13
22
  export declare function readBoundedJson(req: IncomingMessage, maxBytes: number): Promise<unknown>;
23
+ /**
24
+ * Lenient bounded body reader: parse a request body as JSON, or null on an
25
+ * empty body, invalid JSON, or a body past maxBytes (default 64 KiB).
26
+ * Overflow destroys the request instead of draining the remainder (no drain
27
+ * call, matching the current repo-wide behavior); callers must not keep
28
+ * reading the request afterwards. With objectOnly, non-JSON-object payloads
29
+ * also yield null.
30
+ */
31
+ export declare function readJsonBody(req: IncomingMessage, opts?: {
32
+ maxBytes?: number;
33
+ objectOnly?: boolean;
34
+ }): Promise<unknown | null>;
35
+ /** Narrow a value to a JSON object, or undefined when it is not one. */
36
+ export declare function asJsonObject(value: unknown): Record<string, unknown> | undefined;
37
+ /**
38
+ * Write one JSON response. Default headers are the family defaults
39
+ * (content-type and referrer-policy); caller headers are appended or
40
+ * override them.
41
+ */
42
+ export declare function writeJson(res: ServerResponse, status: number, body: unknown, headers?: OutgoingHttpHeaders): void;
14
43
  //# sourceMappingURL=http.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/http.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAEhE,yBAAyB;AACzB,wBAAgB,SAAS,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAIlF;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAU9F"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/http.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAWrF;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAU9F;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,eAAe,EACpB,IAAI,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAO,GACrD,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAsBzB;AAOD,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAEhF;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,OAAO,EACb,OAAO,GAAE,mBAAwB,GAChC,IAAI,CAIN"}
package/lib/types/http.js CHANGED
@@ -1,17 +1,30 @@
1
+ // Generated by scripts/sync-shared.mjs from shared/host/http.ts. Do not edit this copy; edit the shared source and run "node scripts/sync-shared.mjs".
1
2
  /**
2
- * Shared HTTP helpers for the route families: one JSON writer and one
3
- * bounded JSON body reader. Previously copy-pasted across routes.ts,
4
- * update-routes.ts, and mobile-api.ts with drifting failure contracts.
3
+ * Shared JSON body/response helpers for the host route families: one strict
4
+ * bounded body reader, one lenient bounded body reader, one JSON object
5
+ * narrow, and one JSON writer. Previously these were copy-pasted across the
6
+ * package route files (routes.ts, update-routes.ts, mobile-api.ts, and each
7
+ * family's route module) with drifting contracts: body caps ranging 4 KiB to
8
+ * 1 MiB and four distinct overflow behaviors (reject, undefined, null, throw).
9
+ *
10
+ * Packages receive this file as a generated copy via scripts/sync-shared.mjs;
11
+ * edit this shared source and re-run the sync instead of editing a copy.
12
+ * Consumer code is migrated onto it in follow-up waves; no call site changes
13
+ * belong in the same change as its introduction.
14
+ * @module dsh-web-ui-shared/host/http
5
15
  */
6
- /** One JSON response. */
7
- export function writeJson(res, status, body) {
8
- const payload = JSON.stringify(body);
9
- res.writeHead(status, { 'content-type': 'application/json; charset=utf-8', 'referrer-policy': 'no-referrer' });
10
- res.end(payload);
11
- }
16
+ /** Default body cap for readJsonBody: 64 KiB. */
17
+ const DEFAULT_JSON_BODY_MAX_BYTES = 64 * 1024;
18
+ /** Family-default JSON response headers; callers may append or override. */
19
+ const JSON_HEADERS = {
20
+ 'content-type': 'application/json; charset=utf-8',
21
+ 'referrer-policy': 'no-referrer',
22
+ };
12
23
  /**
13
- * Read a request body up to maxBytes and parse it as JSON.
14
- * @throws 'body too large' beyond the cap, or the JSON.parse error.
24
+ * Strict bounded body reader: parse a request body of at most maxBytes as
25
+ * JSON.
26
+ * @throws 'body too large' past the cap, or the JSON.parse error for an
27
+ * invalid or empty payload.
15
28
  */
16
29
  export async function readBoundedJson(req, maxBytes) {
17
30
  const chunks = [];
@@ -25,3 +38,55 @@ export async function readBoundedJson(req, maxBytes) {
25
38
  }
26
39
  return JSON.parse(Buffer.concat(chunks).toString('utf8'));
27
40
  }
41
+ /**
42
+ * Lenient bounded body reader: parse a request body as JSON, or null on an
43
+ * empty body, invalid JSON, or a body past maxBytes (default 64 KiB).
44
+ * Overflow destroys the request instead of draining the remainder (no drain
45
+ * call, matching the current repo-wide behavior); callers must not keep
46
+ * reading the request afterwards. With objectOnly, non-JSON-object payloads
47
+ * also yield null.
48
+ */
49
+ export async function readJsonBody(req, opts = {}) {
50
+ const maxBytes = opts.maxBytes ?? DEFAULT_JSON_BODY_MAX_BYTES;
51
+ const chunks = [];
52
+ let size = 0;
53
+ for await (const chunk of req) {
54
+ const buffer = chunk;
55
+ size += buffer.length;
56
+ if (size > maxBytes) {
57
+ req.destroy();
58
+ return null;
59
+ }
60
+ chunks.push(buffer);
61
+ }
62
+ const text = Buffer.concat(chunks).toString('utf8');
63
+ if (text === '')
64
+ return null;
65
+ try {
66
+ const parsed = JSON.parse(text);
67
+ if (opts.objectOnly && !isJsonObject(parsed))
68
+ return null;
69
+ return parsed;
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ }
75
+ /** Whether a value is a JSON object: typeof object, not null, not an array. */
76
+ function isJsonObject(value) {
77
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
78
+ }
79
+ /** Narrow a value to a JSON object, or undefined when it is not one. */
80
+ export function asJsonObject(value) {
81
+ return isJsonObject(value) ? value : undefined;
82
+ }
83
+ /**
84
+ * Write one JSON response. Default headers are the family defaults
85
+ * (content-type and referrer-policy); caller headers are appended or
86
+ * override them.
87
+ */
88
+ export function writeJson(res, status, body, headers = {}) {
89
+ const payload = JSON.stringify(body);
90
+ res.writeHead(status, { ...JSON_HEADERS, ...headers });
91
+ res.end(payload);
92
+ }
@@ -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;AAsBzF,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,CAkStD"}
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"}
@@ -19,6 +19,8 @@ import { RemoteWebUiPairing } from "./pairing-access.js";
19
19
  import { isTrustedApiRequest, makeRoutes } from "./routes.js";
20
20
  import { makeMobileRoutes } from "./mobile-routes.js";
21
21
  import { makeMobileApiRoutes } from "./mobile-api.js";
22
+ import { PendingTracker } from "./mobile-pending.js";
23
+ import { makePairedModelCatalogRoutes } from "./paired-model-catalog.js";
22
24
  import { makeRemoteApiRoutes, makeRemoteApiUpgradeRoutes } from "./remote-api.js";
23
25
  import { claimPostureKey, postureTargets, probePosture, releasePostureKey } from "./posture.js";
24
26
  import { lanIPv4Addresses } from "./lan.js";
@@ -26,6 +28,8 @@ import { TunnelManager } from "./tunnel.js";
26
28
  import { checkUpdates, fetchLatestVersion, resolveAnchorManifest, resolveUpdateTarget, runUpdateVerified, } from "./update.js";
27
29
  import { makeUpdateRoutes } from "./update-routes.js";
28
30
  import { mountOnce } from "./mount-once.js";
31
+ import { REMOTE_CHANNEL_BOOT_SCRIPT } from "./remote-channel-boot.js";
32
+ import { UUID_POLYFILL_SCRIPT } from "./uuid-polyfill.js";
29
33
  /** Stable cordis plugin name. */
30
34
  export const name = 'remote-web-ui';
31
35
  /** Services required before the pairing surfaces can mount. */
@@ -251,8 +255,9 @@ function applyImpl(ctx, config) {
251
255
  ...makeRoutes({ service, lanAddresses, requirePairingForLan: () => resolve().requirePairingForLan }),
252
256
  ...makeMobileRoutes(),
253
257
  ...(apiProxy !== undefined
254
- ? makeMobileApiRoutes({ service, apiProxy, mobileEnterToSend: () => resolve().mobileEnterToSend })
258
+ ? makeMobileApiRoutes({ service, apiProxy, pendingTracker: new PendingTracker(), mobileEnterToSend: () => resolve().mobileEnterToSend })
255
259
  : []),
260
+ ...(apiProxy !== undefined ? makePairedModelCatalogRoutes({ service, apiProxy, lanAddresses }) : []),
256
261
  // The remote desktop channel: policy-gated `/remote` prefix that
257
262
  // re-issues fenced paths to loopback (see remote-api.ts). The live
258
263
  // requirePairingForLan is re-read per request, same as the gate listener
@@ -380,6 +385,25 @@ function applyImpl(ctx, config) {
380
385
  // re-probe unless the target set is unchanged.
381
386
  runPostureProbe();
382
387
  };
388
+ // Inject the crypto.randomUUID polyfill before any other script runs, so that
389
+ // the main bundle doesn't crash on non-secure contexts (LAN HTTP)
390
+ ctx.effect(() => ctx.on('webserver/index-inject', (table) => {
391
+ table.push({ kind: 'script', placement: 'head', text: UUID_POLYFILL_SCRIPT });
392
+ }), 'remote-web-ui: uuid polyfill');
393
+ // Issue #987: the browser-half channel patch installs at this plugin's
394
+ // boot entry, but dsh-client-connection boots earlier and opens its event
395
+ // streams unrewritten — on a non-loopback origin the SDK fence rejects
396
+ // them and the workspace list never loads. Contribute the rewrite as a
397
+ // parse-time head script so it is active before ANY boot entry runs; the
398
+ // client apply adopts the installed seat instead of patching twice. The
399
+ // row follows the live steady-state decision (enabled + pairing gate); the
400
+ // script itself skips loopback origins.
401
+ ctx.effect(() => ctx.on('webserver/index-inject', (table) => {
402
+ const value = resolve();
403
+ if (!value.enabled || !value.requirePairingForLan)
404
+ return;
405
+ table.push({ kind: 'script', placement: 'head', text: REMOTE_CHANNEL_BOOT_SCRIPT });
406
+ }), 'remote-web-ui: remote channel boot patch');
383
407
  installSettingsSection(ctx, REMOTE_WEB_UI_SETTINGS_NAMESPACE, Config, config ?? {}, {
384
408
  setSource: (source) => {
385
409
  current = source;
@@ -78,4 +78,61 @@ export declare function selectModel(sessionId: string, selection: {
78
78
  reasoningEffort?: string;
79
79
  };
80
80
  }>;
81
+ /** One pending tool approval awaiting the user's decision. */
82
+ export interface PendingApproval {
83
+ approvalId: string;
84
+ toolName: string;
85
+ callId?: string;
86
+ reason?: string;
87
+ }
88
+ /** One pending question group awaiting the user's answer. */
89
+ export interface PendingQuestionItem {
90
+ id: string;
91
+ question: string;
92
+ detail?: string;
93
+ header?: string;
94
+ options?: Array<{
95
+ label: string;
96
+ description?: string;
97
+ }>;
98
+ multiSelect?: boolean;
99
+ }
100
+ /** The pending state for one session. */
101
+ export interface PendingState {
102
+ approvals: PendingApproval[];
103
+ questions: PendingQuestionItem[];
104
+ }
105
+ /** Fetch pending approvals and questions for one session (polling fallback data source). */
106
+ export declare function fetchPending(sessionId: string): Promise<PendingState>;
107
+ /** Submit an approval decision (allowed-once or rejected). */
108
+ export declare function respondApproval(sessionId: string, approvalId: string, outcome: 'allowed-once' | 'rejected'): Promise<void>;
109
+ /** Submit answers to a question group. */
110
+ export declare function respondQuestion(sessionId: string, answers: Array<{
111
+ id: string;
112
+ selected: string[];
113
+ custom?: string;
114
+ }>): Promise<void>;
115
+ /** One entry in a directory listing. */
116
+ export interface DirectoryEntry {
117
+ name: string;
118
+ path: string;
119
+ hidden: boolean;
120
+ }
121
+ /** The host directory listing result (one level, with ancestor breadcrumbs). */
122
+ export interface DirectoryListing {
123
+ path: string;
124
+ home: string;
125
+ crumbs: DirectoryEntry[];
126
+ entries: DirectoryEntry[];
127
+ truncated: boolean;
128
+ }
129
+ /** The result of creating a workspace from an existing directory. */
130
+ export interface WorkspaceCreateResult {
131
+ workspace: WorkspaceView;
132
+ created: boolean;
133
+ }
134
+ /** Browse one directory level on the host (defaults to home directory). */
135
+ export declare function listDirectory(path?: string): Promise<DirectoryListing>;
136
+ /** Create a workspace from an existing host directory (does not mkdir). */
137
+ export declare function createWorkspace(path: string): Promise<WorkspaceCreateResult>;
81
138
  //# sourceMappingURL=api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/mobile/api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAA;AACjF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kDAAkD,CAAA;AACxF,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,6CAA6C,CAAA;AAGzH,6BAA6B;AAC7B,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,cAAc,EAAE,CAAA;IACvB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,gFAAgF;AAChF,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,4DAA4D;AAC5D,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAA;IACpC,UAAU,EAAE,OAAO,CAAA;IACnB,WAAW,EAAE,OAAO,CAAA;CACrB;AAED,wEAAwE;AACxE,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,6CAA6C,EAAE,YAAY,EAAE,CAAA;IAC5E,OAAO,EAAE,OAAO,CAAA;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,uBAAuB,CAAA;CACtC;AAED,4EAA4E;AAC5E,MAAM,WAAW,iBAAiB;IAChC,6EAA6E;IAC7E,iBAAiB,EAAE,OAAO,CAAA;CAC3B;AAED,kEAAkE;AAClE,wBAAsB,cAAc,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAG/D;AAED,yFAAyF;AACzF,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAEzE;AAED,iEAAiE;AACjE,wBAAsB,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAExE;AAED,+DAA+D;AAC/D,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAEnE;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GACzE,OAAO,CAAC,cAAc,CAAC,CAEzB;AAED,oFAAoF;AACpF,wBAAsB,OAAO,CAC3B,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,EAClB,WAAW,SAAK,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,WAAW,CAAC,CAMtB;AAED,qEAAqE;AACrE,wBAAsB,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM3E;AAED,wEAAwE;AACxE,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAMnF;AAED,oFAAoF;AACpF,wBAAsB,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAEtE;AAED,2FAA2F;AAC3F,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GACvE,OAAO,CAAC;IAAE,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC,CAOtF"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/mobile/api.ts"],"names":[],"mappings":"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"}
@@ -66,3 +66,32 @@ export async function selectModel(sessionId, selection) {
66
66
  ...(selection.reasoningEffort !== undefined ? { reasoningEffort: selection.reasoningEffort } : {}),
67
67
  });
68
68
  }
69
+ /** Fetch pending approvals and questions for one session (polling fallback data source). */
70
+ export async function fetchPending(sessionId) {
71
+ return await callUnary('mobile.pending', { sessionId });
72
+ }
73
+ /** Submit an approval decision (allowed-once or rejected). */
74
+ export async function respondApproval(sessionId, approvalId, outcome) {
75
+ await callUnary('mobile.respond', {
76
+ sessionId,
77
+ type: 'approval',
78
+ approvalId,
79
+ outcome,
80
+ });
81
+ }
82
+ /** Submit answers to a question group. */
83
+ export async function respondQuestion(sessionId, answers) {
84
+ await callUnary('mobile.respond', {
85
+ sessionId,
86
+ type: 'question',
87
+ answers,
88
+ });
89
+ }
90
+ /** Browse one directory level on the host (defaults to home directory). */
91
+ export async function listDirectory(path) {
92
+ return await callUnary('host.listDirectory', path === undefined ? {} : { path });
93
+ }
94
+ /** Create a workspace from an existing host directory (does not mkdir). */
95
+ export async function createWorkspace(path) {
96
+ return await callUnary('workspace.create', { path });
97
+ }
@@ -9,7 +9,13 @@
9
9
  * Pure and exported for tests.
10
10
  * @module dsh-remote-web-ui/mobile/markdown
11
11
  */
12
- /** Escape HTML special characters. */
12
+ /**
13
+ * Escape HTML special characters. One regex pass replaces all five
14
+ * characters through the map; the output is identical to five sequential
15
+ * passes (each replacement string contains none of the escaped characters),
16
+ * and the common no-special-character case scans the string only once
17
+ * instead of five times.
18
+ */
13
19
  export declare function escapeHtml(text: string): string;
14
20
  /**
15
21
  * Guard a raw link/image target against dangerous protocols. Returns the
@@ -1 +1 @@
1
- {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../src/mobile/markdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,sCAAsC;AACtC,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO/C;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASlD;AAgBD,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqFjD;AAED,uDAAuD;AACvD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA+HrD"}
1
+ {"version":3,"file":"markdown.d.ts","sourceRoot":"","sources":["../../../src/mobile/markdown.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAWH;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASlD;AAgBD,2EAA2E;AAC3E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAqFjD;AAED,uDAAuD;AACvD,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CA+HrD"}