agentic-browser 2.5.0 → 2.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { r as createCliRuntime } from "../runtime-C3Y-UgC1.mjs";
2
+ import { r as createCliRuntime } from "../runtime-BeD8ZcjH.mjs";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import crypto from "node:crypto";
@@ -242,7 +242,7 @@ async function runCommand(runtime, input) {
242
242
  payload: input.payload
243
243
  });
244
244
  } catch (error) {
245
- throw new Error(`Command rejected: ${error.message}`);
245
+ throw new Error(`Command rejected: ${error.message}`, { cause: error });
246
246
  }
247
247
  }
248
248
  //#endregion
@@ -279,11 +279,11 @@ async function runMemoryStats(runtime) {
279
279
  }
280
280
  //#endregion
281
281
  //#region src/cli/index.ts
282
+ const collect = (value, previous = []) => [...previous, value];
282
283
  async function main() {
283
284
  const runtime = createCliRuntime();
284
285
  const program = new Command();
285
286
  program.name("agentic-browser").description("Agentic browser CLI");
286
- const collect = (value, previous = []) => [...previous, value];
287
287
  program.command("session:start").option("--cdp-url <url>", "connect to existing Chrome via CDP endpoint URL").option("--user-profile <path>", "use 'default' for system Chrome profile or an absolute path").option("--headless", "run Chrome in headless mode (no visible window)").option("--user-agent <string>", "override the browser user-agent string").action(async (options) => {
288
288
  if (options.cdpUrl) runtime.context.config.cdpUrl = options.cdpUrl;
289
289
  if (options.userProfile) runtime.context.config.userProfileDir = options.userProfile === "true" || options.userProfile === "default" ? "default" : options.userProfile;
package/dist/index.d.mts CHANGED
@@ -99,6 +99,7 @@ interface BrowserController {
99
99
  getCurrentUrl(targetWsUrl: string): Promise<string>;
100
100
  terminate(pid: number): void;
101
101
  closeConnection?(targetWsUrl: string): void;
102
+ dispose?(): void;
102
103
  }
103
104
  //#endregion
104
105
  //#region src/memory/memory-schemas.d.ts
@@ -600,8 +601,7 @@ declare class SessionManager {
600
601
  //#region src/transport/control-api.d.ts
601
602
  declare class ControlApi {
602
603
  private readonly sessions;
603
- private readonly eventStore;
604
- constructor(sessions: SessionManager, eventStore: EventStore);
604
+ constructor(sessions: SessionManager);
605
605
  createSession(input: {
606
606
  browser: "chrome";
607
607
  }): Promise<{
package/dist/index.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env node
2
- import { i as createMockAgenticBrowserCore, n as createAgenticBrowserCore, t as AgenticBrowserCore } from "./runtime-C3Y-UgC1.mjs";
2
+ import { i as createMockAgenticBrowserCore, n as createAgenticBrowserCore, t as AgenticBrowserCore } from "./runtime-BeD8ZcjH.mjs";
3
3
  export { AgenticBrowserCore, createAgenticBrowserCore, createMockAgenticBrowserCore };
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as createAgenticBrowserCore } from "../runtime-C3Y-UgC1.mjs";
2
+ import { n as createAgenticBrowserCore } from "../runtime-BeD8ZcjH.mjs";
3
3
  import { readFileSync } from "node:fs";
4
4
  import { dirname, join } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
@@ -155,7 +155,7 @@ function summarizeElements(elements) {
155
155
  });
156
156
  }
157
157
  return {
158
- countsByRole: Object.fromEntries([...byRole.entries()].sort((a, b) => b[1] - a[1])),
158
+ countsByRole: Object.fromEntries([...byRole.entries()].toSorted((a, b) => b[1] - a[1])),
159
159
  primaryActions
160
160
  };
161
161
  }
@@ -175,6 +175,10 @@ async function getJson(url, timeoutMs = 5e3) {
175
175
  if (!response.ok) throw new Error(`HTTP ${response.status}: ${url}`);
176
176
  return await response.json();
177
177
  }
178
+ /** Debugger connection timing */
179
+ const DEBUGGER_READY_TIMEOUT_MS = 15e3;
180
+ const DEBUGGER_INITIAL_RETRY_DELAY_MS = 50;
181
+ const DEBUGGER_MAX_RETRY_DELAY_MS = 250;
178
182
  /** Check if the debug port is accepting TCP connections (faster than an HTTP fetch). */
179
183
  function probePort(port) {
180
184
  return new Promise((resolve) => {
@@ -193,16 +197,15 @@ function probePort(port) {
193
197
  });
194
198
  }
195
199
  async function waitForDebugger(port) {
196
- const maxMs = 15e3;
197
200
  const start = Date.now();
198
- let delay = 50;
199
- while (Date.now() - start < maxMs) {
201
+ let delay = DEBUGGER_INITIAL_RETRY_DELAY_MS;
202
+ while (Date.now() - start < DEBUGGER_READY_TIMEOUT_MS) {
200
203
  if (await probePort(port)) try {
201
204
  await getJson(`http://127.0.0.1:${port}/json/version`);
202
205
  return;
203
206
  } catch {}
204
207
  await new Promise((resolve) => setTimeout(resolve, delay));
205
- delay = Math.min(delay * 2, 250);
208
+ delay = Math.min(delay * 2, DEBUGGER_MAX_RETRY_DELAY_MS);
206
209
  }
207
210
  throw new Error("Chrome debug endpoint did not become ready in time");
208
211
  }
@@ -694,6 +697,9 @@ function buildKeyDispatchPayload(inputKey) {
694
697
  nativeVirtualKeyCode: 0
695
698
  };
696
699
  }
700
+ /** Connection pool configuration */
701
+ const CONNECTION_IDLE_TIMEOUT_MS = 300 * 1e3;
702
+ const CONNECTION_CLEANUP_INTERVAL_MS = 6e4;
697
703
  var ChromeCdpBrowserController = class {
698
704
  connections = /* @__PURE__ */ new Map();
699
705
  cleanupInterval;
@@ -701,11 +707,16 @@ var ChromeCdpBrowserController = class {
701
707
  this.baseDir = baseDir;
702
708
  this.connectionFactory = connectionFactory;
703
709
  this.cleanupInterval = setInterval(() => {
704
- const cutoff = Date.now() - 300 * 1e3;
710
+ const cutoff = Date.now() - CONNECTION_IDLE_TIMEOUT_MS;
705
711
  for (const [url, entry] of this.connections) if (entry.lastUsedAt < cutoff) this.dropConnection(url);
706
- }, 6e4);
712
+ }, CONNECTION_CLEANUP_INTERVAL_MS);
707
713
  this.cleanupInterval.unref();
708
714
  }
715
+ /** Stop the cleanup interval and close all connections. Call when disposing the controller. */
716
+ dispose() {
717
+ clearInterval(this.cleanupInterval);
718
+ for (const url of this.connections.keys()) this.dropConnection(url);
719
+ }
709
720
  async getConnection(targetWsUrl) {
710
721
  const cached = this.connections.get(targetWsUrl);
711
722
  if (cached) {
@@ -849,12 +860,18 @@ var ChromeCdpBrowserController = class {
849
860
  });
850
861
  }
851
862
  async handleDialogAction(targetWsUrl, payload) {
863
+ const DIALOG_WAIT_TIMEOUT_MS = 500;
864
+ const DIALOG_POLL_INTERVAL_MS = 10;
865
+ const MAX_POLL_ATTEMPTS = 10;
852
866
  return await this.withRetry(targetWsUrl, async (conn) => {
853
867
  await this.ensureEnabled(targetWsUrl);
854
868
  const cached = this.connections.get(targetWsUrl);
855
869
  if (!cached?.pendingDialog) try {
856
- await conn.waitForEvent("Page.javascriptDialogOpening", 500);
857
- await new Promise((r) => setTimeout(r, 50));
870
+ await conn.waitForEvent("Page.javascriptDialogOpening", DIALOG_WAIT_TIMEOUT_MS);
871
+ for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) {
872
+ if (cached?.pendingDialog) break;
873
+ await new Promise((r) => setTimeout(r, DIALOG_POLL_INTERVAL_MS));
874
+ }
858
875
  } catch {
859
876
  return "no dialog present";
860
877
  }
@@ -2467,6 +2484,113 @@ var MockBrowserController = class {
2467
2484
  closeConnection(_targetWsUrl) {}
2468
2485
  };
2469
2486
  //#endregion
2487
+ //#region src/lib/domain-schemas.ts
2488
+ const InteractActionSchema = z.enum([
2489
+ "click",
2490
+ "type",
2491
+ "press",
2492
+ "waitFor",
2493
+ "evaluate",
2494
+ "scroll",
2495
+ "hover",
2496
+ "select",
2497
+ "toggle",
2498
+ "goBack",
2499
+ "goForward",
2500
+ "refresh",
2501
+ "dialog"
2502
+ ]);
2503
+ const InteractPayloadSchema = z.object({
2504
+ action: InteractActionSchema,
2505
+ selector: z.string().optional(),
2506
+ fallbackSelectors: z.array(z.string()).optional(),
2507
+ text: z.string().optional(),
2508
+ key: z.string().optional(),
2509
+ value: z.string().optional(),
2510
+ scrollX: z.number().optional(),
2511
+ scrollY: z.number().optional(),
2512
+ timeoutMs: z.number().optional()
2513
+ });
2514
+ const SessionStatusSchema = z.enum([
2515
+ "starting",
2516
+ "ready",
2517
+ "disconnected",
2518
+ "failed",
2519
+ "terminated"
2520
+ ]);
2521
+ const CommandTypeSchema = z.enum([
2522
+ "navigate",
2523
+ "interact",
2524
+ "restart",
2525
+ "terminate"
2526
+ ]);
2527
+ const ResultStatusSchema = z.enum([
2528
+ "success",
2529
+ "failed",
2530
+ "timed_out"
2531
+ ]);
2532
+ const ConnectionStateSchema = z.enum([
2533
+ "connecting",
2534
+ "ready",
2535
+ "disconnected",
2536
+ "failed"
2537
+ ]);
2538
+ const EventCategorySchema = z.enum([
2539
+ "lifecycle",
2540
+ "security",
2541
+ "command"
2542
+ ]);
2543
+ const EventSeveritySchema = z.enum([
2544
+ "info",
2545
+ "warning",
2546
+ "error"
2547
+ ]);
2548
+ z.object({
2549
+ sessionId: z.string().min(1),
2550
+ status: SessionStatusSchema,
2551
+ browserType: z.literal("chrome"),
2552
+ startedAt: z.string().datetime(),
2553
+ endedAt: z.string().datetime().optional(),
2554
+ authTokenRef: z.string().min(1)
2555
+ }).refine((session) => session.status === "terminated" ? Boolean(session.endedAt) : true, {
2556
+ message: "endedAt is required for terminated sessions",
2557
+ path: ["endedAt"]
2558
+ });
2559
+ z.object({
2560
+ commandId: z.string().min(1),
2561
+ sessionId: z.string().min(1),
2562
+ type: CommandTypeSchema,
2563
+ payload: z.record(z.string(), z.unknown()),
2564
+ submittedAt: z.string().datetime(),
2565
+ completedAt: z.string().datetime().optional(),
2566
+ resultStatus: ResultStatusSchema.optional(),
2567
+ resultMessage: z.string().optional()
2568
+ }).refine((command) => command.completedAt ? Boolean(command.resultStatus) : true, {
2569
+ message: "resultStatus is required when completedAt is set",
2570
+ path: ["resultStatus"]
2571
+ });
2572
+ z.object({
2573
+ sessionId: z.string().min(1),
2574
+ state: ConnectionStateSchema,
2575
+ lastHeartbeatAt: z.string().datetime().optional(),
2576
+ disconnectReason: z.string().optional()
2577
+ });
2578
+ z.object({
2579
+ sessionId: z.string().min(1),
2580
+ tokenId: z.string().min(1),
2581
+ issuedAt: z.string().datetime(),
2582
+ expiresAt: z.string().datetime().optional(),
2583
+ revokedAt: z.string().datetime().optional()
2584
+ });
2585
+ z.object({
2586
+ eventId: z.string().min(1),
2587
+ sessionId: z.string().min(1),
2588
+ category: EventCategorySchema,
2589
+ message: z.string().min(1),
2590
+ createdAt: z.string().datetime(),
2591
+ severity: EventSeveritySchema
2592
+ });
2593
+ //#endregion
2470
2594
  //#region src/session/session-store.ts
2471
2595
  var SessionStore = class {
2472
2596
  filePath;
@@ -2610,7 +2734,7 @@ var SessionManager = class {
2610
2734
  return this.mustGetRecord(recovered.sessionId);
2611
2735
  } catch (restartError) {
2612
2736
  this.recordEvent(sessionId, "lifecycle", "error", `Recovery failed: ${restartError.message}`);
2613
- throw new Error(`Session is not ready and recovery failed: ${restartError.message}`);
2737
+ throw new Error(`Session is not ready and recovery failed: ${restartError.message}`, { cause: restartError });
2614
2738
  }
2615
2739
  throw new Error("Session is terminated. Start a new session.");
2616
2740
  }
@@ -2639,8 +2763,11 @@ var SessionManager = class {
2639
2763
  const finalUrl = await this.browser.navigate(record.targetWsUrl, url);
2640
2764
  this.store.setLastUrl(sessionId, finalUrl);
2641
2765
  resultMessage = `Navigated to ${finalUrl}`;
2642
- } else if (input.type === "interact") resultMessage = `Interaction result: ${await this.browser.interact(record.targetWsUrl, input.payload)}`;
2643
- else if (input.type === "restart") {
2766
+ } else if (input.type === "interact") {
2767
+ const parsedPayload = InteractPayloadSchema.safeParse(input.payload);
2768
+ if (!parsedPayload.success) throw new Error(`Invalid interact payload: ${parsedPayload.error.message}`);
2769
+ resultMessage = `Interaction result: ${await this.browser.interact(record.targetWsUrl, parsedPayload.data)}`;
2770
+ } else if (input.type === "restart") {
2644
2771
  await this.restartSession(sessionId);
2645
2772
  resultMessage = "Session restarted";
2646
2773
  } else if (input.type === "terminate") {
@@ -2952,9 +3079,8 @@ var SessionManager = class {
2952
3079
  //#endregion
2953
3080
  //#region src/transport/control-api.ts
2954
3081
  var ControlApi = class {
2955
- constructor(sessions, eventStore) {
3082
+ constructor(sessions) {
2956
3083
  this.sessions = sessions;
2957
- this.eventStore = eventStore;
2958
3084
  }
2959
3085
  async createSession(input) {
2960
3086
  return await this.sessions.createSession(input);
@@ -2990,7 +3116,7 @@ var ControlApi = class {
2990
3116
  return await this.sessions.getCurrentUrl(sessionId);
2991
3117
  }
2992
3118
  listEvents(sessionId, limit = 100) {
2993
- return { events: this.eventStore.list(sessionId, limit) };
3119
+ return { events: this.sessions.listEvents(sessionId, limit) };
2994
3120
  }
2995
3121
  searchMemory(input) {
2996
3122
  return { results: this.sessions.searchMemory(input) };
@@ -3215,7 +3341,7 @@ function buildSelectorHints(insight) {
3215
3341
  if (!evidence.selector || evidence.result !== "success") continue;
3216
3342
  weightedSelectors.set(evidence.selector, (weightedSelectors.get(evidence.selector) ?? 0) + 3);
3217
3343
  }
3218
- return [...weightedSelectors.entries()].sort((a, b) => b[1] - a[1]).slice(0, 5).map(([selector]) => selector);
3344
+ return [...weightedSelectors.entries()].toSorted((a, b) => b[1] - a[1]).slice(0, 5).map(([selector]) => selector);
3219
3345
  }
3220
3346
  function buildSelectorAliases(insight) {
3221
3347
  return insight.selectorAliases ?? [];
@@ -3291,7 +3417,7 @@ var MemoryIndex = class {
3291
3417
  }
3292
3418
  return best ? [best] : [];
3293
3419
  }
3294
- return candidates.map((insight) => scoreInsight(insight, normalizedIntent, normalizedDomain, profileLookup)).filter((result) => result.score > 0).sort((a, b) => b.score - a.score).slice(0, limit);
3420
+ return candidates.map((insight) => scoreInsight(insight, normalizedIntent, normalizedDomain, profileLookup)).filter((result) => result.score > 0).toSorted((a, b) => b.score - a.score).slice(0, limit);
3295
3421
  }
3296
3422
  };
3297
3423
  //#endregion
@@ -3668,10 +3794,19 @@ var TaskInsightStore = class {
3668
3794
  };
3669
3795
  //#endregion
3670
3796
  //#region src/memory/memory-service.ts
3797
+ /** Memory service configuration */
3671
3798
  const SEARCH_CACHE_TTL_MS = 2e3;
3799
+ const MAX_EVIDENCE_RECORDS = 50;
3800
+ const MAX_RECIPE_STEPS = 8;
3801
+ const MAX_SELECTOR_ALIASES = 10;
3802
+ /** Site profile limits */
3672
3803
  const MAX_NAVIGATION_PATHS = 20;
3673
3804
  const MAX_LAYOUT_FINGERPRINTS = 10;
3674
3805
  const MAX_SELECTOR_PATTERNS = 15;
3806
+ function normalizePathSegment(value) {
3807
+ const [firstSegment = ""] = value.replace(/\/\*$/, "").split("/").filter(Boolean);
3808
+ return firstSegment.toLowerCase();
3809
+ }
3675
3810
  var MemoryService = class {
3676
3811
  store;
3677
3812
  profileStore;
@@ -3729,7 +3864,7 @@ var MemoryService = class {
3729
3864
  fresh: insights.filter((x) => x.freshness === "fresh").length,
3730
3865
  suspect: insights.filter((x) => x.freshness === "suspect").length,
3731
3866
  stale: insights.filter((x) => x.freshness === "stale").length,
3732
- topDomains: [...byDomain.entries()].sort((a, b) => b[1] - a[1]).slice(0, 10).map(([domain, count]) => ({
3867
+ topDomains: [...byDomain.entries()].toSorted((a, b) => b[1] - a[1]).slice(0, 10).map(([domain, count]) => ({
3733
3868
  domain,
3734
3869
  count
3735
3870
  }))
@@ -3781,7 +3916,7 @@ var MemoryService = class {
3781
3916
  const refreshed = applySuccess({
3782
3917
  ...matched,
3783
3918
  useCount: matched.useCount + 1,
3784
- evidence: [...matched.evidence.slice(-49), evidence],
3919
+ evidence: [...matched.evidence.slice(-(MAX_EVIDENCE_RECORDS - 1)), evidence],
3785
3920
  actionRecipe: this.mergeRecipe(matched.actionRecipe, input.step),
3786
3921
  expectedOutcome: input.expectedOutcome
3787
3922
  });
@@ -3815,7 +3950,7 @@ var MemoryService = class {
3815
3950
  const failed = applyFailure({
3816
3951
  ...matched,
3817
3952
  useCount: matched.useCount + 1,
3818
- evidence: [...matched.evidence.slice(-49), evidence]
3953
+ evidence: [...matched.evidence.slice(-(MAX_EVIDENCE_RECORDS - 1)), evidence]
3819
3954
  }, signal);
3820
3955
  this.store.upsert(failed);
3821
3956
  this.invalidateSearchCache();
@@ -3837,7 +3972,7 @@ var MemoryService = class {
3837
3972
  if (fpIndex >= 0) profile.layoutFingerprints[fpIndex] = fingerprint;
3838
3973
  else {
3839
3974
  profile.layoutFingerprints.push(fingerprint);
3840
- if (profile.layoutFingerprints.length > MAX_LAYOUT_FINGERPRINTS) profile.layoutFingerprints = profile.layoutFingerprints.sort((a, b) => b.lastSeenAt.localeCompare(a.lastSeenAt)).slice(0, MAX_LAYOUT_FINGERPRINTS);
3975
+ if (profile.layoutFingerprints.length > MAX_LAYOUT_FINGERPRINTS) profile.layoutFingerprints = profile.layoutFingerprints.toSorted((a, b) => b.lastSeenAt.localeCompare(a.lastSeenAt)).slice(0, MAX_LAYOUT_FINGERPRINTS);
3841
3976
  }
3842
3977
  profile.lastVisitAt = now;
3843
3978
  this.profileStore.upsert(profile);
@@ -3929,12 +4064,8 @@ var MemoryService = class {
3929
4064
  return score;
3930
4065
  }
3931
4066
  sameFirstPathSegment(left, right) {
3932
- const normalize = (value) => {
3933
- const [firstSegment = ""] = value.replace(/\/\*$/, "").split("/").filter(Boolean);
3934
- return firstSegment.toLowerCase();
3935
- };
3936
- const leftSegment = normalize(left);
3937
- const rightSegment = normalize(right);
4067
+ const leftSegment = normalizePathSegment(left);
4068
+ const rightSegment = normalizePathSegment(right);
3938
4069
  return Boolean(leftSegment) && leftSegment === rightSegment;
3939
4070
  }
3940
4071
  createEvidence(input, result, reason) {
@@ -3963,11 +4094,11 @@ var MemoryService = class {
3963
4094
  selector,
3964
4095
  fallbackSelectors: []
3965
4096
  });
3966
- if (aliasMap.size >= 10) break;
4097
+ if (aliasMap.size >= MAX_SELECTOR_ALIASES) break;
3967
4098
  }
3968
4099
  return {
3969
4100
  ...insight,
3970
- selectorAliases: [...aliasMap.values()].slice(0, 10)
4101
+ selectorAliases: [...aliasMap.values()].slice(0, MAX_SELECTOR_ALIASES)
3971
4102
  };
3972
4103
  }
3973
4104
  deriveAliasName(selector) {
@@ -3986,7 +4117,7 @@ var MemoryService = class {
3986
4117
  }
3987
4118
  mergeRecipe(recipe, step) {
3988
4119
  if (recipe.find((existing) => existing.summary === step.summary && existing.selector === step.selector)) return recipe;
3989
- return [...recipe, step].slice(-8);
4120
+ return [...recipe, step].slice(-MAX_RECIPE_STEPS);
3990
4121
  }
3991
4122
  };
3992
4123
  //#endregion
@@ -4018,7 +4149,7 @@ var AgenticBrowserCore = class {
4018
4149
  constructor(context, browserController) {
4019
4150
  this.context = context;
4020
4151
  this.sessions = new SessionManager(context, browserController);
4021
- this.api = new ControlApi(this.sessions, context.eventStore);
4152
+ this.api = new ControlApi(this.sessions);
4022
4153
  }
4023
4154
  async startSession(input = { browser: "chrome" }) {
4024
4155
  return await this.api.createSession(input);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-browser",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
4
4
  "description": "CLI and MCP server for AI agents to control Chrome via CDP",
5
5
  "keywords": [
6
6
  "agent",
@@ -66,9 +66,9 @@
66
66
  "typecheck": "tsc"
67
67
  },
68
68
  "dependencies": {
69
- "@modelcontextprotocol/sdk": "^1.27.1",
69
+ "@modelcontextprotocol/sdk": "^1.29.0",
70
70
  "commander": "^14.0.3",
71
- "ws": "^8.19.0",
71
+ "ws": "^8.20.0",
72
72
  "zod": "^4.3.6"
73
73
  },
74
74
  "devDependencies": {
@@ -76,20 +76,20 @@
76
76
  "@semantic-release/github": "^12.0.6",
77
77
  "@semantic-release/npm": "^13.1.5",
78
78
  "@semantic-release/release-notes-generator": "^14.1.0",
79
- "@types/node": "^25.4.0",
79
+ "@types/node": "^25.6.0",
80
80
  "@types/ws": "^8.18.1",
81
- "oxfmt": "^0.37.0",
82
- "oxlint": "^1.52.0",
83
- "react": "^19.2.4",
84
- "react-dom": "^19.2.4",
81
+ "oxfmt": "^0.44.0",
82
+ "oxlint": "^1.59.0",
83
+ "react": "^19.2.5",
84
+ "react-dom": "^19.2.5",
85
85
  "semantic-release": "^25.0.3",
86
- "tsdown": "^0.21.1",
87
- "typescript": "^5.9.3",
88
- "vitest": "^4.0.18",
86
+ "tsdown": "^0.21.7",
87
+ "typescript": "^6.0.2",
88
+ "vitest": "^4.1.4",
89
89
  "vocs": "1.4.1"
90
90
  },
91
91
  "engines": {
92
- "node": ">=20"
92
+ "node": ">=22"
93
93
  },
94
- "packageManager": "pnpm@10.32.0"
94
+ "packageManager": "pnpm@10.33.0"
95
95
  }