unbrowse 3.1.0-experiments.995f8bb → 3.1.0-experiments.9cbcb13

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 (105) hide show
  1. package/dist/cli.js +79 -33
  2. package/dist/index.js +2 -6
  3. package/dist/mcp.js +73 -22
  4. package/dist/server.js +25994 -0
  5. package/package.json +1 -2
  6. package/vendor/kuri/manifest.json +1 -1
  7. package/runtime-src/agent-outcome.ts +0 -166
  8. package/runtime-src/analytics-session.ts +0 -55
  9. package/runtime-src/api/browse-index.ts +0 -343
  10. package/runtime-src/api/browse-session.ts +0 -572
  11. package/runtime-src/api/browse-submit-prereqs.ts +0 -48
  12. package/runtime-src/api/browse-submit.ts +0 -1184
  13. package/runtime-src/api/routes.ts +0 -1833
  14. package/runtime-src/auth/browser-cookies.ts +0 -423
  15. package/runtime-src/auth/index.ts +0 -535
  16. package/runtime-src/auth/runtime.ts +0 -116
  17. package/runtime-src/browser/index.ts +0 -659
  18. package/runtime-src/browser/types.ts +0 -41
  19. package/runtime-src/build-info.generated.ts +0 -6
  20. package/runtime-src/capture/index.ts +0 -1929
  21. package/runtime-src/capture/prefetch.ts +0 -95
  22. package/runtime-src/capture/rsc.ts +0 -45
  23. package/runtime-src/cli/shortcuts.ts +0 -273
  24. package/runtime-src/cli.ts +0 -1734
  25. package/runtime-src/client/graph-client.ts +0 -100
  26. package/runtime-src/client/index.ts +0 -1425
  27. package/runtime-src/debug-trace.ts +0 -18
  28. package/runtime-src/domain.ts +0 -38
  29. package/runtime-src/execution/index.ts +0 -3407
  30. package/runtime-src/execution/retry.ts +0 -46
  31. package/runtime-src/execution/robots.ts +0 -167
  32. package/runtime-src/execution/search-forms.ts +0 -188
  33. package/runtime-src/execution/token-resolver.ts +0 -135
  34. package/runtime-src/extraction/index.ts +0 -1507
  35. package/runtime-src/foundry/publish-bundle.ts +0 -392
  36. package/runtime-src/graph/agent-augment.ts +0 -315
  37. package/runtime-src/graph/index.ts +0 -1532
  38. package/runtime-src/graph/local-fixtures.ts +0 -393
  39. package/runtime-src/graph/local-harness.ts +0 -646
  40. package/runtime-src/graph/planner.ts +0 -411
  41. package/runtime-src/graph/session.ts +0 -294
  42. package/runtime-src/graph/trace-store.ts +0 -136
  43. package/runtime-src/impact-log.ts +0 -227
  44. package/runtime-src/index.ts +0 -24
  45. package/runtime-src/indexer/index.ts +0 -465
  46. package/runtime-src/intent-match.ts +0 -1515
  47. package/runtime-src/kuri/client.ts +0 -1839
  48. package/runtime-src/logger.ts +0 -30
  49. package/runtime-src/marketplace/index.ts +0 -111
  50. package/runtime-src/mcp.ts +0 -1911
  51. package/runtime-src/orchestrator/browser-agent.ts +0 -374
  52. package/runtime-src/orchestrator/dag-advisor.ts +0 -59
  53. package/runtime-src/orchestrator/dag-feedback.ts +0 -257
  54. package/runtime-src/orchestrator/first-pass-action.ts +0 -403
  55. package/runtime-src/orchestrator/index.ts +0 -4480
  56. package/runtime-src/orchestrator/passive-publish.ts +0 -187
  57. package/runtime-src/orchestrator/timing-economics.ts +0 -80
  58. package/runtime-src/payments/cascade.ts +0 -137
  59. package/runtime-src/payments/index.ts +0 -270
  60. package/runtime-src/payments/lobster-pay.ts +0 -182
  61. package/runtime-src/payments/wallet.ts +0 -98
  62. package/runtime-src/publish/review-context.ts +0 -93
  63. package/runtime-src/publish/sanitize.ts +0 -197
  64. package/runtime-src/publish/schema-review.ts +0 -192
  65. package/runtime-src/publish-admission.ts +0 -388
  66. package/runtime-src/ratelimit/index.ts +0 -23
  67. package/runtime-src/reverse-engineer/bundle-scanner.ts +0 -127
  68. package/runtime-src/reverse-engineer/description-prompt.ts +0 -213
  69. package/runtime-src/reverse-engineer/index.ts +0 -1551
  70. package/runtime-src/reverse-engineer/token-sources.ts +0 -379
  71. package/runtime-src/router.ts +0 -17
  72. package/runtime-src/routing-telemetry.ts +0 -395
  73. package/runtime-src/runtime/browser-access.ts +0 -11
  74. package/runtime-src/runtime/browser-auth.ts +0 -12
  75. package/runtime-src/runtime/browser-host.ts +0 -48
  76. package/runtime-src/runtime/lifecycle.ts +0 -17
  77. package/runtime-src/runtime/local-server.ts +0 -311
  78. package/runtime-src/runtime/paths.ts +0 -99
  79. package/runtime-src/runtime/setup.ts +0 -251
  80. package/runtime-src/runtime/supervisor.ts +0 -69
  81. package/runtime-src/runtime/update-hints.ts +0 -351
  82. package/runtime-src/server.ts +0 -100
  83. package/runtime-src/session-logs.ts +0 -142
  84. package/runtime-src/settings.ts +0 -221
  85. package/runtime-src/single-binary.ts +0 -143
  86. package/runtime-src/site-policy.ts +0 -54
  87. package/runtime-src/stale-cleanup-runner.ts +0 -144
  88. package/runtime-src/stale-cleanup.ts +0 -133
  89. package/runtime-src/telemetry-attribution.ts +0 -120
  90. package/runtime-src/telemetry.ts +0 -253
  91. package/runtime-src/template-params.ts +0 -141
  92. package/runtime-src/transform/drift.ts +0 -60
  93. package/runtime-src/transform/index.ts +0 -277
  94. package/runtime-src/types/index.ts +0 -1
  95. package/runtime-src/types/skill.ts +0 -931
  96. package/runtime-src/vault/index.ts +0 -196
  97. package/runtime-src/verification/auth-gate.ts +0 -8
  98. package/runtime-src/verification/candidates.ts +0 -27
  99. package/runtime-src/verification/index.ts +0 -120
  100. package/runtime-src/verification/matrix.ts +0 -30
  101. package/runtime-src/version.ts +0 -148
  102. package/runtime-src/workflow/artifact.ts +0 -161
  103. package/runtime-src/workflow/compile.ts +0 -808
  104. package/runtime-src/workflow/publish.ts +0 -225
  105. package/runtime-src/workflow/runtime.ts +0 -213
@@ -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
- }