unbrowse 3.1.0 → 3.2.0

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 (107) hide show
  1. package/dist/cli.js +455 -96
  2. package/dist/index.js +2 -6
  3. package/dist/mcp.js +695 -46
  4. package/dist/server.js +25811 -0
  5. package/package.json +1 -2
  6. package/vendor/kuri/darwin-arm64/kuri +0 -0
  7. package/vendor/kuri/darwin-x64/kuri +0 -0
  8. package/vendor/kuri/linux-arm64/kuri +0 -0
  9. package/vendor/kuri/linux-x64/kuri +0 -0
  10. package/vendor/kuri/manifest.json +7 -10
  11. package/runtime-src/agent-outcome.ts +0 -166
  12. package/runtime-src/analytics-session.ts +0 -55
  13. package/runtime-src/api/browse-index.ts +0 -317
  14. package/runtime-src/api/browse-session.ts +0 -572
  15. package/runtime-src/api/browse-submit-prereqs.ts +0 -48
  16. package/runtime-src/api/browse-submit.ts +0 -1184
  17. package/runtime-src/api/routes.ts +0 -1823
  18. package/runtime-src/auth/browser-cookies.ts +0 -423
  19. package/runtime-src/auth/index.ts +0 -535
  20. package/runtime-src/auth/runtime.ts +0 -116
  21. package/runtime-src/browser/index.ts +0 -659
  22. package/runtime-src/browser/types.ts +0 -41
  23. package/runtime-src/build-info.generated.ts +0 -6
  24. package/runtime-src/capture/index.ts +0 -1794
  25. package/runtime-src/capture/prefetch.ts +0 -95
  26. package/runtime-src/capture/rsc.ts +0 -45
  27. package/runtime-src/cli/shortcuts.ts +0 -273
  28. package/runtime-src/cli.ts +0 -1572
  29. package/runtime-src/client/graph-client.ts +0 -100
  30. package/runtime-src/client/index.ts +0 -1425
  31. package/runtime-src/debug-trace.ts +0 -18
  32. package/runtime-src/domain.ts +0 -38
  33. package/runtime-src/execution/index.ts +0 -3397
  34. package/runtime-src/execution/retry.ts +0 -46
  35. package/runtime-src/execution/robots.ts +0 -167
  36. package/runtime-src/execution/search-forms.ts +0 -188
  37. package/runtime-src/extraction/index.ts +0 -1507
  38. package/runtime-src/foundry/publish-bundle.ts +0 -392
  39. package/runtime-src/graph/agent-augment.ts +0 -315
  40. package/runtime-src/graph/index.ts +0 -1524
  41. package/runtime-src/graph/local-fixtures.ts +0 -393
  42. package/runtime-src/graph/local-harness.ts +0 -646
  43. package/runtime-src/graph/planner.ts +0 -411
  44. package/runtime-src/graph/session.ts +0 -294
  45. package/runtime-src/graph/trace-store.ts +0 -136
  46. package/runtime-src/index.ts +0 -24
  47. package/runtime-src/indexer/index.ts +0 -465
  48. package/runtime-src/intent-match.ts +0 -1515
  49. package/runtime-src/kuri/client.ts +0 -1839
  50. package/runtime-src/logger.ts +0 -30
  51. package/runtime-src/marketplace/index.ts +0 -103
  52. package/runtime-src/mcp.ts +0 -1747
  53. package/runtime-src/orchestrator/browser-agent.ts +0 -374
  54. package/runtime-src/orchestrator/dag-advisor.ts +0 -59
  55. package/runtime-src/orchestrator/dag-feedback.ts +0 -257
  56. package/runtime-src/orchestrator/first-pass-action.ts +0 -403
  57. package/runtime-src/orchestrator/index.ts +0 -4480
  58. package/runtime-src/orchestrator/passive-publish.ts +0 -187
  59. package/runtime-src/orchestrator/timing-economics.ts +0 -80
  60. package/runtime-src/payments/cascade.ts +0 -137
  61. package/runtime-src/payments/index.ts +0 -270
  62. package/runtime-src/payments/lobster-pay.ts +0 -182
  63. package/runtime-src/payments/wallet.ts +0 -98
  64. package/runtime-src/publish/review-context.ts +0 -93
  65. package/runtime-src/publish/sanitize.ts +0 -197
  66. package/runtime-src/publish/schema-review.ts +0 -192
  67. package/runtime-src/publish-admission.ts +0 -388
  68. package/runtime-src/ratelimit/index.ts +0 -23
  69. package/runtime-src/reverse-engineer/bundle-scanner.ts +0 -127
  70. package/runtime-src/reverse-engineer/description-prompt.ts +0 -213
  71. package/runtime-src/reverse-engineer/index.ts +0 -1551
  72. package/runtime-src/router.ts +0 -17
  73. package/runtime-src/routing-telemetry.ts +0 -395
  74. package/runtime-src/runtime/browser-access.ts +0 -11
  75. package/runtime-src/runtime/browser-auth.ts +0 -12
  76. package/runtime-src/runtime/browser-host.ts +0 -48
  77. package/runtime-src/runtime/lifecycle.ts +0 -17
  78. package/runtime-src/runtime/local-server.ts +0 -311
  79. package/runtime-src/runtime/paths.ts +0 -99
  80. package/runtime-src/runtime/setup.ts +0 -251
  81. package/runtime-src/runtime/supervisor.ts +0 -69
  82. package/runtime-src/runtime/update-hints.ts +0 -351
  83. package/runtime-src/server.ts +0 -100
  84. package/runtime-src/session-logs.ts +0 -142
  85. package/runtime-src/settings.ts +0 -221
  86. package/runtime-src/single-binary.ts +0 -143
  87. package/runtime-src/site-policy.ts +0 -54
  88. package/runtime-src/stale-cleanup-runner.ts +0 -144
  89. package/runtime-src/stale-cleanup.ts +0 -133
  90. package/runtime-src/telemetry-attribution.ts +0 -120
  91. package/runtime-src/telemetry.ts +0 -253
  92. package/runtime-src/template-params.ts +0 -141
  93. package/runtime-src/transform/drift.ts +0 -60
  94. package/runtime-src/transform/index.ts +0 -277
  95. package/runtime-src/types/index.ts +0 -1
  96. package/runtime-src/types/skill.ts +0 -912
  97. package/runtime-src/vault/index.ts +0 -196
  98. package/runtime-src/verification/auth-gate.ts +0 -8
  99. package/runtime-src/verification/candidates.ts +0 -27
  100. package/runtime-src/verification/index.ts +0 -120
  101. package/runtime-src/verification/matrix.ts +0 -30
  102. package/runtime-src/version.ts +0 -148
  103. package/runtime-src/workflow/artifact.ts +0 -161
  104. package/runtime-src/workflow/compile.ts +0 -808
  105. package/runtime-src/workflow/publish.ts +0 -225
  106. package/runtime-src/workflow/runtime.ts +0 -213
  107. package/vendor/kuri/win-x64/kuri.exe +0 -0
