@linxin666/dsh-remote-web-ui 0.2.6 → 0.2.8

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 (86) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +8 -1
  3. package/README.zh.md +2 -1
  4. package/lib/client.js +55 -10
  5. package/lib/client.js.map +1 -1
  6. package/lib/index.js +12 -8
  7. package/lib/mobile.js +37 -18
  8. package/lib/mobile.js.map +1 -1
  9. package/lib/types/client/FenceNotice.d.ts +3 -2
  10. package/lib/types/client/FenceNotice.d.ts.map +1 -1
  11. package/lib/types/client/FooterRemoteEntry.js +1 -1
  12. package/lib/types/client/PluginSettingsCard.d.ts +1 -1
  13. package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
  14. package/lib/types/client/RemoteEntry.js +8 -8
  15. package/lib/types/client/RemotePanel.js +3 -3
  16. package/lib/types/client/RemoteSettingsCard.d.ts.map +1 -1
  17. package/lib/types/client/RemoteSettingsCard.js +2 -2
  18. package/lib/types/client/UpdateEntry.d.ts.map +1 -1
  19. package/lib/types/client/UpdateEntry.js +2 -2
  20. package/lib/types/client/UpdatePanel.js +1 -1
  21. package/lib/types/client/deep-link.js +1 -1
  22. package/lib/types/client/index.d.ts.map +1 -1
  23. package/lib/types/client/index.js +45 -23
  24. package/lib/types/client/pair-api.d.ts +6 -0
  25. package/lib/types/client/pair-api.d.ts.map +1 -1
  26. package/lib/types/client/pair-api.js +11 -0
  27. package/lib/types/client/remote-channel.d.ts +25 -0
  28. package/lib/types/client/remote-channel.d.ts.map +1 -1
  29. package/lib/types/client/remote-channel.js +24 -0
  30. package/lib/types/client/settings-form.d.ts.map +1 -1
  31. package/lib/types/client/update-api.d.ts.map +1 -1
  32. package/lib/types/gate.js +1 -1
  33. package/lib/types/index.js +15 -15
  34. package/lib/types/invariant.d.ts.map +1 -1
  35. package/lib/types/loopback-proxy.js +1 -1
  36. package/lib/types/mobile/PairRequiredView.js +1 -1
  37. package/lib/types/mobile/PairRequiredView.test.js +1 -1
  38. package/lib/types/mobile/api.js +1 -1
  39. package/lib/types/mobile/index.js +5 -5
  40. package/lib/types/mobile/markdown.test.js +1 -1
  41. package/lib/types/mobile/messages.d.ts.map +1 -1
  42. package/lib/types/mobile/messages.js +5 -3
  43. package/lib/types/mobile/messages.test.js +21 -1
  44. package/lib/types/mobile/mobile-theme.test.js +2 -2
  45. package/lib/types/mobile/mux.d.ts +7 -4
  46. package/lib/types/mobile/mux.d.ts.map +1 -1
  47. package/lib/types/mobile/mux.js +44 -16
  48. package/lib/types/mobile/mux.test.js +58 -1
  49. package/lib/types/mobile/rpc.d.ts.map +1 -1
  50. package/lib/types/mobile/theme-toggle.js +1 -1
  51. package/lib/types/mobile/views/App.js +7 -7
  52. package/lib/types/mobile/views/App.test.js +2 -2
  53. package/lib/types/mobile/views/ChatView.js +6 -6
  54. package/lib/types/mobile/views/ChatView.test.js +3 -3
  55. package/lib/types/mobile/views/SessionListView.js +3 -3
  56. package/lib/types/mobile/views/SessionListView.test.js +2 -2
  57. package/lib/types/mobile/views/WorkspaceView.js +3 -3
  58. package/lib/types/mobile/views/WorkspaceView.test.js +3 -3
  59. package/lib/types/mobile-api.d.ts +1 -1
  60. package/lib/types/mobile-api.d.ts.map +1 -1
  61. package/lib/types/mobile-api.js +2 -2
  62. package/lib/types/pairing-access.d.ts.map +1 -1
  63. package/lib/types/pairing.d.ts.map +1 -1
  64. package/lib/types/pairing.persist.test.js +2 -2
  65. package/lib/types/remote-api.js +6 -6
  66. package/lib/types/remote-methods.d.ts.map +1 -1
  67. package/lib/types/routes.d.ts +9 -7
  68. package/lib/types/routes.d.ts.map +1 -1
  69. package/lib/types/routes.js +8 -5
  70. package/lib/types/tunnel.d.ts.map +1 -1
  71. package/lib/types/update-routes.d.ts +2 -2
  72. package/lib/types/update-routes.d.ts.map +1 -1
  73. package/lib/types/update-routes.js +1 -1
  74. package/package.json +15 -12
  75. package/src/client/FenceNotice.tsx +3 -2
  76. package/src/client/RemoteEntry.tsx +3 -2
  77. package/src/client/UpdateEntry.tsx +1 -0
  78. package/src/client/index.ts +40 -15
  79. package/src/client/pair-api.ts +16 -0
  80. package/src/client/remote-channel.ts +42 -0
  81. package/src/index.ts +1 -1
  82. package/src/mobile/messages.test.ts +23 -0
  83. package/src/mobile/messages.ts +5 -3
  84. package/src/mobile/mux.test.ts +64 -0
  85. package/src/mobile/mux.ts +44 -17
  86. package/src/routes.ts +7 -2
@@ -470,7 +470,8 @@ function applyTurnEnd(state, event) {
470
470
  ...message,
471
471
  ...(wasPending ? { pending: false } : {}),
472
472
  ...(failed ? { failed: true } : {}),
473
- seq: Math.max(message.seq, event.seq),
473
+ // Preserve each step's own final-event seq. Collapsing every message
474
+ // onto turn/end makes same-turn ordering depend on arbitrary ids.
474
475
  time: event.time,
475
476
  });
476
477
  }
