@steipete/oracle 0.15.2 → 0.16.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 (67) hide show
  1. package/dist/bin/oracle-cli.js +1 -1
  2. package/dist/src/browser/actions/assistantResponse.js +218 -126
  3. package/dist/src/browser/actions/modelSelection.js +133 -22
  4. package/dist/src/browser/actions/navigation.js +235 -14
  5. package/dist/src/browser/actions/thinkingStatus.js +228 -0
  6. package/dist/src/browser/actions/thinkingTime.js +68 -11
  7. package/dist/src/browser/chromeLifecycle.js +29 -28
  8. package/dist/src/browser/config.js +14 -1
  9. package/dist/src/browser/constants.js +5 -1
  10. package/dist/src/browser/controlPlan.js +2 -2
  11. package/dist/src/browser/index.js +56 -16
  12. package/dist/src/browser/liveTabs.js +113 -31
  13. package/dist/src/browser/pageActions.js +1 -1
  14. package/dist/src/browser/projectSourcesRunner.js +4 -4
  15. package/dist/src/browser/reattach.js +2 -2
  16. package/dist/src/browser/recoverConversation.js +90 -29
  17. package/dist/src/cli/browserConfig.js +5 -1
  18. package/dist/src/cli/browserTabs.js +54 -33
  19. package/dist/src/cli/options.js +24 -0
  20. package/dist/src/cli/runOptions.js +6 -1
  21. package/dist/src/cli/sessionDisplay.js +38 -14
  22. package/dist/src/oracle/config.js +25 -0
  23. package/dist/src/oracle/geminiModels.js +2 -0
  24. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  25. package/dist/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  26. package/package.json +9 -10
  27. package/vendor/oracle-notifier/OracleNotifier.app/Contents/CodeResources +0 -0
  28. package/vendor/oracle-notifier/OracleNotifier.app/Contents/MacOS/OracleNotifier +0 -0
  29. package/dist/bin/oracle.js +0 -569
  30. package/dist/docs-site/.nojekyll +0 -0
  31. package/dist/docs-site/CNAME +0 -1
  32. package/dist/docs-site/RELEASING.html +0 -410
  33. package/dist/docs-site/agents.html +0 -374
  34. package/dist/docs-site/anthropic.html +0 -368
  35. package/dist/docs-site/bridge.html +0 -416
  36. package/dist/docs-site/browser-mode.html +0 -594
  37. package/dist/docs-site/chromium-forks.html +0 -347
  38. package/dist/docs-site/cli-reference.html +0 -346
  39. package/dist/docs-site/configuration.html +0 -462
  40. package/dist/docs-site/favicon.svg +0 -14
  41. package/dist/docs-site/followup.html +0 -375
  42. package/dist/docs-site/gemini.html +0 -383
  43. package/dist/docs-site/grok.html +0 -325
  44. package/dist/docs-site/index.html +0 -360
  45. package/dist/docs-site/install.html +0 -335
  46. package/dist/docs-site/linux.html +0 -321
  47. package/dist/docs-site/llms.txt +0 -43
  48. package/dist/docs-site/manual-tests.html +0 -596
  49. package/dist/docs-site/mcp.html +0 -391
  50. package/dist/docs-site/multimodel.html +0 -364
  51. package/dist/docs-site/mythical-pro-agents.html +0 -360
  52. package/dist/docs-site/notifier.html +0 -338
  53. package/dist/docs-site/openai-endpoints.html +0 -399
  54. package/dist/docs-site/openrouter.html +0 -344
  55. package/dist/docs-site/quickstart.html +0 -369
  56. package/dist/docs-site/refactor/ux.html +0 -532
  57. package/dist/docs-site/sessions.html +0 -388
  58. package/dist/docs-site/social-card.png +0 -0
  59. package/dist/docs-site/social-card.svg +0 -79
  60. package/dist/docs-site/spec.html +0 -363
  61. package/dist/docs-site/testing.html +0 -320
  62. package/dist/docs-site/tui-debug.html +0 -326
  63. package/dist/docs-site/windows-work.html +0 -323
  64. package/dist/docs-site/windows.html +0 -320
  65. package/dist/src/browser/chromeCookies.js +0 -312
  66. package/dist/src/browser/keytarShim.js +0 -56
  67. package/dist/src/browser/windowsCookies.js +0 -219