@@ -1,572 +0,0 @@
1
- import { randomUUID } from "node:crypto";
2
- import { getKuriErrorMessage } from "../kuri/client.js";
3
-
4
- export interface BrowseSession {
5
- sessionId: string;
6
- tabId: string;
7
- url: string;
8
- harActive: boolean;
9
- domain: string;
10
- brokerPort?: number;
11
- client?: BrowseSessionClient;
12
- }
13
-
14
- export interface BrowseTabRef {
15
- id: string;
16
- url?: string;
17
- }
18
-
19
- export interface BrowseSessionClient {
20
- start(): Promise<void>;
21
- newTab(): Promise<string>;
22
- harStart(tabId: string): Promise<void>;
23
- closeTab(tabId: string): Promise<void>;
24
- discoverTabs(): Promise<BrowseTabRef[]>;
25
- getCurrentUrl(tabId: string): Promise<string>;
26
- getPort?(): number;
27
- }
28
-
29
- export type BrowseSessionErrorCode =
30
- | "no_active_session"
31
- | "session_id_required"
32
- | "session_not_found"
33
- | "session_expired";
34
-
35
- export class BrowseSessionError extends Error {
36
- code: BrowseSessionErrorCode;
37
- statusCode: number;
38
-
39
- constructor(code: BrowseSessionErrorCode, message?: string) {
40
- super(message ?? code);
41
- this.name = "BrowseSessionError";
42
- this.code = code;
43
- this.statusCode = browseSessionErrorStatus(code);
44
- }
45
- }
46
-
47
- const RECOVERABLE_BROWSE_FAILURES = [
48
- "cdp command failed",
49
- "transport closed",
50
- "target closed",
51
- "tab not found",
52
- "session closed",
53
- "connection refused",
54
- "unable to connect",
55
- "operation was aborted",
56
- "aborterror",
57
- "execution context was destroyed",
58
- "cannot find context with specified id",
59
- "no such target",
60
- "socket connection was closed unexpectedly",
61
- "econnreset",
62
- ];
63
-
64
- const LIVE_CHECK_RETRIES = 8;
65
- const LIVE_CHECK_RETRY_DELAY_MS = 250;
66
-
67
- const sessionQueues = new Map<string, Promise<void>>();
68
-
69
- function browseSessionErrorStatus(code: BrowseSessionErrorCode): number {
70
- switch (code) {
71
- case "session_not_found":
72
- return 404;
73
- case "no_active_session":
74
- return 404;
75
- case "session_id_required":
76
- return 409;
77
- case "session_expired":
78
- return 409;
79
- }
80
- }
81
-
82
- export function extractBrowseFailureMessage(value: unknown): string | null {
83
- return typeof value === "string" ? value : getKuriErrorMessage(value);
84
- }
85
-
86
- export function isRecoverableBrowseFailure(value: unknown): boolean {
87
- const message = extractBrowseFailureMessage(value);
88
- if (!message) return false;
89
- const normalized = message.toLowerCase();
90
- return RECOVERABLE_BROWSE_FAILURES.some((needle) => normalized.includes(needle));
91
- }
92
-
93
- function normalizeSessionId(sessionId?: string): string {
94
- return sessionId?.trim() || randomUUID();
95
- }
96
-
97
- function extractDomain(url: string | undefined): string {
98
- if (!url) return "";
99
- try {
100
- return new URL(url).hostname.replace(/^www\./, "");
101
- } catch {
102
- return "";
103
- }
104
- }
105
-
106
- function normalizeBrowseUrl(url: string | undefined): URL | null {
107
- if (!url) return null;
108
- try {
109
- return new URL(url);
110
- } catch {
111
- return null;
112
- }
113
- }
114
-
115
- function normalizeBrowsePathname(pathname: string): string {
116
- if (!pathname) return "/";
117
- return pathname.endsWith("/") && pathname !== "/" ? pathname.slice(0, -1) : pathname;
118
- }
119
-
120
- function matchesPreferredBrowseTab(tabUrl: string | undefined, preferredUrl: string | undefined): boolean {
121
- const candidate = normalizeBrowseUrl(tabUrl);
122
- const preferred = normalizeBrowseUrl(preferredUrl);
123
- if (!candidate || !preferred) return false;
124
- if (candidate.hostname.replace(/^www\./, "") !== preferred.hostname.replace(/^www\./, "")) return false;
125
- return normalizeBrowsePathname(candidate.pathname) === normalizeBrowsePathname(preferred.pathname);
126
- }
127
-
128
- function matchesPreferredBrowseDomain(tabUrl: string | undefined, preferredUrl: string | undefined): boolean {
129
- const candidate = normalizeBrowseUrl(tabUrl);
130
- const preferred = normalizeBrowseUrl(preferredUrl);
131
- if (!candidate || !preferred) return false;
132
- return candidate.hostname.replace(/^www\./, "") === preferred.hostname.replace(/^www\./, "");
133
- }
134
-
135
- function isPlaceholderBrowseUrl(url: string | undefined): boolean {
136
- if (!url) return true;
137
- const normalized = url.trim().toLowerCase();
138
- return normalized === "about:blank"
139
- || normalized.startsWith("chrome://newtab")
140
- || normalized.startsWith("chrome://new-tab-page")
141
- || normalized.startsWith("edge://newtab");
142
- }
143
-
144
- function hasMeaningfulBrowseUrl(url: string | undefined): boolean {
145
- return hasKnownBrowseUrl(url) && !isPlaceholderBrowseUrl(url);
146
- }
147
-
148
- function cleanupSessionQueue(sessionId: string): void {
149
- sessionQueues.delete(sessionId);
150
- }
151
-
152
- function sleep(ms: number): Promise<void> {
153
- return new Promise((resolve) => setTimeout(resolve, ms));
154
- }
155
-
156
- function hasKnownBrowseUrl(value: string | undefined): boolean {
157
- return typeof value === "string" && value.length > 0;
158
- }
159
-
160
- function resolveSessionClient(session: BrowseSession | undefined, fallback: BrowseSessionClient): BrowseSessionClient {
161
- const fallbackPort = fallback.getPort?.();
162
- if (session?.brokerPort !== undefined && fallbackPort === session.brokerPort) return fallback;
163
- return session?.client ?? fallback;
164
- }
165
-
166
- export function createRegisteredBrowseSession(
167
- sessions: Map<string, BrowseSession>,
168
- input: {
169
- sessionId?: string;
170
- tabId: string;
171
- url: string;
172
- domain: string;
173
- harActive?: boolean;
174
- brokerPort?: number;
175
- client?: BrowseSessionClient;
176
- },
177
- ): BrowseSession {
178
- const existing = [...sessions.values()].find((session) => session.tabId === input.tabId);
179
- if (existing) {
180
- existing.url = input.url;
181
- existing.domain = input.domain;
182
- existing.harActive = input.harActive ?? existing.harActive;
183
- existing.brokerPort = input.brokerPort ?? existing.brokerPort;
184
- existing.client = input.client ?? existing.client;
185
- return existing;
186
- }
187
-
188
- const sessionId = normalizeSessionId(input.sessionId);
189
- const session: BrowseSession = {
190
- sessionId,
191
- tabId: input.tabId,
192
- url: input.url,
193
- domain: input.domain,
194
- harActive: input.harActive ?? true,
195
- brokerPort: input.brokerPort,
196
- client: input.client,
197
- };
198
- sessions.set(sessionId, session);
199
- return session;
200
- }
201
-
202
- export function removeBrowseSession(
203
- sessions: Map<string, BrowseSession>,
204
- sessionId: string,
205
- ): BrowseSession | undefined {
206
- const existing = sessions.get(sessionId);
207
- sessions.delete(sessionId);
208
- cleanupSessionQueue(sessionId);
209
- return existing;
210
- }
211
-
212
- function ownedTabIds(
213
- sessions: Map<string, BrowseSession>,
214
- excludeSessionId?: string,
215
- ): Set<string> {
216
- return new Set(
217
- [...sessions.values()]
218
- .filter((session) => session.sessionId !== excludeSessionId)
219
- .map((session) => session.tabId),
220
- );
221
- }
222
-
223
- async function createBrowseSession(
224
- sessions: Map<string, BrowseSession>,
225
- client: BrowseSessionClient,
226
- injectInterceptor: (tabId: string) => Promise<unknown>,
227
- sessionId?: string,
228
- ): Promise<BrowseSession> {
229
- await client.start();
230
- const tabId = await client.newTab();
231
- if (!tabId) throw new Error("Failed to create browser tab");
232
- await client.harStart(tabId).catch(() => {});
233
- await injectInterceptor(tabId);
234
- return createRegisteredBrowseSession(sessions, {
235
- sessionId,
236
- tabId,
237
- url: "about:blank",
238
- domain: "",
239
- harActive: true,
240
- brokerPort: client.getPort?.(),
241
- client,
242
- });
243
- }
244
-
245
- async function adoptExistingBrowseTab(
246
- sessions: Map<string, BrowseSession>,
247
- client: BrowseSessionClient,
248
- injectInterceptor: (tabId: string) => Promise<unknown>,
249
- preferredUrl?: string,
250
- sessionId?: string,
251
- ): Promise<BrowseSession | null> {
252
- try {
253
- await client.start();
254
- const tabs = await client.discoverTabs();
255
- if (!preferredUrl) return null;
256
- const reservedTabs = ownedTabIds(sessions, sessionId);
257
- const candidate = tabs.find((tab) => {
258
- if (!tab.id || reservedTabs.has(tab.id)) return false;
259
- return matchesPreferredBrowseTab(tab.url, preferredUrl);
260
- });
261
-
262
- if (!candidate?.id) return null;
263
- await client.harStart(candidate.id).catch(() => {});
264
- await injectInterceptor(candidate.id);
265
- return createRegisteredBrowseSession(sessions, {
266
- sessionId,
267
- tabId: candidate.id,
268
- url: candidate.url ?? "about:blank",
269
- domain: extractDomain(candidate.url),
270
- harActive: true,
271
- brokerPort: client.getPort?.(),
272
- client,
273
- });
274
- } catch {
275
- return null;
276
- }
277
- }
278
-
279
- async function dropBrowseSession(
280
- sessions: Map<string, BrowseSession>,
281
- client: BrowseSessionClient,
282
- session: BrowseSession | undefined,
283
- ): Promise<void> {
284
- if (!session) return;
285
- await resolveSessionClient(session, client).closeTab(session.tabId).catch(() => {});
286
- removeBrowseSession(sessions, session.sessionId);
287
- }
288
-
289
- export async function isBrowseSessionLive(
290
- session: BrowseSession,
291
- client: BrowseSessionClient,
292
- ): Promise<boolean> {
293
- if (!session.tabId) return false;
294
- const sessionClient = resolveSessionClient(session, client);
295
-
296
- try {
297
- await sessionClient.start();
298
- } catch (error) {
299
- if (isRecoverableBrowseFailure(error)) return false;
300
- throw error;
301
- }
302
-
303
- for (let attempt = 0; attempt < LIVE_CHECK_RETRIES; attempt += 1) {
304
- try {
305
- const tabs = await sessionClient.discoverTabs();
306
- const exactTab = tabs.find((tab) => tab.id === session.tabId);
307
- if (!exactTab) {
308
- if (attempt < LIVE_CHECK_RETRIES - 1) {
309
- await sleep(LIVE_CHECK_RETRY_DELAY_MS);
310
- continue;
311
- }
312
- return false;
313
- }
314
- session.brokerPort = sessionClient.getPort?.() ?? session.brokerPort;
315
- session.client = sessionClient;
316
- if (hasMeaningfulBrowseUrl(exactTab.url)) {
317
- session.url = exactTab.url!;
318
- session.domain = extractDomain(session.url);
319
- }
320
-
321
- try {
322
- const currentUrl = await sessionClient.getCurrentUrl(session.tabId);
323
- if (hasMeaningfulBrowseUrl(currentUrl)) {
324
- session.url = currentUrl;
325
- session.domain = extractDomain(currentUrl);
326
- session.brokerPort = sessionClient.getPort?.() ?? session.brokerPort;
327
- session.client = sessionClient;
328
- return true;
329
- }
330
- if (
331
- isPlaceholderBrowseUrl(currentUrl)
332
- && isPlaceholderBrowseUrl(exactTab.url)
333
- && isPlaceholderBrowseUrl(session.url)
334
- ) {
335
- session.url = currentUrl || exactTab.url || session.url;
336
- session.domain = extractDomain(session.url);
337
- session.brokerPort = sessionClient.getPort?.() ?? session.brokerPort;
338
- session.client = sessionClient;
339
- return true;
340
- }
341
- } catch (error) {
342
- if (!isRecoverableBrowseFailure(error)) return false;
343
- }
344
-
345
- if (hasMeaningfulBrowseUrl(session.url) || hasMeaningfulBrowseUrl(exactTab.url)) {
346
- if (hasMeaningfulBrowseUrl(exactTab.url)) {
347
- session.url = exactTab.url!;
348
- session.domain = extractDomain(session.url);
349
- }
350
- return true;
351
- }
352
- } catch (error) {
353
- if (!isRecoverableBrowseFailure(error)) return false;
354
- }
355
-
356
- if (attempt < LIVE_CHECK_RETRIES - 1) await sleep(LIVE_CHECK_RETRY_DELAY_MS);
357
- }
358
-
359
- return false;
360
- }
361
-
362
- async function listLiveBrowseSessions(
363
- sessions: Map<string, BrowseSession>,
364
- client: BrowseSessionClient,
365
- ): Promise<BrowseSession[]> {
366
- const live: BrowseSession[] = [];
367
- const stale: string[] = [];
368
-
369
- for (const session of sessions.values()) {
370
- if (await isBrowseSessionLive(session, client)) {
371
- live.push(session);
372
- } else {
373
- stale.push(session.sessionId);
374
- }
375
- }
376
-
377
- for (const sessionId of stale) removeBrowseSession(sessions, sessionId);
378
- return live;
379
- }
380
-
381
- export async function resolveRequestedBrowseSession(
382
- sessions: Map<string, BrowseSession>,
383
- client: BrowseSessionClient,
384
- requestedSessionId?: string,
385
- ): Promise<BrowseSession> {
386
- if (requestedSessionId) {
387
- const session = sessions.get(requestedSessionId);
388
- if (!session) throw new BrowseSessionError("session_not_found");
389
- return session;
390
- }
391
-
392
- const live = await listLiveBrowseSessions(sessions, client);
393
- if (live.length === 0) throw new BrowseSessionError("no_active_session");
394
- if (live.length > 1) throw new BrowseSessionError("session_id_required");
395
- return live[0];
396
- }
397
-
398
- export async function getOrCreateBrowseSession(
399
- sessions: Map<string, BrowseSession>,
400
- client: BrowseSessionClient,
401
- injectInterceptor: (tabId: string) => Promise<unknown>,
402
- ): Promise<BrowseSession> {
403
- await client.start();
404
- const existingSessions = [...sessions.values()];
405
- if (existingSessions.length === 1) {
406
- const existing = existingSessions[0];
407
- if (await isBrowseSessionLive(existing, client)) return existing;
408
- const preferredUrl = existing.url;
409
- const targetSessionId = existing.sessionId;
410
- const existingClient = resolveSessionClient(existing, client);
411
- await dropBrowseSession(sessions, existingClient, existing);
412
- const adopted = await adoptExistingBrowseTab(sessions, existingClient, injectInterceptor, preferredUrl, targetSessionId);
413
- if (adopted) return adopted;
414
- return createBrowseSession(sessions, existingClient, injectInterceptor, targetSessionId);
415
- }
416
-
417
- const live = await listLiveBrowseSessions(sessions, client);
418
- if (live.length === 1) return live[0];
419
- if (live.length > 1) throw new BrowseSessionError("session_id_required");
420
-
421
- const existing = existingSessions[0];
422
- const targetSessionId = existing?.sessionId;
423
- const preferredUrl = existing?.url;
424
- if (existing) await dropBrowseSession(sessions, client, existing);
425
- const adopted = await adoptExistingBrowseTab(sessions, client, injectInterceptor, preferredUrl, targetSessionId);
426
- if (adopted) return adopted;
427
- return createBrowseSession(sessions, client, injectInterceptor, targetSessionId);
428
- }
429
-
430
- export async function resetBrowseSession(
431
- sessions: Map<string, BrowseSession>,
432
- client: BrowseSessionClient,
433
- injectInterceptor: (tabId: string) => Promise<unknown>,
434
- sessionId?: string,
435
- ): Promise<BrowseSession> {
436
- await client.start();
437
- const existing = sessionId
438
- ? sessions.get(sessionId)
439
- : [...sessions.values()][0];
440
- const targetSessionId = sessionId ?? existing?.sessionId;
441
- const preferredUrl = existing?.url;
442
- const existingClient = resolveSessionClient(existing, client);
443
- await dropBrowseSession(sessions, existingClient, existing);
444
- const adopted = await adoptExistingBrowseTab(sessions, existingClient, injectInterceptor, preferredUrl, targetSessionId);
445
- if (adopted) return adopted;
446
- return createBrowseSession(sessions, existingClient, injectInterceptor, targetSessionId);
447
- }
448
-
449
- async function withSessionQueue<T>(sessionId: string, fn: () => Promise<T>): Promise<T> {
450
- const prev = sessionQueues.get(sessionId) ?? Promise.resolve();
451
- let release!: () => void;
452
- const waitTurn = new Promise<void>((resolve) => {
453
- release = resolve;
454
- });
455
- const gate = prev.catch(() => {}).then(() => waitTurn);
456
- sessionQueues.set(sessionId, gate);
457
- await prev.catch(() => {});
458
- try {
459
- return await fn();
460
- } finally {
461
- release();
462
- if (sessionQueues.get(sessionId) === gate) cleanupSessionQueue(sessionId);
463
- }
464
- }
465
-
466
- export async function withRecoveredBrowseSession<T>(
467
- sessions: Map<string, BrowseSession>,
468
- client: BrowseSessionClient,
469
- injectInterceptor: (tabId: string) => Promise<unknown>,
470
- run: (session: BrowseSession) => Promise<T>,
471
- shouldReset?: (result: T) => boolean,
472
- ): Promise<{ session: BrowseSession; result: T; recovered: boolean }> {
473
- let session = await getOrCreateBrowseSession(sessions, client, injectInterceptor);
474
-
475
- try {
476
- const result = await run(session);
477
- if (!shouldReset || !shouldReset(result)) {
478
- return { session, result, recovered: false };
479
- }
480
- } catch (error) {
481
- if (!isRecoverableBrowseFailure(error)) throw error;
482
- }
483
-
484
- session = await resetBrowseSession(sessions, client, injectInterceptor, session.sessionId);
485
- const result = await run(session);
486
- return { session, result, recovered: true };
487
- }
488
-
489
- export async function withSerializedRecoveredBrowseSession<T>(
490
- sessions: Map<string, BrowseSession>,
491
- client: BrowseSessionClient,
492
- injectInterceptor: (tabId: string) => Promise<unknown>,
493
- requestedSessionId: string | undefined,
494
- run: (session: BrowseSession) => Promise<T>,
495
- shouldReset?: (result: T) => boolean,
496
- ): Promise<{ session: BrowseSession; result: T; recovered: boolean }> {
497
- const resolved = await resolveRequestedBrowseSession(sessions, client, requestedSessionId);
498
- return withSessionQueue(resolved.sessionId, async () => {
499
- let session = sessions.get(resolved.sessionId);
500
- if (!session) throw new BrowseSessionError("session_expired");
501
- const sessionClient = resolveSessionClient(session, client);
502
-
503
- try {
504
- const result = await run(session);
505
- if (!shouldReset || !shouldReset(result)) {
506
- return { session, result, recovered: false };
507
- }
508
- } catch (error) {
509
- if (!isRecoverableBrowseFailure(error)) throw error;
510
- }
511
-
512
- session = await resetBrowseSession(sessions, sessionClient, injectInterceptor, resolved.sessionId);
513
- const result = await run(session);
514
- return { session, result, recovered: true };
515
- });
516
- }
517
-
518
- export async function withSerializedStrictBrowseSession<T>(
519
- sessions: Map<string, BrowseSession>,
520
- client: BrowseSessionClient,
521
- requestedSessionId: string | undefined,
522
- run: (session: BrowseSession) => Promise<T>,
523
- shouldExpire?: (result: T) => boolean,
524
- ): Promise<{ session: BrowseSession; result: T; recovered: false }> {
525
- const resolved = await resolveRequestedBrowseSession(sessions, client, requestedSessionId);
526
- return withSessionQueue(resolved.sessionId, async () => {
527
- const session = sessions.get(resolved.sessionId);
528
- if (!session) throw new BrowseSessionError("session_expired");
529
-
530
- const live = await isBrowseSessionLive(session, client);
531
- if (!live) {
532
- removeBrowseSession(sessions, resolved.sessionId);
533
- throw new BrowseSessionError("session_expired");
534
- }
535
-
536
- try {
537
- const result = await run(session);
538
- if (shouldExpire?.(result)) {
539
- const stillLive = await isBrowseSessionLive(session, client);
540
- if (!stillLive) {
541
- removeBrowseSession(sessions, resolved.sessionId);
542
- throw new BrowseSessionError("session_expired");
543
- }
544
- }
545
- return { session, result, recovered: false };
546
- } catch (error) {
547
- if (error instanceof BrowseSessionError) throw error;
548
- if (isRecoverableBrowseFailure(error)) {
549
- const stillLive = await isBrowseSessionLive(session, client);
550
- if (!stillLive) {
551
- removeBrowseSession(sessions, resolved.sessionId);
552
- throw new BrowseSessionError("session_expired");
553
- }
554
- }
555
- throw error;
556
- }
557
- });
558
- }
559
-
560
- export async function getOrCreateNavigateBrowseSession(
561
- sessions: Map<string, BrowseSession>,
562
- client: BrowseSessionClient,
563
- injectInterceptor: (tabId: string) => Promise<unknown>,
564
- requestedSessionId?: string,
565
- ): Promise<BrowseSession> {
566
- if (requestedSessionId) {
567
- const session = sessions.get(requestedSessionId);
568
- if (!session) throw new BrowseSessionError("session_not_found");
569
- return session;
570
- }
571
- return createBrowseSession(sessions, client, injectInterceptor);
572
- }
@@ -1,48 +0,0 @@
1
- export const MONTH_INDEX: Record<string, string> = {
2
- jan: "01",
3
- january: "01",
4
- feb: "02",
5
- february: "02",
6
- mar: "03",
7
- march: "03",
8
- apr: "04",
9
- april: "04",
10
- may: "05",
11
- jun: "06",
12
- june: "06",
13
- jul: "07",
14
- july: "07",
15
- aug: "08",
16
- august: "08",
17
- sep: "09",
18
- sept: "09",
19
- september: "09",
20
- oct: "10",
21
- october: "10",
22
- nov: "11",
23
- november: "11",
24
- dec: "12",
25
- december: "12",
26
- };
27
-
28
- export function inferCalendarIsoDate(monthLabel: string, dayLabel: string): string | null {
29
- const day = Number.parseInt(dayLabel.trim(), 10);
30
- if (!Number.isFinite(day) || day < 1 || day > 31) return null;
31
-
32
- const year = monthLabel.match(/\b(\d{4})\b/)?.[1];
33
- if (!year) return null;
34
-
35
- const tokens = monthLabel
36
- .toLowerCase()
37
- .replace(/[^a-z0-9\s]+/g, " ")
38
- .split(/\s+/)
39
- .map((token) => token.trim())
40
- .filter(Boolean);
41
-
42
- const month = tokens
43
- .map((token) => MONTH_INDEX[token] ?? MONTH_INDEX[token.slice(0, 3)])
44
- .find(Boolean);
45
- if (!month) return null;
46
-
47
- return `${year}-${month}-${String(day).padStart(2, "0")}`;
48
- }