@@ -542,10 +543,11 @@ function snapshotOf(state) {
542
543
  for (let index = 1; index < out.length; index += 1) {
543
544
  const prev = out[index - 1];
544
545
  const current = out[index];
545
- if (prev.seq > current.seq || (prev.seq === current.seq && prev.id >= current.id)) {
546
+ if (prev.seq > current.seq) {
546
547
  ordered = false;
547
548
  break;
548
549
  }
549
550
  }
550
- return ordered ? out : out.sort((a, b) => a.seq - b.seq || (a.id < b.id ? -1 : 1));
551
+ // Array.sort is stable: equal-seq rows keep their event insertion order.
552
+ return ordered ? out : out.sort((a, b) => a.seq - b.seq);
551
553
  }
@@ -1,6 +1,6 @@
1
1
  /** foldEvents: message-list folding from a session event stream. */
2
2
  import { describe, expect, it } from 'vitest';
3
- import { EventFolder, foldEvents } from './messages.js';
3
+ import { EventFolder, foldEvents } from "./messages.js";
4
4
  /** Assemble one event with an auto-incrementing seq / time. */
5
5
  function makeEvent(type, data, seq, time = seq * 1_000) {
6
6
  return { type, seq, time, data };
@@ -152,6 +152,26 @@ describe('foldEvents', () => {
152
152
  expect(assistant?.failed).toBeFalsy();
153
153
  expect(assistant?.pending).toBeFalsy();
154
154
  });
155
+ it('keeps same-turn steps in final-event order after turn/end', () => {
156
+ const result = foldEvents([
157
+ makeEvent('user/message', userMessageData('u-1', 'multi-step'), 0),
158
+ makeEvent('assistant/message', assistantMessageData('step-z', 0, 0, 'first'), 2),
159
+ makeEvent('assistant/message', assistantMessageData('step-a', 0, 1, 'second'), 4),
160
+ makeEvent('assistant/message', assistantMessageData('step-m', 0, 2, 'third'), 6),
161
+ makeEvent('turn/end', { turn: 0, reason: { kind: 'completed' } }, 7),
162
+ ]);
163
+ const assistants = result.filter(message => message.kind === 'assistant');
164
+ expect(assistants.map(message => message.id)).toEqual(['step-z', 'step-a', 'step-m']);
165
+ expect(assistants.map(message => message.seq)).toEqual([2, 4, 6]);
166
+ expect(assistants.every(message => message.pending !== true)).toBe(true);
167
+ });
168
+ it('keeps stable insertion order when legacy rows share a seq', () => {
169
+ const folder = new EventFolder([
170
+ { id: 'z-last-lexically', kind: 'assistant', text: 'first', seq: 5, time: 5_000 },
171
+ { id: 'a-first-lexically', kind: 'assistant', text: 'second', seq: 5, time: 5_000 },
172
+ ]);
173
+ expect(folder.snapshot().map(message => message.id)).toEqual(['z-last-lexically', 'a-first-lexically']);
174
+ });
155
175
  it('is idempotent: applying the same batch twice yields an identical list', () => {
156
176
  const events = [
157
177
  makeEvent('user/message', userMessageData('u-1', 'hi'), 0),
@@ -1,7 +1,7 @@
1
1
  // @vitest-environment jsdom
2
2
  /** mobile-theme: light default, persisted dark toggle, document wiring. */
3
3
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
4
- import { getMobileTheme, setMobileTheme, subscribeMobileTheme, toggleMobileTheme, } from './mobile-theme.js';
4
+ import { getMobileTheme, setMobileTheme, subscribeMobileTheme, toggleMobileTheme, } from "./mobile-theme.js";
5
5
  const STORAGE_KEY = 'dsh.remote.theme';
6
6
  /** jsdom in this setup ships a bare localStorage object; install a real fake. */
7
7
  function makeStorage() {
@@ -54,7 +54,7 @@ describe('mobile-theme', () => {
54
54
  it('boot reads the stored theme and initMobileTheme applies it', async () => {
55
55
  storage.setItem(STORAGE_KEY, 'dark');
56
56
  vi.resetModules();
57
- const fresh = await import('./mobile-theme.js');
57
+ const fresh = await import("./mobile-theme.js");
58
58
  expect(fresh.getMobileTheme()).toBe('dark');
59
59
  fresh.initMobileTheme();
60
60
  expect(document.documentElement.dataset.theme).toBe('dark');
@@ -35,9 +35,9 @@ export interface MuxClientOptions {
35
35
  * the ordinary HTTP channel (unaffected by SSE-impairing tunnels).
36
36
  */
37
37
  pollLatest?: (sessionId: string) => Promise<HistoryPage>;
38
- /** Poll cadence while SSE is stalled (default 3000 ms). */
38
+ /** Initial poll cadence while SSE is stalled (default 3000 ms). Empty polls back off to 60000 ms. */
39
39
  pollIntervalMs?: number;
40
- /** How long SSE must go without a frame before fallback kicks in (default 12000 ms). */
40
+ /** Initial SSE stall window (default 12000 ms); a previously-live stream gets three windows before fallback. */
41
41
  stallThresholdMs?: number;
42
42
  /** Clock seam for tests (defaults to Date.now). */
43
43
  now?: () => number;
@@ -72,8 +72,8 @@ export declare class MuxClient {
72
72
  private lastDataAt;
73
73
  /**
74
74
  * Whether the SSE channel has ever delivered a frame in this stream (a
75
- * delivered frame proves the tunnel forwards SSE; silence alone then means
76
- * the agent idle, not a dead channel — only an onerror re-arms fallback).
75
+ * delivered frame proves the tunnel can forward SSE; sustained silence may
76
+ * still mean a suspended mobile tunnel, so polling re-arms after 3 windows).
77
77
  */
78
78
  private sseAlive;
79
79
  /** Per-session highest event seq already emitted, for poll dedup. */
@@ -83,6 +83,8 @@ export declare class MuxClient {
83
83
  private polling;
84
84
  /** Epoch ms of the next due poll while polling (kept on the same tick timer). */
85
85
  private nextPollAt;
86
+ /** Adaptive delay: productive polls reset it; empty/error polls add one base interval up to one minute. */
87
+ private pollDelayMs;
86
88
  /**
87
89
  * @param url - the mobile events endpoint (browser-relative).
88
90
  * @param options - seams.
@@ -110,6 +112,7 @@ export declare class MuxClient {
110
112
  private startTick;
111
113
  private stopTick;
112
114
  private tick;
115
+ private isSseStalled;
113
116
  private startPolling;
114
117
  private stopPolling;
115
118
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"mux.d.ts","sourceRoot":"","sources":["../../../src/mobile/mux.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2CAA2C,CAAA;AAGzE,OAAO,EAA2B,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AAEpE,kCAAkC;AAClC,MAAM,WAAW,gBAAgB;IAC/B,iEAAiE;IACjE,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,eAAe,CAAA;IAChD;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAA;IACxD,2DAA2D;IAC3D,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,wFAAwF;IACxF,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAA;IACrD,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAA;IAC1C,KAAK,IAAI,IAAI,CAAA;CACd;AAuBD;;;;;GAKG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkC;IAChE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6C;IACxE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IACzC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuC;IACjE,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAE5B,wEAAwE;IACxE,OAAO,CAAC,gBAAgB,CAAoB;IAC5C,6EAA6E;IAC7E,OAAO,CAAC,UAAU,CAAI;IACtB;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAQ;IACxB,qEAAqE;IACrE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA4B;IAC1D,+FAA+F;IAC/F,OAAO,CAAC,SAAS,CAA4C;IAC7D,OAAO,CAAC,OAAO,CAAQ;IACvB,iFAAiF;IACjF,OAAO,CAAC,UAAU,CAAI;IAEtB;;;OAGG;IACH,YAAY,GAAG,SAAsB,EAAE,OAAO,GAAE,gBAAqB,EAOpE;IAED,+EAA+E;IAC/E,KAAK,IAAI,IAAI,CAKZ;IAED,sBAAsB;IACtB,IAAI,IAAI,IAAI,CAOX;IAED,sEAAsE;IACtE,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI,CAGvD;IAED;;;;OAIG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAU3C;IAED,OAAO,CAAC,OAAO;IAqBf;;;;;OAKG;IACH,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,IAAI;IAkBZ,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,WAAW;IAInB;;;;OAIG;YACW,QAAQ;IAsBtB,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,IAAI;IAUZ,OAAO,CAAC,WAAW;CAapB"}
1
+ {"version":3,"file":"mux.d.ts","sourceRoot":"","sources":["../../../src/mobile/mux.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2CAA2C,CAAA;AAGzE,OAAO,EAA2B,KAAK,WAAW,EAAE,MAAM,UAAU,CAAA;AAEpE,kCAAkC;AAClC,MAAM,WAAW,gBAAgB;IAC/B,iEAAiE;IACjE,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,eAAe,CAAA;IAChD;;;;OAIG;IACH,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC,CAAA;IACxD,qGAAqG;IACrG,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,gHAAgH;IAChH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAED,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAA;IACrD,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAA;IAC1C,KAAK,IAAI,IAAI,CAAA;CACd;AAyBD;;;;;GAKG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAkC;IAChE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6C;IACxE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAQ;IACzC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAuC;IACjE,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAQ;IAE5B,wEAAwE;IACxE,OAAO,CAAC,gBAAgB,CAAoB;IAC5C,6EAA6E;IAC7E,OAAO,CAAC,UAAU,CAAI;IACtB;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAQ;IACxB,qEAAqE;IACrE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA4B;IAC1D,+FAA+F;IAC/F,OAAO,CAAC,SAAS,CAA4C;IAC7D,OAAO,CAAC,OAAO,CAAQ;IACvB,iFAAiF;IACjF,OAAO,CAAC,UAAU,CAAI;IACtB,2GAA2G;IAC3G,OAAO,CAAC,WAAW,CAAQ;IAE3B;;;OAGG;gBACS,GAAG,SAAsB,EAAE,OAAO,GAAE,gBAAqB;IAUrE,+EAA+E;IAC/E,KAAK,IAAI,IAAI;IAOb,sBAAsB;IACtB,IAAI,IAAI,IAAI;IASZ,sEAAsE;IACtE,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,GAAG,MAAM,IAAI;IAKxD;;;;OAIG;IACH,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAU5C,OAAO,CAAC,OAAO;IAqBf;;;;;OAKG;IACH,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,IAAI;IAgBZ,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,YAAY;IAQpB,OAAO,CAAC,WAAW;IAMnB;;;;OAIG;YACW,QAAQ;IAsCtB,OAAO,CAAC,aAAa;IAuBrB,OAAO,CAAC,IAAI;IAUZ,OAAO,CAAC,WAAW;CAapB"}
@@ -25,7 +25,7 @@
25
25
  */
26
26
  import { muxFrameSchema } from '@deepseek-ai/dsh-host-apiproxy/api/events.schema';
27
27
  import { serverRequestSchema } from '@deepseek-ai/dsh-host-apiproxy/api/rpc.schema';
28
- import { history as fetchHistory } from './api.js';
28
+ import { history as fetchHistory } from "./api.js";
29
29
  /** Browser default source factory. */
30
30
  function browserSource(url) {
31
31
  // The DOM EventSource is structurally compatible; the `this`-typed handler
@@ -34,6 +34,8 @@ function browserSource(url) {
34
34
  }
35
35
  const DEFAULT_POLL_INTERVAL_MS = 3000;
36
36
  const DEFAULT_STALL_THRESHOLD_MS = 12000;
37
+ const LIVE_SSE_STALL_MULTIPLIER = 3;
38
+ const MAX_POLL_BACKOFF_MS = 60000;
37
39
  /**
38
40
  * Stall-check granularity: the single scheduler tick runs at least this
39
41
  * often so fallback arms within a second of the stall threshold passing,
@@ -64,8 +66,8 @@ export class MuxClient {
64
66
  lastDataAt = 0;
65
67
  /**
66
68
  * Whether the SSE channel has ever delivered a frame in this stream (a
67
- * delivered frame proves the tunnel forwards SSE; silence alone then means
68
- * the agent idle, not a dead channel — only an onerror re-arms fallback).
69
+ * delivered frame proves the tunnel can forward SSE; sustained silence may
70
+ * still mean a suspended mobile tunnel, so polling re-arms after 3 windows).
69
71
  */
70
72
  sseAlive = false;
71
73
  /** Per-session highest event seq already emitted, for poll dedup. */
@@ -75,6 +77,8 @@ export class MuxClient {
75
77
  polling = false;
76
78
  /** Epoch ms of the next due poll while polling (kept on the same tick timer). */
77
79
  nextPollAt = 0;
80
+ /** Adaptive delay: productive polls reset it; empty/error polls add one base interval up to one minute. */
81
+ pollDelayMs;
78
82
  /**
79
83
  * @param url - the mobile events endpoint (browser-relative).
80
84
  * @param options - seams.
@@ -84,6 +88,7 @@ export class MuxClient {
84
88
  this.sourceFactory = options.sourceFactory ?? browserSource;
85
89
  this.pollLatest = options.pollLatest ?? ((sessionId) => fetchHistory(sessionId, undefined, DEFAULT_POLL_PAGE_SIZE));
86
90
  this.pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
91
+ this.pollDelayMs = this.pollIntervalMs;
87
92
  this.stallThresholdMs = options.stallThresholdMs ?? DEFAULT_STALL_THRESHOLD_MS;
88
93
  this.now = options.now ?? (() => Date.now());
89
94
  }
@@ -121,9 +126,8 @@ export class MuxClient {
121
126
  return;
122
127
  }
123
128
  // If SSE is already stalled for this session, start patching right away.
124
- if (!this.polling && !this.stopped && !this.sseAlive && (this.now() - this.lastDataAt) > this.stallThresholdMs) {
129
+ if (!this.polling && !this.stopped && this.isSseStalled())
125
130
  this.startPolling();
126
- }
127
131
  }
128
132
  connect() {
129
133
  // A fresh stream starts unknown; only a delivered frame proves it works.
@@ -170,30 +174,36 @@ export class MuxClient {
170
174
  if (this.observeSessionId === undefined)
171
175
  return;
172
176
  if (this.polling) {
173
- // The stall phase has ended; the same tick now paces the polls.
177
+ // The stall phase has ended; the same tick now paces the adaptive polls.
174
178
  if (this.now() >= this.nextPollAt) {
175
- this.nextPollAt = this.now() + this.pollIntervalMs;
179
+ // pollTick schedules the next run after it settles, so slow requests
180
+ // cannot overlap with another scheduler tick.
181
+ this.nextPollAt = Number.POSITIVE_INFINITY;
176
182
  void this.pollTick();
177
183
  }
178
184
  return;
179
185
  }
180
- // A live SSE channel only goes silent while the agent idles; never poll
181
- // against it. Fallback arms again only via onerror or a stream that has
182
- // never delivered.
183
- if (this.sseAlive)
184
- return;
185
- if ((this.now() - this.lastDataAt) > this.stallThresholdMs)
186
+ if (this.isSseStalled())
186
187
  this.startPolling();
187
188
  }
189
+ isSseStalled() {
190
+ const windowMs = this.sseAlive
191
+ ? this.stallThresholdMs * LIVE_SSE_STALL_MULTIPLIER
192
+ : this.stallThresholdMs;
193
+ return (this.now() - this.lastDataAt) > windowMs;
194
+ }
188
195
  startPolling() {
189
196
  if (this.polling || this.stopped)
190
197
  return;
191
198
  this.polling = true;
192
- this.nextPollAt = this.now() + this.pollIntervalMs;
199
+ this.pollDelayMs = this.pollIntervalMs;
200
+ this.nextPollAt = Number.POSITIVE_INFINITY;
193
201
  void this.pollTick();
194
202
  }
195
203
  stopPolling() {
196
204
  this.polling = false;
205
+ this.pollDelayMs = this.pollIntervalMs;
206
+ this.nextPollAt = 0;
197
207
  }
198
208
  /**
199
209
  * Fetch the latest history page for the observed session and re-emit any
@@ -206,21 +216,39 @@ export class MuxClient {
206
216
  this.stopPolling();
207
217
  return;
208
218
  }
219
+ let emitted = 0;
209
220
  try {
210
221
  const page = await this.pollLatest(sessionId);
211
222
  let maxSeq = this.pollWatermark.get(sessionId) ?? -1;
212
- for (const entry of page.events) {
223
+ const ordered = [...page.events].sort((left, right) => {
224
+ const leftSeq = typeof left.event?.seq === 'number' ? left.event.seq : -1;
225
+ const rightSeq = typeof right.event?.seq === 'number' ? right.event.seq : -1;
226
+ return leftSeq - rightSeq;
227
+ });
228
+ for (const entry of ordered) {
213
229
  const event = entry.event;
214
230
  const seq = typeof event?.seq === 'number' ? event.seq : -1;
215
231
  if (seq <= maxSeq)
216
232
  continue;
217
233
  maxSeq = seq;
234
+ emitted += 1;
218
235
  this.emit({ type: 'session/event', sessionId: sessionId, event });
219
236
  }
220
237
  this.pollWatermark.set(sessionId, maxSeq);
221
238
  }
222
239
  catch {
223
- // Transient (network, pairing, history paging); the next tick retries.
240
+ // Transient (network, pairing, history paging); retry with backoff.
241
+ }
242
+ finally {
243
+ if (emitted > 0) {
244
+ this.pollDelayMs = this.pollIntervalMs;
245
+ }
246
+ else {
247
+ this.pollDelayMs = Math.min(MAX_POLL_BACKOFF_MS, this.pollDelayMs + this.pollIntervalMs);
248
+ }
249
+ if (this.polling && this.observeSessionId === sessionId) {
250
+ this.nextPollAt = this.now() + this.pollDelayMs;
251
+ }
224
252
  }
225
253
  }
226
254
  handleMessage(data) {
@@ -1,6 +1,6 @@
1
1
  /** mux: live-event client, SSE delivery + stall-driven polling fallback. */
2
2
  import { beforeEach, describe, expect, it, vi } from 'vitest';
3
- import { MuxClient } from './mux.js';
3
+ import { MuxClient } from "./mux.js";
4
4
  /** Create an EventSource factory that records every opened source. */
5
5
  function makeSources() {
6
6
  const sources = [];
@@ -76,6 +76,21 @@ describe('MuxClient polling fallback', () => {
76
76
  expect(frames[1]?.event).toMatchObject({ seq: 1 });
77
77
  client.stop();
78
78
  });
79
+ it('sorts an out-of-order history page before advancing the watermark', async () => {
80
+ const { factory } = makeSources();
81
+ const pollLatest = vi.fn(async (_sessionId) => pageOf([2, 1, 3]));
82
+ const client = new MuxClient('/m/api/events.mux', baseOptions(pollLatest, factory));
83
+ const seqs = [];
84
+ client.onFrame((frame) => {
85
+ if (frame.type === 'session/event' && typeof frame.event.seq === 'number')
86
+ seqs.push(frame.event.seq);
87
+ });
88
+ client.start();
89
+ client.observe('s1');
90
+ await vi.advanceTimersByTimeAsync(1200);
91
+ expect(seqs).toEqual([1, 2, 3]);
92
+ client.stop();
93
+ });
79
94
  it('keeps the watermark so a repeated page never re-emits old events', async () => {
80
95
  const { factory } = makeSources();
81
96
  // Two calls return the same page: the second must emit nothing.
@@ -139,6 +154,48 @@ describe('MuxClient polling fallback', () => {
139
154
  expect(live[0]).toMatchObject({ type: 'session/subscribed', sessionId: 's1' });
140
155
  client.stop();
141
156
  });
157
+ it('recovers when a previously-live SSE stream becomes silently stalled', async () => {
158
+ const { factory, sources } = makeSources();
159
+ const pollLatest = vi.fn(async (_sessionId) => pageOf([5]));
160
+ const client = new MuxClient('/m/api/events.mux', baseOptions(pollLatest, factory));
161
+ const frames = [];
162
+ client.onFrame(frame => { frames.push(frame); });
163
+ client.start();
164
+ client.observe('s1');
165
+ sources[0]?.onmessage?.({ data: envelopeWith({ type: 'session/subscribed', sessionId: 's1', lastSeq: 4 }) });
166
+ await vi.advanceTimersByTimeAsync(2400);
167
+ expect(pollLatest).not.toHaveBeenCalled();
168
+ // A once-live stream gets three stall windows; the next scheduler tick
169
+ // crosses that boundary and starts the ordinary-HTTP recovery path.
170
+ await vi.advanceTimersByTimeAsync(400);
171
+ expect(pollLatest).toHaveBeenCalledTimes(1);
172
+ expect(frames.at(-1)?.event).toMatchObject({ seq: 5 });
173
+ client.stop();
174
+ });
175
+ it('backs empty polls off and resets to the base cadence after progress', async () => {
176
+ const { factory } = makeSources();
177
+ const pages = [pageOf([]), pageOf([1]), pageOf([1, 2])];
178
+ const pollLatest = vi.fn(async (_sessionId) => pages.shift() ?? pageOf([]));
179
+ const client = new MuxClient('/m/api/events.mux', baseOptions(pollLatest, factory));
180
+ client.start();
181
+ client.observe('s1');
182
+ await vi.advanceTimersByTimeAsync(1200);
183
+ expect(pollLatest).toHaveBeenCalledTimes(1);
184
+ await vi.advanceTimersByTimeAsync(400);
185
+ expect(pollLatest).toHaveBeenCalledTimes(1);
186
+ await vi.advanceTimersByTimeAsync(400);
187
+ expect(pollLatest).toHaveBeenCalledTimes(2);
188
+ // The productive second poll resets the next delay from 800 ms to 400 ms.
189
+ await vi.advanceTimersByTimeAsync(400);
190
+ expect(pollLatest).toHaveBeenCalledTimes(3);
191
+ await vi.advanceTimersByTimeAsync(400);
192
+ expect(pollLatest).toHaveBeenCalledTimes(4);
193
+ await vi.advanceTimersByTimeAsync(400);
194
+ expect(pollLatest).toHaveBeenCalledTimes(4);
195
+ await vi.advanceTimersByTimeAsync(400);
196
+ expect(pollLatest).toHaveBeenCalledTimes(5);
197
+ client.stop();
198
+ });
142
199
  it('drives the whole lifecycle on a single scheduler tick (one interval)', async () => {
143
200
  const { factory } = makeSources();
144
201
  const setIntervalSpy = vi.spyOn(globalThis, 'setInterval');
@@ -1 +1 @@
1
- {"version":3,"file":"rpc.d.ts","sourceRoot":"","sources":["../../../src/mobile/rpc.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,0EAA0E;AAC1E,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,OAAO,EAAE,MAAM,EAG1B;CACF;AAED,kEAAkE;AAClE,qBAAa,YAAa,SAAQ,KAAK;IACrC,iDAAiD;IACjD,QAAQ,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;IAEjD,YAAY,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAInD;CACF;AAID,8EAA8E;AAC9E,wBAAgB,SAAS,IAAI,MAAM,CAMlC;AAED;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,CAAC,CAAC,CAmCZ"}
1
+ {"version":3,"file":"rpc.d.ts","sourceRoot":"","sources":["../../../src/mobile/rpc.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,0EAA0E;AAC1E,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,kEAAkE;AAClE,qBAAa,YAAa,SAAQ,KAAK;IACrC,iDAAiD;IACjD,QAAQ,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAA;gBAErC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;CAKrD;AAID,8EAA8E;AAC9E,wBAAgB,SAAS,IAAI,MAAM,CAMlC;AAED;;;;;;;;;GASG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,CAAC,CAAC,CAmCZ"}
@@ -6,7 +6,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  * mode); the choice persists across visits.
7
7
  */
8
8
  import { useSyncExternalStore } from 'react';
9
- import { getMobileTheme, subscribeMobileTheme, toggleMobileTheme } from './mobile-theme.js';
9
+ import { getMobileTheme, subscribeMobileTheme, toggleMobileTheme } from "./mobile-theme.js";
10
10
  /** Sun glyph: circle + eight rays (inline SVG, no emoji). */
11
11
  function SunIcon() {
12
12
  return (_jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", "aria-hidden": true, children: [_jsx("circle", { cx: "12", cy: "12", r: "4.2" }), _jsx("path", { d: "M12 2.5v2.4M12 19.1v2.4M2.5 12h2.4M19.1 12h2.4M5 5l1.7 1.7M17.3 17.3 19 19M19 5l-1.7 1.7M6.7 17.3 5 19" })] }));
@@ -6,13 +6,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  * affordance, and every piece of data is fetched on demand.
7
7
  */
8
8
  import { useCallback, useEffect, useRef, useState } from 'react';
9
- import { fetchMobilePreferences, history as fetchHistory, listSessions, listWorkspaces, prompt } from '../api.js';
10
- import { MuxClient } from '../mux.js';
11
- import { RpcCallError, RpcTransportError } from '../rpc.js';
12
- import { ChatView } from './ChatView.js';
13
- import { SessionListView } from './SessionListView.js';
14
- import { WorkspaceView as WorkspaceRoster } from './WorkspaceView.js';
15
- import { PairRequiredView } from '../PairRequiredView.js';
9
+ import { fetchMobilePreferences, history as fetchHistory, listSessions, listWorkspaces, prompt } from "../api.js";
10
+ import { MuxClient } from "../mux.js";
11
+ import { RpcCallError, RpcTransportError } from "../rpc.js";
12
+ import { ChatView } from "./ChatView.js";
13
+ import { SessionListView } from "./SessionListView.js";
14
+ import { WorkspaceView as WorkspaceRoster } from "./WorkspaceView.js";
15
+ import { PairRequiredView } from "../PairRequiredView.js";
16
16
  /** Read the optional workspace target carried from the pairing QR flow. */
17
17
  export function mobileWorkspaceTarget(search) {
18
18
  const value = new URLSearchParams(search).get('workspace');
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  // @vitest-environment jsdom
3
3
  import { afterEach, describe, expect, it, vi } from 'vitest';
4
4
  import { cleanup, render, screen, waitFor } from '@testing-library/react';
5
- import { RpcTransportError } from '../rpc.js';
5
+ import { RpcTransportError } from "../rpc.js";
6
6
  const api = vi.hoisted(() => ({
7
7
  fetchMobilePreferences: vi.fn(),
8
8
  history: vi.fn(),
@@ -21,7 +21,7 @@ vi.mock('../mux.ts', () => ({
21
21
  vi.mock('./WorkspaceView.tsx', () => ({ WorkspaceView: () => _jsx("div", { children: "workspace-ready" }) }));
22
22
  vi.mock('./SessionListView.tsx', () => ({ SessionListView: () => _jsx("div", { children: "sessions-ready" }) }));
23
23
  vi.mock('./ChatView.tsx', () => ({ ChatView: () => _jsx("div", { children: "chat-ready" }) }));
24
- import { App, mobilePairStateForError } from './App.js';
24
+ import { App, mobilePairStateForError } from "./App.js";
25
25
  afterEach(() => {
26
26
  cleanup();
27
27
  vi.clearAllMocks();
@@ -12,12 +12,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
12
  * permission pickers, both as bottom sheets.
13
13
  */
14
14
  import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
15
- import { loadHistory, prompt } from './App.js';
16
- import { errorText, formatTime, staleHostHint } from './App.js';
17
- import { fetchMobilePreferences, models, selectModel, sendCommand } from '../api.js';
18
- import { EventFolder, foldEvents } from '../messages.js';
19
- import { renderMarkdown } from '../markdown.js';
20
- import { ThemeToggle } from '../theme-toggle.js';
15
+ import { loadHistory, prompt } from "./App.js";
16
+ import { errorText, formatTime, staleHostHint } from "./App.js";
17
+ import { fetchMobilePreferences, models, selectModel, sendCommand } from "../api.js";
18
+ import { EventFolder, foldEvents } from "../messages.js";
19
+ import { renderMarkdown } from "../markdown.js";
20
+ import { ThemeToggle } from "../theme-toggle.js";
21
21
  /**
22
22
  * Hard cap on live events buffered while the initial history tail page is in
23
23
  * flight. Beyond this the oldest buffered event is dropped and a follow-up
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  /** ChatView: collapsible message folds, toolbar chips, and the bottom sheets. */
4
4
  import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest';
5
5
  import { act, cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
6
- import { ChatView, MAX_TAIL_BUFFER_EVENTS } from './ChatView.js';
6
+ import { ChatView, MAX_TAIL_BUFFER_EVENTS } from "./ChatView.js";
7
7
  // The api module is fully mocked; App.tsx's history wrapper is overridden to
8
8
  // feed fixed history pages, its pure helpers (errorText / formatTime) stay real.
9
9
  vi.mock('../api.ts', () => ({
@@ -20,8 +20,8 @@ vi.mock('./App.tsx', async (importOriginal) => {
20
20
  prompt: vi.fn(async () => { }),
21
21
  };
22
22
  });
23
- import { fetchMobilePreferences, models, selectModel, sendCommand } from '../api.js';
24
- import { loadHistory, prompt } from './App.js';
23
+ import { fetchMobilePreferences, models, selectModel, sendCommand } from "../api.js";
24
+ import { loadHistory, prompt } from "./App.js";
25
25
  const session = {
26
26
  sessionId: 's-1',
27
27
  title: '测试会话',
@@ -12,9 +12,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
12
  * the new chat — the same "new session opens" flow as the desktop UI.
13
13
  */
14
14
  import { useCallback, useEffect, useRef, useState } from 'react';
15
- import { createSession, listAgentPresets, listSessions, listWorkspaces } from '../api.js';
16
- import { errorText, formatTime, staleHostHint, toSessionView } from './App.js';
17
- import { ThemeToggle } from '../theme-toggle.js';
15
+ import { createSession, listAgentPresets, listSessions, listWorkspaces } from "../api.js";
16
+ import { errorText, formatTime, staleHostHint, toSessionView } from "./App.js";
17
+ import { ThemeToggle } from "../theme-toggle.js";
18
18
  /** Rows that belong to the opened workspace (its owned session id set). */
19
19
  function ownedItems(page, workspace) {
20
20
  const owned = new Set(workspace.sessionIds);
@@ -3,7 +3,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  /** SessionListView: owned-row filtering, incremental pages, session creation. */
4
4
  import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
5
  import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
6
- import { SessionListView } from './SessionListView.js';
6
+ import { SessionListView } from "./SessionListView.js";
7
7
  // The api module is fully mocked; the view's App.tsx helpers stay real.
8
8
  vi.mock('../api.ts', () => ({
9
9
  listSessions: vi.fn(),
@@ -11,7 +11,7 @@ vi.mock('../api.ts', () => ({
11
11
  listAgentPresets: vi.fn(),
12
12
  createSession: vi.fn(),
13
13
  }));
14
- import { createSession, listAgentPresets, listSessions, listWorkspaces } from '../api.js';
14
+ import { createSession, listAgentPresets, listSessions, listWorkspaces } from "../api.js";
15
15
  const listSessionsMock = vi.mocked(listSessions);
16
16
  const listWorkspacesMock = vi.mocked(listWorkspaces);
17
17
  const listAgentPresetsMock = vi.mocked(listAgentPresets);
@@ -6,9 +6,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  * until a workspace is opened).
7
7
  */
8
8
  import { useEffect, useState } from 'react';
9
- import { listWorkspaces } from '../api.js';
10
- import { errorText } from './App.js';
11
- import { ThemeToggle } from '../theme-toggle.js';
9
+ import { listWorkspaces } from "../api.js";
10
+ import { errorText } from "./App.js";
11
+ import { ThemeToggle } from "../theme-toggle.js";
12
12
  /**
13
13
  * Render the workspace roster.
14
14
  * @param props - the pick action.
@@ -3,12 +3,12 @@ import { jsx as _jsx } from "react/jsx-runtime";
3
3
  /** Mobile workspace landing: roster rendering and QR deep-link selection. */
4
4
  import { afterEach, describe, expect, it, vi } from 'vitest';
5
5
  import { cleanup, render, screen, waitFor } from '@testing-library/react';
6
- import { mobileWorkspaceTarget } from './App.js';
7
- import { WorkspaceView } from './WorkspaceView.js';
6
+ import { mobileWorkspaceTarget } from "./App.js";
7
+ import { WorkspaceView } from "./WorkspaceView.js";
8
8
  vi.mock('../api.ts', () => ({
9
9
  listWorkspaces: vi.fn(),
10
10
  }));
11
- import { listWorkspaces } from '../api.js';
11
+ import { listWorkspaces } from "../api.js";
12
12
  const listWorkspacesMock = vi.mocked(listWorkspaces);
13
13
  const workspaces = [
14
14
  {
@@ -34,7 +34,7 @@ export interface MobileApiDeps {
34
34
  }
35
35
  /** Mobile API route paths. */
36
36
  export declare const MOBILE_API_PATHS: {
37
- readonly events: '/m/api/events.mux';
37
+ readonly events: "/m/api/events.mux";
38
38
  };
39
39
  /**
40
40
  * Build the mobile data-channel routes.
@@ -1 +1 @@
1
- {"version":3,"file":"mobile-api.d.ts","sourceRoot":"","sources":["../../src/mobile-api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAA;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAA;AAG9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAyDlD,iCAAiC;AACjC,MAAM,WAAW,aAAa;IAC5B,uDAAuD;IACvD,OAAO,EAAE,cAAc,CAAA;IACvB,0DAA0D;IAC1D,QAAQ,EAAE,QAAQ,CAAA;IAClB,kEAAkE;IAClE,iBAAiB,EAAE,MAAM,OAAO,CAAA;IAChC,oFAAoF;IACpF,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,8BAA8B;AAC9B,eAAO,MAAM,gBAAgB;aAC3B,MAAM,EAAE,mBAAmB;CACnB,CAAA;AAOV;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,aAAa,GAAG,QAAQ,EAAE,CA4InE"}
1
+ {"version":3,"file":"mobile-api.d.ts","sourceRoot":"","sources":["../../src/mobile-api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAA;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAA;AAG9D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAyDlD,iCAAiC;AACjC,MAAM,WAAW,aAAa;IAC5B,uDAAuD;IACvD,OAAO,EAAE,cAAc,CAAA;IACvB,0DAA0D;IAC1D,QAAQ,EAAE,QAAQ,CAAA;IAClB,kEAAkE;IAClE,iBAAiB,EAAE,MAAM,OAAO,CAAA;IAChC,oFAAoF;IACpF,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B;AAED,8BAA8B;AAC9B,eAAO,MAAM,gBAAgB;;CAEnB,CAAA;AAOV;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,aAAa,GAAG,QAAQ,EAAE,CA4InE"}
@@ -19,8 +19,8 @@
19
19
  * unary channel), gated identically.
20
20
  */
21
21
  import { RpcId } from '@deepseek-ai/dsh-host-apiproxy/api/rpc';
22
- import { readBoundedJson, writeJson } from './http.js';
23
- import { readCookie } from './gate.js';
22
+ import { readBoundedJson, writeJson } from "./http.js";
23
+ import { readCookie } from "./gate.js";
24
24
  /**
25
25
  * Methods the phone surface may call. Everything else is refused HERE — but
26
26
  * note the paired-device cookie also passes the global api/gate for the full
@@ -1 +1 @@
1
- {"version":3,"file":"pairing-access.d.ts","sourceRoot":"","sources":["../../src/pairing-access.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE3D,wDAAwD;AACxD,eAAO,MAAM,qBAAqB,uBAAuB,CAAA;AAEzD;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,OAAO;IAO3C,OAAO,CAAC,QAAQ,CAAC,KAAK;IANxB;;;OAGG;IACH,YACE,GAAG,EAAE,OAAO,EACK,KAAK,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,EAG9D;IAED;;;;OAIG;IACH,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAEhD;CACF"}
1
+ {"version":3,"file":"pairing-access.d.ts","sourceRoot":"","sources":["../../src/pairing-access.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE3D,wDAAwD;AACxD,eAAO,MAAM,qBAAqB,uBAAuB,CAAA;AAEzD;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,OAAO;IAO3C,OAAO,CAAC,QAAQ,CAAC,KAAK;IANxB;;;OAGG;gBAED,GAAG,EAAE,OAAO,EACK,KAAK,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO;IAK/D;;;;OAIG;IACH,cAAc,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO;CAGlD"}
@@ -1 +1 @@
1
- {"version":3,"file":"pairing.d.ts","sourceRoot":"","sources":["../../src/pairing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pairing.d.ts","sourceRoot":"","sources":["../../src/pairing.ts"],"names":[],"mappings":"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"}
@@ -8,8 +8,8 @@ import { mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from 'node
8
8
  import { tmpdir } from 'node:os';
9
9
  import { join } from 'node:path';
10
10
  import { afterEach, beforeEach, describe, expect, it } from 'vitest';
11
- import { pairingConfigOf } from './index.js';
12
- import { DEFAULT_IDLE_EXPIRE_MS, PairingService } from './pairing.js';
11
+ import { pairingConfigOf } from "./index.js";
12
+ import { DEFAULT_IDLE_EXPIRE_MS, PairingService } from "./pairing.js";
13
13
  const BASE_CONFIG = {
14
14
  tokenTtlMs: 60_000,
15
15
  offlineAfterMs: 25_000,
@@ -19,12 +19,12 @@
19
19
  * browser marker added after authentication. Plugin loopback fences then
20
20
  * pass. The pairing cookie never leaves this process.
21
21
  */
22
- import { readCookie } from './gate.js';
23
- import { writeJson } from './http.js';
24
- import { proxyLoopbackHttp, proxyLoopbackUpgrade } from './loopback-proxy.js';
25
- import { DESKTOP_LAUNCHER_PATH, LOOPBACK_ONLY_METHODS, PLUGIN_MANAGER_PATH, REMOTE_PREFIX, REMOTE_UPGRADE_PATHS, WEB_UI_SETTINGS_BRIDGE_PATH, } from './remote-methods.js';
26
- export { DESKTOP_LAUNCHER_PATH, LOOPBACK_ONLY_METHODS, PLUGIN_MANAGER_PATH, REMOTE_API_PATHS, REMOTE_PREFIX, REMOTE_UPGRADE_PATHS, WEB_UI_SETTINGS_BRIDGE_PATH, } from './remote-methods.js';
27
- export { REMOTE_API_PREFIX } from './remote-methods.js';
22
+ import { readCookie } from "./gate.js";
23
+ import { writeJson } from "./http.js";
24
+ import { proxyLoopbackHttp, proxyLoopbackUpgrade } from "./loopback-proxy.js";
25
+ import { DESKTOP_LAUNCHER_PATH, LOOPBACK_ONLY_METHODS, PLUGIN_MANAGER_PATH, REMOTE_PREFIX, REMOTE_UPGRADE_PATHS, WEB_UI_SETTINGS_BRIDGE_PATH, } from "./remote-methods.js";
26
+ export { DESKTOP_LAUNCHER_PATH, LOOPBACK_ONLY_METHODS, PLUGIN_MANAGER_PATH, REMOTE_API_PATHS, REMOTE_PREFIX, REMOTE_UPGRADE_PATHS, WEB_UI_SETTINGS_BRIDGE_PATH, } from "./remote-methods.js";
27
+ export { REMOTE_API_PREFIX } from "./remote-methods.js";
28
28
  const ALLOWED_METHODS = new Set(['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE']);
29
29
  /** Reject traversal and empty segments; allow plugin file-path characters. */
30
30
  function isSafeSegment(segment) {
@@ -1 +1 @@
1
- {"version":3,"file":"remote-methods.d.ts","sourceRoot":"","sources":["../../src/remote-methods.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,gFAAgF;AAChF,eAAO,MAAM,aAAa,YAAY,CAAA;AAEtC,+DAA+D;AAC/D,eAAO,MAAM,iBAAiB,gBAAyB,CAAA;AAEvD,qFAAqF;AACrF,eAAO,MAAM,gBAAgB;aAC3B,GAAG;aACH,IAAI;CACI,CAAA;AAEV;;;GAGG;AACH,eAAO,MAAM,oBAAoB,qKAMvB,CAAA;AAEV,wEAAwE;AACxE,eAAO,MAAM,mBAAmB,wBAAwB,CAAA;AAExD,6FAA6F;AAC7F,eAAO,MAAM,qBAAqB,8BAA8B,CAAA;AAEhE,iFAAiF;AACjF,eAAO,MAAM,2BAA2B,6BAA6B,CAAA;AAErE;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,aAgBhC,CAAA"}
1
+ {"version":3,"file":"remote-methods.d.ts","sourceRoot":"","sources":["../../src/remote-methods.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,gFAAgF;AAChF,eAAO,MAAM,aAAa,YAAY,CAAA;AAEtC,+DAA+D;AAC/D,eAAO,MAAM,iBAAiB,gBAAyB,CAAA;AAEvD,qFAAqF;AACrF,eAAO,MAAM,gBAAgB;;;CAGnB,CAAA;AAEV;;;GAGG;AACH,eAAO,MAAM,oBAAoB,qKAMvB,CAAA;AAEV,wEAAwE;AACxE,eAAO,MAAM,mBAAmB,wBAAwB,CAAA;AAExD,6FAA6F;AAC7F,eAAO,MAAM,qBAAqB,8BAA8B,CAAA;AAEhE,iFAAiF;AACjF,eAAO,MAAM,2BAA2B,6BAA6B,CAAA;AAErE;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,aAgBhC,CAAA"}