@@ -3,10 +3,32 @@ import { createHash } from "node:crypto";
3
3
  import chalk from "chalk";
4
4
  import { sessionStore } from "../sessionStore.js";
5
5
  import { collectChatGptTabs, DEFAULT_REMOTE_CHROME_HOST, DEFAULT_REMOTE_CHROME_PORT, extractConversationIdFromUrl, formatBrowserTabState, harvestChatGptTab, sessionMatchesTab, } from "../browser/liveTabs.js";
6
- import { recoverConversationTab } from "../browser/recoverConversation.js";
6
+ import { isRecoveredConversationHarvestReady, recoverConversationTab, } from "../browser/recoverConversation.js";
7
7
  import { resolveOutputPath } from "./writeOutputPath.js";
8
8
  const LIVE_POLL_MS = 2000;
9
9
  const DEFAULT_STALL_THRESHOLD_MS = 60_000;
10
+ function isRecoverableMissingTabError(message) {
11
+ return (message.includes("No ChatGPT tab matched") ||
12
+ message.includes("No live ChatGPT tabs found") ||
13
+ message.includes("ECONNREFUSED") ||
14
+ message.includes("Could not connect"));
15
+ }
16
+ function finishRecoveredChrome(recoveredChrome, closeAfterRecover) {
17
+ if (!recoveredChrome) {
18
+ return;
19
+ }
20
+ try {
21
+ if (closeAfterRecover) {
22
+ recoveredChrome.kill();
23
+ }
24
+ else {
25
+ recoveredChrome.process?.unref?.();
26
+ }
27
+ }
28
+ catch {
29
+ // best-effort cleanup
30
+ }
31
+ }
10
32
  function sessionBrowserEndpoint(meta) {
11
33
  const runtime = meta?.browser?.runtime ?? {};
12
34
  const remote = meta?.browser?.config?.remoteChrome ?? {};
@@ -150,12 +172,13 @@ export async function harvestSessionBrowserOutput(sessionId, options = {}) {
150
172
  if (!meta) {
151
173
  throw new Error(`No session found with ID ${sessionId}.`);
152
174
  }
153
- const initialEndpoint = sessionBrowserEndpoint(meta) ?? {
175
+ const recordedEndpoint = sessionBrowserEndpoint(meta);
176
+ const initialEndpoint = recordedEndpoint ?? {
154
177
  host: DEFAULT_REMOTE_CHROME_HOST,
155
178
  port: DEFAULT_REMOTE_CHROME_PORT,
156
179
  };
157
180
  const ref = options.browserTabRef ?? resolveSessionTabRef(meta);
158
- const recoverIfMissing = options.recoverIfMissing !== false;
181
+ const recoverIfMissing = options.recoverIfMissing !== false && !options.browserTabRef;
159
182
  let recoveredChrome = null;
160
183
  try {
161
184
  let harvested;
@@ -169,19 +192,18 @@ export async function harvestSessionBrowserOutput(sessionId, options = {}) {
169
192
  }
170
193
  catch (error) {
171
194
  const message = error instanceof Error ? error.message : String(error);
172
- const isMissingTabError = message.includes("No ChatGPT tab matched") ||
173
- message.includes("ECONNREFUSED") ||
174
- message.includes("Could not connect");
175
- if (!isMissingTabError || !recoverIfMissing) {
195
+ if (!isRecoverableMissingTabError(message) || !recoverIfMissing) {
176
196
  throw error;
177
197
  }
178
198
  console.log(chalk.yellow(`No live ChatGPT tab matched session "${sessionId}". Attempting recovery by reopening the saved conversation URL.`));
179
- const recovered = await recoverConversationTab(meta, (line) => console.log(line));
199
+ const recovered = await recoverConversationTab(meta, (line) => console.log(line), {
200
+ existingEndpoint: recordedEndpoint ?? undefined,
201
+ });
180
202
  recoveredChrome = recovered.chrome;
181
203
  harvested = await harvestChatGptTab({
182
204
  host: recovered.host,
183
205
  port: recovered.port,
184
- ref: recovered.url,
206
+ ref: recovered.ref,
185
207
  stallWindowMs: options.stallWindowMs,
186
208
  });
187
209
  }
@@ -197,14 +219,7 @@ export async function harvestSessionBrowserOutput(sessionId, options = {}) {
197
219
  return harvested;
198
220
  }
199
221
  finally {
200
- if (recoveredChrome && options.closeAfterRecover) {
201
- try {
202
- recoveredChrome.kill();
203
- }
204
- catch {
205
- // best-effort cleanup
206
- }
207
- }
222
+ finishRecoveredChrome(recoveredChrome, options.closeAfterRecover);
208
223
  }
209
224
  }
210
225
  export async function liveTailSessionBrowserOutput(sessionId, options = {}) {
@@ -212,16 +227,19 @@ export async function liveTailSessionBrowserOutput(sessionId, options = {}) {
212
227
  if (!meta) {
213
228
  throw new Error(`No session found with ID ${sessionId}.`);
214
229
  }
215
- let endpoint = sessionBrowserEndpoint(meta) ?? {
230
+ const recordedEndpoint = sessionBrowserEndpoint(meta);
231
+ let endpoint = recordedEndpoint ?? {
216
232
  host: DEFAULT_REMOTE_CHROME_HOST,
217
233
  port: DEFAULT_REMOTE_CHROME_PORT,
218
234
  };
219
235
  let browserTabRef = options.browserTabRef ?? resolveSessionTabRef(meta);
220
- const recoverIfMissing = options.recoverIfMissing !== false;
236
+ const recoverIfMissing = options.recoverIfMissing !== false && !options.browserTabRef;
221
237
  let recoveredChrome = null;
222
238
  const stallThresholdMs = options.stallThresholdMs ?? DEFAULT_STALL_THRESHOLD_MS;
223
239
  let lastHash = null;
224
240
  let unchangedSince = Date.now();
241
+ let requireRecoveredContent = false;
242
+ let recoveredContentDeadlineMs = 0;
225
243
  try {
226
244
  // Probe once to see if the live tab is still alive; recover if not.
227
245
  try {
@@ -233,17 +251,19 @@ export async function liveTailSessionBrowserOutput(sessionId, options = {}) {
233
251
  }
234
252
  catch (error) {
235
253
  const message = error instanceof Error ? error.message : String(error);
236
- const isMissingTabError = message.includes("No ChatGPT tab matched") ||
237
- message.includes("ECONNREFUSED") ||
238
- message.includes("Could not connect");
239
- if (!isMissingTabError || !recoverIfMissing) {
254
+ if (!isRecoverableMissingTabError(message) || !recoverIfMissing) {
240
255
  throw error;
241
256
  }
242
257
  console.log(chalk.yellow(`No live ChatGPT tab matched session "${sessionId}". Attempting recovery by reopening the saved conversation URL.`));
243
- const recovered = await recoverConversationTab(meta, (line) => console.log(line));
258
+ const recovered = await recoverConversationTab(meta, (line) => console.log(line), {
259
+ existingEndpoint: recordedEndpoint ?? undefined,
260
+ waitForReady: false,
261
+ });
244
262
  recoveredChrome = recovered.chrome;
245
263
  endpoint = { host: recovered.host, port: recovered.port };
246
- browserTabRef = recovered.url;
264
+ browserTabRef = recovered.ref;
265
+ requireRecoveredContent = true;
266
+ recoveredContentDeadlineMs = Date.now() + stallThresholdMs;
247
267
  }
248
268
  while (true) {
249
269
  const harvested = await harvestChatGptTab({
@@ -252,6 +272,14 @@ export async function liveTailSessionBrowserOutput(sessionId, options = {}) {
252
272
  ref: browserTabRef,
253
273
  });
254
274
  const fullText = harvested.lastAssistantMarkdown ?? harvested.lastAssistantText ?? "";
275
+ if (requireRecoveredContent && !isRecoveredConversationHarvestReady(harvested)) {
276
+ if (Date.now() < recoveredContentDeadlineMs) {
277
+ await new Promise((resolve) => setTimeout(resolve, LIVE_POLL_MS));
278
+ continue;
279
+ }
280
+ throw new Error("Recovered ChatGPT conversation did not become ready in time.");
281
+ }
282
+ requireRecoveredContent = false;
255
283
  const hash = createHash("sha1").update(fullText).digest("hex");
256
284
  if (hash !== lastHash) {
257
285
  lastHash = hash;
@@ -291,13 +319,6 @@ export async function liveTailSessionBrowserOutput(sessionId, options = {}) {
291
319
  }
292
320
  }
293
321
  finally {
294
- if (recoveredChrome && options.closeAfterRecover) {
295
- try {
296
- recoveredChrome.kill();
297
- }
298
- catch {
299
- // best-effort cleanup
300
- }
301
- }
322
+ finishRecoveredChrome(recoveredChrome, options.closeAfterRecover);
302
323
  }
303
324
  }
@@ -255,6 +255,20 @@ export function resolveApiModel(modelValue) {
255
255
  // Passthrough for custom/OpenRouter model IDs.
256
256
  return normalized;
257
257
  }
258
+ function parseBrowserGpt56Label(modelValue) {
259
+ const normalized = normalizeModelOption(modelValue).toLowerCase();
260
+ if (!normalized || normalized.includes("/"))
261
+ return null;
262
+ const match = normalized.match(/^(?:(?:chatgpt|gpt)[\s._-]*)?5[._-]6(?:[\s._-]+(.+))?$/);
263
+ if (!match)
264
+ return null;
265
+ return {
266
+ variant: (match[1] ?? "").replace(/[^a-z0-9]+/g, " ").trim(),
267
+ };
268
+ }
269
+ export function isGpt56BrowserLabel(modelValue) {
270
+ return parseBrowserGpt56Label(modelValue) !== null;
271
+ }
258
272
  export function inferModelFromLabel(modelValue) {
259
273
  const normalized = normalizeModelOption(modelValue).toLowerCase();
260
274
  if (!normalized) {
@@ -298,6 +312,16 @@ export function inferModelFromLabel(modelValue) {
298
312
  if (normalized.includes("classic")) {
299
313
  return "gpt-5-pro";
300
314
  }
315
+ // Browser label family currently exposed by ChatGPT as "GPT-5.6 Sol".
316
+ const gpt56Label = parseBrowserGpt56Label(normalized);
317
+ if (gpt56Label) {
318
+ const { variant } = gpt56Label;
319
+ if (!variant)
320
+ return "gpt-5.6";
321
+ if (variant === "sol")
322
+ return "gpt-5.6-sol";
323
+ throw new InvalidArgumentError(`Unknown GPT-5.6 browser variant "${variant}". Use gpt-5.6 or gpt-5.6-sol.`);
324
+ }
301
325
  if (normalized.includes("thinking") && normalized.includes("heavy")) {
302
326
  return "gpt-5.5";
303
327
  }
@@ -25,7 +25,12 @@ export function resolveRunOptionsFromConfig({ prompt, files = [], model, models,
25
25
  .filter(Boolean);
26
26
  const cliModelArg = normalizeModelOption(model ?? userConfig?.model) || DEFAULT_MODEL;
27
27
  const apiModel = resolveApiModel(cliModelArg);
28
- const browserModel = normalizeChatGptModelForBrowser(inferModelFromLabel(cliModelArg));
28
+ // Browser label inference is intentionally engine-scoped: API model ids such as
29
+ // gpt-5.6-luna must remain provider values even though browser mode rejects
30
+ // unrecognized GPT-5.6 picker variants.
31
+ const browserModel = resolvedEngine === "browser"
32
+ ? normalizeChatGptModelForBrowser(inferModelFromLabel(cliModelArg))
33
+ : apiModel;
29
34
  const isCodex = apiModel.startsWith("gpt-5.1-codex");
30
35
  const isClaude = apiModel.startsWith("claude");
31
36
  const isGrok = apiModel.startsWith("grok");
@@ -43,19 +43,42 @@ function formatBytes(bytes) {
43
43
  function isDeepResearchBrowserSession(metadata) {
44
44
  return metadata.mode === "browser" && metadata.browser?.config?.researchMode === "deep";
45
45
  }
46
- function isDeepResearchPlaceholderCapture(metadata, logText) {
47
- const answer = trimBeforeFirstAnswer(logText)
48
- .replace(/^Answer:\s*/i, "")
46
+ const DEEP_RESEARCH_TOOL_CALL_MARKERS = [
47
+ "called tool",
48
+ "used tool",
49
+ "użyto narzędzia",
50
+ "narzędzie wywołane",
51
+ ];
52
+ function isDeepResearchToolCallPlaceholder(answerText, outputTokens) {
53
+ const lines = answerText
49
54
  .toLowerCase()
50
- .replace(/\s+/g, " ")
51
- .trim();
52
- const isToolOnly = answer === "called tool" ||
53
- answer === "used tool" ||
54
- answer === "użyto narzędzia" ||
55
- answer === "narzędzie wywołane";
55
+ .split(/\r?\n/)
56
+ .map((line) => line.replace(/\s+/g, " ").trim())
57
+ .filter(Boolean);
58
+ if (!lines[0] || !DEEP_RESEARCH_TOOL_CALL_MARKERS.includes(lines[0])) {
59
+ return false;
60
+ }
61
+ if (lines.length === 1) {
62
+ return outputTokens == null || outputTokens <= 8;
63
+ }
64
+ const wrapper = lines.slice(1).join(" ");
65
+ const structuralSignals = [
66
+ wrapper.includes("deep research app"),
67
+ /\bcall tool\b/.test(wrapper),
68
+ /\brequest\s*\{/.test(wrapper),
69
+ /\bresponse\s*\{/.test(wrapper),
70
+ /\bsession[_ ]id\b/.test(wrapper),
71
+ ].filter(Boolean).length;
72
+ return wrapper.includes("deep research app") && structuralSignals >= 2;
73
+ }
74
+ export function isDeepResearchPlaceholderCapture(metadata, logText) {
75
+ if (/\[reattach\][^\n]*\nAnswer:/i.test(logText)) {
76
+ return false;
77
+ }
78
+ const answer = trimBeforeFirstAnswer(logText).replace(/^Answer:\s*/i, "");
56
79
  const modelUsage = metadata.models?.find((run) => run.model === metadata.model)?.usage;
57
80
  const outputTokens = metadata.usage?.outputTokens ?? modelUsage?.outputTokens;
58
- return isToolOnly && (outputTokens == null || outputTokens <= 8);
81
+ return isDeepResearchToolCallPlaceholder(answer, outputTokens);
59
82
  }
60
83
  async function writeReattachAnswer(sessionId, result, replaceExistingLog) {
61
84
  const body = result.answerMarkdown || result.answerText;
@@ -566,10 +589,11 @@ export function trimBeforeFirstAnswer(logText) {
566
589
  if (index === -1) {
567
590
  return logText;
568
591
  }
569
- const fromFirstAnswer = logText.slice(index);
570
- if (/^Answer:\s*(called tool|used tool|użyto narzędzia|narzędzie wywołane)\s*\n\[reattach\]/i.test(fromFirstAnswer)) {
571
- const laterIndex = logText.lastIndexOf(marker);
572
- if (laterIndex > index) {
592
+ const laterIndex = logText.lastIndexOf(marker);
593
+ const reattachIndex = logText.indexOf("[reattach]", index + marker.length);
594
+ if (laterIndex > index && reattachIndex > index && reattachIndex < laterIndex) {
595
+ const firstCapture = logText.slice(index + marker.length, reattachIndex);
596
+ if (isDeepResearchToolCallPlaceholder(firstCapture)) {
573
597
  return logText.slice(laterIndex);
574
598
  }
575
599
  }
@@ -26,7 +26,32 @@ const countTokensAnthropic = (input) => {
26
26
  countTokensAnthropicImpl ??= require("@anthropic-ai/tokenizer").countTokens;
27
27
  return countTokensAnthropicImpl(stringifyTokenizerInput(input));
28
28
  };
29
+ // GPT-5.6 applies higher rates to requests above 272K input tokens. Keep the
30
+ // supported limit at the base-rate boundary until cost estimation supports tiers.
31
+ const GPT_5_6_BASE_RATE_INPUT_LIMIT = 272_000;
29
32
  export const MODEL_CONFIGS = {
33
+ "gpt-5.6": {
34
+ model: "gpt-5.6",
35
+ provider: "openai",
36
+ tokenizer: countTokensGpt5,
37
+ inputLimit: GPT_5_6_BASE_RATE_INPUT_LIMIT,
38
+ pricing: {
39
+ inputPerToken: 5 / 1_000_000,
40
+ outputPerToken: 30 / 1_000_000,
41
+ },
42
+ reasoning: { effort: "xhigh" },
43
+ },
44
+ "gpt-5.6-sol": {
45
+ model: "gpt-5.6-sol",
46
+ provider: "openai",
47
+ tokenizer: countTokensGpt5,
48
+ inputLimit: GPT_5_6_BASE_RATE_INPUT_LIMIT,
49
+ pricing: {
50
+ inputPerToken: 5 / 1_000_000,
51
+ outputPerToken: 30 / 1_000_000,
52
+ },
53
+ reasoning: { effort: "xhigh" },
54
+ },
30
55
  "gpt-5.5-pro": {
31
56
  model: "gpt-5.5-pro",
32
57
  provider: "openai",
@@ -3,6 +3,8 @@ const MODEL_ID_MAP = {
3
3
  "gemini-3.1-pro": "gemini-3.1-pro-preview",
4
4
  "gemini-3.5-flash": "gemini-3.5-flash",
5
5
  "gemini-3-pro": "gemini-3-pro-preview",
6
+ "gpt-5.6": "gpt-5.6",
7
+ "gpt-5.6-sol": "gpt-5.6-sol",
6
8
  "gpt-5.5": "gpt-5.5",
7
9
  "gpt-5.5-pro": "gpt-5.5-pro",
8
10
  "gpt-5.4": "gpt-5.4",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@steipete/oracle",
3
- "version": "0.15.2",
4
- "description": "CLI wrapper around OpenAI Responses API with GPT-5.5 Pro, GPT-5.5, GPT-5.4, GPT-5.2, GPT-5.1, and GPT-5.1 Codex high reasoning modes.",
3
+ "version": "0.16.0",
4
+ "description": "CLI wrapper around OpenAI Responses API with GPT-5.6 Sol, GPT-5.6, GPT-5.5 Pro, GPT-5.5, GPT-5.4, GPT-5.2, GPT-5.1, and GPT-5.1 Codex high reasoning modes.",
5
5
  "keywords": [],
6
6
  "homepage": "https://askoracle.sh",
7
7
  "bugs": {
@@ -32,12 +32,12 @@
32
32
  "docs:list": "tsx scripts/docs-list.ts",
33
33
  "docs:check": "node --no-deprecation --import tsx bin/oracle-cli.ts docs check",
34
34
  "docs:site": "node scripts/build-docs-site.mjs",
35
- "build": "tsgo -p tsconfig.build.json && pnpm run build:vendor",
35
+ "build": "tsc -p tsconfig.build.json && pnpm run build:vendor",
36
36
  "build:vendor": "node -e \"const fs=require('fs'); const path=require('path'); const vendorRoot=path.join('dist','vendor'); fs.rmSync(vendorRoot,{recursive:true,force:true}); const vendors=[['oracle-notifier']]; vendors.forEach(([name])=>{const src=path.join('vendor',name); const dest=path.join(vendorRoot,name); fs.mkdirSync(dest,{recursive:true}); if(fs.existsSync(src)){fs.cpSync(src,dest,{recursive:true,force:true});}});\"",
37
37
  "start": "pnpm run build && node ./dist/scripts/run-cli.js",
38
38
  "oracle": "pnpm start",
39
39
  "check": "pnpm run format:check && pnpm run lint",
40
- "typecheck": "tsgo --noEmit",
40
+ "typecheck": "tsc --noEmit",
41
41
  "format": "oxfmt --write .",
42
42
  "format:check": "oxfmt --check .",
43
43
  "format:diff": "oxfmt --write . && git --no-pager diff",
@@ -86,17 +86,16 @@
86
86
  "@anthropic-ai/tokenizer": "^0.0.4",
87
87
  "@types/chrome-remote-interface": "^0.34.0",
88
88
  "@types/inquirer": "^9.0.10",
89
- "@types/node": "^26.1.0",
90
- "@typescript/native-preview": "7.0.0-dev.20260706.1",
89
+ "@types/node": "^26.1.1",
91
90
  "@vitest/coverage-v8": "4.1.10",
92
- "devtools-protocol": "0.0.1656784",
91
+ "devtools-protocol": "0.0.1658499",
93
92
  "es-toolkit": "^1.49.0",
94
93
  "esbuild": "^0.28.1",
95
- "oxfmt": "0.57.0",
96
- "oxlint": "^1.72.0",
94
+ "oxfmt": "0.58.0",
95
+ "oxlint": "^1.73.0",
97
96
  "puppeteer-core": "^25.3.0",
98
97
  "tsx": "^4.23.0",
99
- "typescript": "^6.0.3",
98
+ "typescript": "^7.0.2",
100
99
  "vitest": "^4.1.10"
101
100
  },
102
101
  "devEngines": {