From 7b5f34317991e2b435fb6a82b564b6ba95532360 Mon Sep 17 00:00:00 2001 From: Thomas Date: Sat, 19 Apr 2025 02:16:33 +1000 Subject: [PATCH 01/82] fix: update context left display logic in TerminalChatInput component (#307) Added persistent context length with colour coding. --- codex-cli/src/components/chat/terminal-chat-input.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/codex-cli/src/components/chat/terminal-chat-input.tsx b/codex-cli/src/components/chat/terminal-chat-input.tsx index 3db4d7f189..ad7ad39a26 100644 --- a/codex-cli/src/components/chat/terminal-chat-input.tsx +++ b/codex-cli/src/components/chat/terminal-chat-input.tsx @@ -397,7 +397,15 @@ export default function TerminalChatInput({ <> send q or ctrl+c to exit | send "/clear" to reset | send "/help" for commands | press enter to send - {contextLeftPercent < 25 && ( + {contextLeftPercent > 25 && ( + <> + {" — "} + 40 ? "green" : "yellow"}> + {Math.round(contextLeftPercent)}% context left + + + )} + {contextLeftPercent <= 25 && ( <> {" — "} From 82f5abbeea54b172fab29eb87bc9d15dab36c2a9 Mon Sep 17 00:00:00 2001 From: Amar Sood Date: Fri, 18 Apr 2025 12:19:06 -0400 Subject: [PATCH 02/82] Fix handling of Shift+Enter in e.g. Ghostty (#338) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix: Shift + Enter no longer prints “[27;2;13~” in the single‑line input. Validated as working and necessary in Ghostty on Linux. ## Key points - src/components/vendor/ink-text-input.tsx - Added early handler that recognises the two modifyOtherKeys escape‑sequences - [13;u (mode 2 / CSI‑u) - [27;;13~ (mode 1 / legacy CSI‑~) - If Ctrl is held (hasCtrl flag) → call onSubmit() (same as plain Enter). - Otherwise → insert a real newline at the caret (same as Option+Enter). - Prevents the raw sequence from being inserted into the buffer. - src/components/chat/multiline-editor.tsx - Replaced non‑breaking spaces with normal spaces to satisfy eslint no‑irregular‑whitespace rule (no behaviour change). All unit tests (114) and ESLint now pass: npm test ✔️ npm run lint ✔️ --- .../src/components/chat/multiline-editor.tsx | 38 +++++++--- .../src/components/vendor/ink-text-input.tsx | 72 +++++++++++++++++++ .../tests/multiline-shift-enter-mod1.test.tsx | 49 +++++++++++++ 3 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 codex-cli/tests/multiline-shift-enter-mod1.test.tsx diff --git a/codex-cli/src/components/chat/multiline-editor.tsx b/codex-cli/src/components/chat/multiline-editor.tsx index c99961bbcd..bb4878c631 100644 --- a/codex-cli/src/components/chat/multiline-editor.tsx +++ b/codex-cli/src/components/chat/multiline-editor.tsx @@ -259,25 +259,47 @@ const MultilineTextEditorInner = ( console.log("[MultilineTextEditor] event", { input, key }); } - // 1) CSI‑u / modifyOtherKeys (Ink strips initial ESC, so we start with '[') + // 1a) CSI-u / modifyOtherKeys *mode 2* (Ink strips initial ESC, so we + // start with '[') – format: "[;u". if (input.startsWith("[") && input.endsWith("u")) { const m = input.match(/^\[([0-9]+);([0-9]+)u$/); if (m && m[1] === "13") { const mod = Number(m[2]); - // In xterm's encoding: bit‑1 (value 2) is Shift. Everything >1 that - // isn't exactly 1 means some modifier was held. We treat *shift - // present* (2,4,6,8) as newline; plain (1) as submit. + // In xterm's encoding: bit-1 (value 2) is Shift. Everything >1 that + // isn't exactly 1 means some modifier was held. We treat *shift or + // alt present* (2,3,4,6,8,9) as newline; Ctrl (bit-2 / value 4) + // triggers submit. See xterm/DEC modifyOtherKeys docs. - // Xterm encodes modifier keys in `mod` – bit‑2 (value 4) indicates - // that Ctrl was held. We avoid the `&` bitwise operator (disallowed - // by our ESLint config) by using arithmetic instead. const hasCtrl = Math.floor(mod / 4) % 2 === 1; if (hasCtrl) { if (onSubmit) { onSubmit(buffer.current.getText()); } } else { - // Any variant without Ctrl just inserts newline (Shift, Alt, none) + buffer.current.newline(); + } + setVersion((v) => v + 1); + return; + } + } + + // 1b) CSI-~ / modifyOtherKeys *mode 1* – format: "[27;;~". + // Terminals such as iTerm2 (default), older xterm versions, or when + // modifyOtherKeys=1 is configured, emit this legacy sequence. We + // translate it to the same behaviour as the mode‑2 variant above so + // that Shift+Enter (newline) / Ctrl+Enter (submit) work regardless + // of the user’s terminal settings. + if (input.startsWith("[27;") && input.endsWith("~")) { + const m = input.match(/^\[27;([0-9]+);13~$/); + if (m) { + const mod = Number(m[1]); + const hasCtrl = Math.floor(mod / 4) % 2 === 1; + + if (hasCtrl) { + if (onSubmit) { + onSubmit(buffer.current.getText()); + } + } else { buffer.current.newline(); } setVersion((v) => v + 1); diff --git a/codex-cli/src/components/vendor/ink-text-input.tsx b/codex-cli/src/components/vendor/ink-text-input.tsx index c516799a70..40b0a1d49a 100644 --- a/codex-cli/src/components/vendor/ink-text-input.tsx +++ b/codex-cli/src/components/vendor/ink-text-input.tsx @@ -153,6 +153,78 @@ function TextInput({ useInput( (input, key) => { + // ──────────────────────────────────────────────────────────────── + // Support Shift+Enter / Ctrl+Enter from terminals that have + // modifyOtherKeys enabled. Such terminals encode the key‑combo in a + // CSI sequence rather than sending a bare "\r"/"\n". Ink passes the + // sequence through as raw text (without the initial ESC), so we need to + // detect and translate it before the generic character handler below + // treats it as literal input (e.g. "[27;2;13~"). We support both the + // modern *mode 2* (CSI‑u, ending in "u") and the legacy *mode 1* + // variant (ending in "~"). + // + // - Shift+Enter → insert newline (same behaviour as Option+Enter) + // - Ctrl+Enter → submit the input (same as plain Enter) + // + // References: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Modify-Other-Keys + // ──────────────────────────────────────────────────────────────── + + function handleEncodedEnterSequence(raw: string): boolean { + // CSI‑u (modifyOtherKeys=2) → "[13;u" + let m = raw.match(/^\[([0-9]+);([0-9]+)u$/); + if (m && m[1] === "13") { + const mod = Number(m[2]); + const hasCtrl = Math.floor(mod / 4) % 2 === 1; + + if (hasCtrl) { + if (onSubmit) { + onSubmit(originalValue); + } + } else { + const newValue = + originalValue.slice(0, cursorOffset) + + "\n" + + originalValue.slice(cursorOffset); + + setState({ + cursorOffset: cursorOffset + 1, + cursorWidth: 0, + }); + onChange(newValue); + } + return true; // handled + } + + // CSI‑~ (modifyOtherKeys=1) → "[27;;13~" + m = raw.match(/^\[27;([0-9]+);13~$/); + if (m) { + const mod = Number(m[1]); + const hasCtrl = Math.floor(mod / 4) % 2 === 1; + + if (hasCtrl) { + if (onSubmit) { + onSubmit(originalValue); + } + } else { + const newValue = + originalValue.slice(0, cursorOffset) + + "\n" + + originalValue.slice(cursorOffset); + + setState({ + cursorOffset: cursorOffset + 1, + cursorWidth: 0, + }); + onChange(newValue); + } + return true; // handled + } + return false; // not an encoded Enter sequence + } + + if (handleEncodedEnterSequence(input)) { + return; + } if ( key.upArrow || key.downArrow || diff --git a/codex-cli/tests/multiline-shift-enter-mod1.test.tsx b/codex-cli/tests/multiline-shift-enter-mod1.test.tsx new file mode 100644 index 0000000000..d309756cfb --- /dev/null +++ b/codex-cli/tests/multiline-shift-enter-mod1.test.tsx @@ -0,0 +1,49 @@ +// Regression test: Terminals with modifyOtherKeys=1 emit CSI~ sequence for +// Shift+Enter: ESC [ 27 ; mod ; 13 ~. The editor must treat Shift+Enter as +// newline (without submitting) and Ctrl+Enter as submit. + +import { renderTui } from "./ui-test-helpers.js"; +import MultilineTextEditor from "../src/components/chat/multiline-editor.js"; +import * as React from "react"; +import { describe, it, expect, vi } from "vitest"; + +async function type( + stdin: NodeJS.WritableStream, + text: string, + flush: () => Promise, +) { + stdin.write(text); + await flush(); +} + +describe("MultilineTextEditor – Shift+Enter with modifyOtherKeys=1", () => { + it("inserts newline, does NOT submit", async () => { + const onSubmit = vi.fn(); + + const { stdin, lastFrameStripped, flush, cleanup } = renderTui( + React.createElement(MultilineTextEditor, { + height: 5, + width: 20, + initialText: "", + onSubmit, + }), + ); + + await flush(); + + await type(stdin, "abc", flush); + // Shift+Enter => ESC [27;2;13~ + await type(stdin, "\u001B[27;2;13~", flush); + await type(stdin, "def", flush); + + const frame = lastFrameStripped(); + expect(frame).toMatch(/abc/); + expect(frame).toMatch(/def/); + // newline inserted -> at least 2 lines + expect(frame.split("\n").length).toBeGreaterThanOrEqual(2); + + expect(onSubmit).not.toHaveBeenCalled(); + + cleanup(); + }); +}); From 4acd7d8617c6b355297cfa2afef21b1e75f6fc32 Mon Sep 17 00:00:00 2001 From: Prama <109050315+PramaYudhistira@users.noreply.github.com> Date: Fri, 18 Apr 2025 14:35:51 -0400 Subject: [PATCH 03/82] fix: Improper spawn of sh on Windows Powershell (#318) # Fix CLI launcher on Windows by replacing `sh`-based entrypoint with cross-platform Node script ## What's changed * This PR attempts to replace the sh-based entry point with a node script that works on all platforms including Windows Powershell and CMD ## Why * Previously, when installing Codex globally via `npm i -g @openai/codex`, Windows resulted in a broken CLI issue due to the `ps1` launcher trying to execute `sh.exe`. * If users don't have Unix-style shell, running the command will fail as seen below since `sh.exe` can't be found * Output: ``` & : The term 'sh.exe' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again. At C:\Users\{user}\AppData\Roaming\npm\codex.ps1:24 char:7 + & "sh$exe" "$basedir/node_modules/@openai/codex/bin/codex" $args + ~~~~~~~~ + CategoryInfo : ObjectNotFound: (sh.exe:String) [], CommandNotFoundException + FullyQualifiedErrorId : CommandNotFoundException ``` ## How * By using a Node based entry point that resolves the path to the compiled ESM bundle and dynamically loads it using native ESM * Removed dependency on platform-specific launchers allowing a single entrypoint to work everywhere Node.js runs. ## Result Codex CLI now supports cross-platform and launches correctly via: * macOS / Linux * Windows PowerShell * GitBash * CMD * WSL Directly addresses #316 ![image](https://github.com/user-attachments/assets/85faaca4-24bc-47c9-8160-4e30df6da4c3) ![image](https://github.com/user-attachments/assets/a13f7adc-52c1-4c0e-af02-e35a35dc45d4) --- codex-cli/bin/codex.js | 24 ++++++++++++++++++++++++ codex-cli/package.json | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 codex-cli/bin/codex.js diff --git a/codex-cli/bin/codex.js b/codex-cli/bin/codex.js new file mode 100644 index 0000000000..347dc854ec --- /dev/null +++ b/codex-cli/bin/codex.js @@ -0,0 +1,24 @@ +#!/usr/bin/env node +// Unified entry point for Codex CLI on all platforms +// Dynamically loads the compiled ESM bundle in dist/cli.js + +import path from 'path'; +import { fileURLToPath, pathToFileURL } from 'url'; + +// Determine this script's directory +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Resolve the path to the compiled CLI bundle +const cliPath = path.resolve(__dirname, '../dist/cli.js'); +const cliUrl = pathToFileURL(cliPath).href; + +// Load and execute the CLI +(async () => { + try { + await import(cliUrl); + } catch (err) { + console.error(err); + process.exit(1); + } +})(); diff --git a/codex-cli/package.json b/codex-cli/package.json index f5722a1351..6834f15694 100644 --- a/codex-cli/package.json +++ b/codex-cli/package.json @@ -3,7 +3,7 @@ "version": "0.1.2504172351", "license": "Apache-2.0", "bin": { - "codex": "bin/codex" + "codex": "bin/codex.js" }, "type": "module", "engines": { From 8e2e77fafb5f3b166622c0cae11fc1143ced276a Mon Sep 17 00:00:00 2001 From: Fouad Matin <169186268+fouad-openai@users.noreply.github.com> Date: Fri, 18 Apr 2025 14:09:35 -0700 Subject: [PATCH 04/82] feat: add /bug report command (#312) Add `/bug` command for chat session --- .github/ISSUE_TEMPLATE/2-bug-report.yml | 1 + .../components/chat/terminal-chat-input.tsx | 66 +++++++++++++++ .../src/components/chat/terminal-chat.tsx | 1 + codex-cli/src/components/help-overlay.tsx | 3 + codex-cli/src/utils/bug-report.ts | 81 +++++++++++++++++++ 5 files changed, 152 insertions(+) create mode 100644 codex-cli/src/utils/bug-report.ts diff --git a/.github/ISSUE_TEMPLATE/2-bug-report.yml b/.github/ISSUE_TEMPLATE/2-bug-report.yml index 11476d5074..9db7f2ef2e 100644 --- a/.github/ISSUE_TEMPLATE/2-bug-report.yml +++ b/.github/ISSUE_TEMPLATE/2-bug-report.yml @@ -26,6 +26,7 @@ body: label: Which model were you using? description: Like `gpt-4.1`, `o4-mini`, `o3`, etc. - type: input + id: platform attributes: label: What platform is your computer? description: | diff --git a/codex-cli/src/components/chat/terminal-chat-input.tsx b/codex-cli/src/components/chat/terminal-chat-input.tsx index ad7ad39a26..fda19b6be8 100644 --- a/codex-cli/src/components/chat/terminal-chat-input.tsx +++ b/codex-cli/src/components/chat/terminal-chat-input.tsx @@ -45,6 +45,7 @@ export default function TerminalChatInput({ onCompact, interruptAgent, active, + items = [], }: { isNew: boolean; loading: boolean; @@ -65,6 +66,8 @@ export default function TerminalChatInput({ onCompact: () => void; interruptAgent: () => void; active: boolean; + // New: current conversation items so we can include them in bug reports + items?: Array; }): React.ReactElement { const app = useApp(); const [selectedSuggestion, setSelectedSuggestion] = useState(0); @@ -239,6 +242,68 @@ export default function TerminalChatInput({ }, ); + return; + } else if (inputValue === "/bug") { + // Generate a GitHub bug report URL pre‑filled with session details + setInput(""); + + try { + // Dynamically import dependencies to avoid unnecessary bundle size + const [{ default: open }, os] = await Promise.all([ + import("open"), + import("node:os"), + ]); + + // Lazy import CLI_VERSION to avoid circular deps + const { CLI_VERSION } = await import("../../utils/session.js"); + + const { buildBugReportUrl } = await import( + "../../utils/bug-report.js" + ); + + const url = buildBugReportUrl({ + items: items ?? [], + cliVersion: CLI_VERSION, + model: loadConfig().model ?? "unknown", + platform: `${os.platform()} ${os.arch()} ${os.release()}`, + }); + + // Open the URL in the user's default browser + await open(url, { wait: false }); + + // Inform the user in the chat history + setItems((prev) => [ + ...prev, + { + id: `bugreport-${Date.now()}`, + type: "message", + role: "system", + content: [ + { + type: "input_text", + text: "📋 Opened browser to file a bug report. Please include any context that might help us fix the issue!", + }, + ], + }, + ]); + } catch (error) { + // If anything went wrong, notify the user + setItems((prev) => [ + ...prev, + { + id: `bugreport-error-${Date.now()}`, + type: "message", + role: "system", + content: [ + { + type: "input_text", + text: `⚠️ Failed to create bug report URL: ${error}`, + }, + ], + }, + ]); + } + return; } else if (inputValue.startsWith("/")) { // Handle invalid/unrecognized commands. @@ -330,6 +395,7 @@ export default function TerminalChatInput({ openHelpOverlay, history, // Add history to the dependency array onCompact, + items, ], ); diff --git a/codex-cli/src/components/chat/terminal-chat.tsx b/codex-cli/src/components/chat/terminal-chat.tsx index 50ee447932..1cfeffe14e 100644 --- a/codex-cli/src/components/chat/terminal-chat.tsx +++ b/codex-cli/src/components/chat/terminal-chat.tsx @@ -516,6 +516,7 @@ export default function TerminalChat({ agent.run(inputs, lastResponseId || ""); return {}; }} + items={items} /> )} {overlayMode === "history" && ( diff --git a/codex-cli/src/components/help-overlay.tsx b/codex-cli/src/components/help-overlay.tsx index 9feabc16aa..132add8307 100644 --- a/codex-cli/src/components/help-overlay.tsx +++ b/codex-cli/src/components/help-overlay.tsx @@ -52,6 +52,9 @@ export default function HelpOverlay({ /clearhistory – clear command history + + /bug – file a bug report with session log + /compact – condense context into a summary diff --git a/codex-cli/src/utils/bug-report.ts b/codex-cli/src/utils/bug-report.ts new file mode 100644 index 0000000000..0fbd0329ed --- /dev/null +++ b/codex-cli/src/utils/bug-report.ts @@ -0,0 +1,81 @@ +import type { ResponseItem } from "openai/resources/responses/responses.mjs"; + +/** + * Build a GitHub issues‐new URL that pre‑fills the Codex 2‑bug‑report.yml + * template with whatever structured data we can infer from the current + * session. + */ +export function buildBugReportUrl({ + items, + cliVersion, + model, + platform, +}: { + /** Chat history so we can summarise user steps */ + items: Array; + /** CLI revision string (e.g. output of `codex --revision`) */ + cliVersion: string; + /** Active model name */ + model: string; + /** Platform string – e.g. `darwin arm64 23.0.0` */ + platform: string; +}): string { + const params = new URLSearchParams({ + template: "2-bug-report.yml", + labels: "bug", + }); + + // Template ids ------------------------------------------------------------- + params.set("version", cliVersion); + params.set("model", model); + + // The platform input has no explicit `id`, so GitHub falls back to a slug of + // the label text. For “What platform is your computer?” that slug is: + // what-platform-is-your-computer + params.set("what-platform-is-your-computer", platform); + + // Build the steps bullet list --------------------------------------------- + const bullets: Array = []; + for (let i = 0; i < items.length; ) { + const entry = items[i]; + if (entry?.type === "message" && entry.role === "user") { + const contentArray = entry.content as + | Array<{ text?: string }> + | undefined; + const messageText = contentArray + ?.map((c) => c.text ?? "") + .join(" ") + .trim(); + + let reasoning = 0; + let toolCalls = 0; + let j = i + 1; + while ( + j < items.length && + !(entry?.type === "message" && entry.role === "user") + ) { + const it = items[j]; + if (it?.type === "message" && it?.role === "assistant") { + reasoning += 1; + } else if (it?.type === "function_call") { + toolCalls += 1; + } + j++; + } + + bullets.push( + `- "${messageText}"\n - \`${reasoning} reasoning steps\` | \`${toolCalls} tool calls\``, + ); + + i = j; + } else { + i += 1; + } + } + + if (bullets.length) { + params.set("steps", bullets.join("\n")); + } + + return `https://github.com/openai/codex/issues/new?${params.toString()}`; +} From 9a046dfcaa5dcc2df8551f4c491ab2bdc2d08055 Mon Sep 17 00:00:00 2001 From: Jon Church Date: Fri, 18 Apr 2025 19:11:34 -0400 Subject: [PATCH 05/82] =?UTF-8?q?Revert=20"fix:=20canonicalize=20the=20wri?= =?UTF-8?q?teable=20paths=20used=20in=20seatbelt=20policy=E2=80=A6=20(#370?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 3356ac0aefac43d45973b994dcabfb8125779cd7. related #330 --- codex-cli/src/utils/agent/sandbox/macos-seatbelt.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/codex-cli/src/utils/agent/sandbox/macos-seatbelt.ts b/codex-cli/src/utils/agent/sandbox/macos-seatbelt.ts index 77f7216fd4..0317458261 100644 --- a/codex-cli/src/utils/agent/sandbox/macos-seatbelt.ts +++ b/codex-cli/src/utils/agent/sandbox/macos-seatbelt.ts @@ -3,7 +3,6 @@ import type { SpawnOptions } from "child_process"; import { exec } from "./raw-exec.js"; import { log } from "../log.js"; -import { realpathSync } from "fs"; import { CONFIG_DIR } from "src/utils/config.js"; function getCommonRoots() { @@ -30,9 +29,7 @@ export function execWithSeatbelt( const { policies, params } = writableRoots .map((root, index) => ({ policy: `(subpath (param "WRITABLE_ROOT_${index}"))`, - // the kernel resolves symlinks before handing them to seatbelt for checking - // so store the canonicalized form in the policy to be compared against - param: `-DWRITABLE_ROOT_${index}=${realpathSync(root)}`, + param: `-DWRITABLE_ROOT_${index}=${root}`, })) .reduce( ( From e2fe2572ba8717cbacef26c8d459a72bcf2a77e9 Mon Sep 17 00:00:00 2001 From: Alpha Diop <90140491+alphajoop@users.noreply.github.com> Date: Fri, 18 Apr 2025 23:25:15 +0000 Subject: [PATCH 06/82] chore: migrate to pnpm for improved monorepo management (#287) # Migrate to pnpm for improved monorepo management ## Summary This PR migrates the Codex repository from npm to pnpm, providing faster dependency installation, better disk space usage, and improved monorepo management. ## Changes - Added `pnpm-workspace.yaml` to define workspace packages - Added `.npmrc` with optimal pnpm configuration - Updated root package.json with workspace scripts - Moved resolutions and overrides to the root package.json - Updated scripts to use pnpm instead of npm - Added documentation for the migration - Updated GitHub Actions workflow for pnpm ## Benefits - **Faster installations**: pnpm is significantly faster than npm - **Disk space savings**: pnpm's content-addressable store avoids duplication - **Strict dependency management**: prevents phantom dependencies - **Simplified monorepo management**: better workspace coordination - **Preparation for Turborepo**: as discussed, this is the first step before adding Turborepo ## Testing - Verified that `pnpm install` works correctly - Verified that `pnpm run build` completes successfully - Ensured all existing functionality is preserved ## Documentation Added a detailed migration guide in `PNPM_MIGRATION.md` explaining: - Why we're migrating to pnpm - How to use pnpm with this repository - Common commands and workspace-specific commands - Monorepo structure and configuration ## Next Steps As discussed, once this change is stable, we can consider adding Turborepo as a follow-up enhancement. --- .github/workflows/ci.yml | 67 +- .gitignore | 6 + .npmrc | 4 + .prettierignore | 1 + PNPM_MIGRATION.md | 70 + codex-cli/package-lock.json | 7372 ----------------------- codex-cli/package.json | 12 +- codex-cli/src/utils/agent/agent-loop.ts | 1 - lint-staged.config.mjs | 4 + package-lock.json | 359 -- package.json | 25 +- pnpm-workspace.yaml | 5 + 12 files changed, 152 insertions(+), 7774 deletions(-) create mode 100644 .npmrc create mode 100644 PNPM_MIGRATION.md delete mode 100644 codex-cli/package-lock.json create mode 100644 lint-staged.config.mjs delete mode 100644 package-lock.json create mode 100644 pnpm-workspace.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51b84cc930..695190c4c9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,40 +19,47 @@ jobs: with: node-version: 22 - # Run codex-cli/ tasks first because they are higher signal. + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.8.1 + run_install: false - - name: Install dependencies (codex-cli) - working-directory: codex-cli - run: npm ci - - - name: Check formatting (codex-cli) - working-directory: codex-cli - run: npm run format - - - name: Run tests (codex-cli) - working-directory: codex-cli - run: npm run test - - - name: Lint (codex-cli) - working-directory: codex-cli + - name: Get pnpm store directory + id: pnpm-cache + shell: bash run: | - npm run lint -- \ + echo "store_path=$(pnpm store path --silent)" >> $GITHUB_OUTPUT + + - name: Setup pnpm cache + uses: actions/cache@v4 + with: + path: ${{ steps.pnpm-cache.outputs.store_path }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install + + # Run all tasks using workspace filters + + - name: Check formatting + run: pnpm run format + + - name: Run tests + run: pnpm run test + + - name: Lint + run: | + pnpm --filter @openai/codex exec -- eslint src tests --ext ts --ext tsx \ + --report-unused-disable-directives \ --rule "no-console:error" \ --rule "no-debugger:error" \ --max-warnings=-1 - - name: Type‑check (codex-cli) - working-directory: codex-cli - run: npm run typecheck + - name: Type-check + run: pnpm run typecheck - - name: Build (codex-cli) - working-directory: codex-cli - run: npm run build - - # Run formatting checks in the root directory last. - - - name: Install dependencies (root) - run: npm ci - - - name: Check formatting (root) - run: npm run format + - name: Build + run: pnpm run build diff --git a/.gitignore b/.gitignore index 09f3f04532..820100ff4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,11 @@ # deps +# Node.js dependencies node_modules +.pnpm-store +.pnpm-debug.log + +# Keep pnpm-lock.yaml +!pnpm-lock.yaml # build dist/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000000..4153fe87bb --- /dev/null +++ b/.npmrc @@ -0,0 +1,4 @@ +shamefully-hoist=true +strict-peer-dependencies=false +node-linker=hoisted +prefer-workspace-packages=true diff --git a/.prettierignore b/.prettierignore index 07700970c0..1800016bda 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,2 +1,3 @@ /codex-cli/dist /codex-cli/node_modules +pnpm-lock.yaml diff --git a/PNPM_MIGRATION.md b/PNPM_MIGRATION.md new file mode 100644 index 0000000000..860633c8e1 --- /dev/null +++ b/PNPM_MIGRATION.md @@ -0,0 +1,70 @@ +# Migration to pnpm + +This project has been migrated from npm to pnpm to improve dependency management and developer experience. + +## Why pnpm? + +- **Faster installation**: pnpm is significantly faster than npm and yarn +- **Disk space savings**: pnpm uses a content-addressable store to avoid duplication +- **Phantom dependency prevention**: pnpm creates a strict node_modules structure +- **Native workspaces support**: simplified monorepo management + +## How to use pnpm + +### Installation + +```bash +# Global installation of pnpm +npm install -g pnpm@10.8.1 + +# Or with corepack (available with Node.js 22+) +corepack enable +corepack prepare pnpm@10.8.1 --activate +``` + +### Common commands + +| npm command | pnpm equivalent | +| --------------- | ---------------- | +| `npm install` | `pnpm install` | +| `npm run build` | `pnpm run build` | +| `npm test` | `pnpm test` | +| `npm run lint` | `pnpm run lint` | + +### Workspace-specific commands + +| Action | Command | +| ------------------------------------------ | ---------------------------------------- | +| Run a command in a specific package | `pnpm --filter @openai/codex run build` | +| Install a dependency in a specific package | `pnpm --filter @openai/codex add lodash` | +| Run a command in all packages | `pnpm -r run test` | + +## Monorepo structure + +``` +codex/ +├── pnpm-workspace.yaml # Workspace configuration +├── .npmrc # pnpm configuration +├── package.json # Root dependencies and scripts +├── codex-cli/ # Main package +│ └── package.json # codex-cli specific dependencies +└── docs/ # Documentation (future package) +``` + +## Configuration files + +- **pnpm-workspace.yaml**: Defines the packages included in the monorepo +- **.npmrc**: Configures pnpm behavior +- **Root package.json**: Contains shared scripts and dependencies + +## CI/CD + +CI/CD workflows have been updated to use pnpm instead of npm. Make sure your CI environments use pnpm 10.8.1 or higher. + +## Known issues + +If you encounter issues with pnpm, try the following solutions: + +1. Remove the `node_modules` folder and `pnpm-lock.yaml` file, then run `pnpm install` +2. Make sure you're using pnpm 10.8.1 or higher +3. Verify that Node.js 22 or higher is installed diff --git a/codex-cli/package-lock.json b/codex-cli/package-lock.json deleted file mode 100644 index cd7a7e9131..0000000000 --- a/codex-cli/package-lock.json +++ /dev/null @@ -1,7372 +0,0 @@ -{ - "name": "@openai/codex", - "version": "0.1.2504172351", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "@openai/codex", - "version": "0.1.2504172351", - "license": "Apache-2.0", - "dependencies": { - "@inkjs/ui": "^2.0.0", - "chalk": "^5.2.0", - "diff": "^7.0.0", - "dotenv": "^16.1.4", - "fast-deep-equal": "^3.1.3", - "figures": "^6.1.0", - "file-type": "^20.1.0", - "ink": "^5.2.0", - "js-yaml": "^4.1.0", - "marked": "^15.0.7", - "marked-terminal": "^7.3.0", - "meow": "^13.2.0", - "open": "^10.1.0", - "openai": "^4.89.0", - "react": "^18.2.0", - "shell-quote": "^1.8.2", - "strip-ansi": "^7.1.0", - "to-rotated": "^1.0.0", - "use-interval": "1.4.0", - "zod": "^3.24.3" - }, - "bin": { - "codex": "bin/codex" - }, - "devDependencies": { - "@eslint/js": "^9.22.0", - "@types/diff": "^7.0.2", - "@types/js-yaml": "^4.0.9", - "@types/marked-terminal": "^6.1.1", - "@types/react": "^18.0.32", - "@types/shell-quote": "^1.7.5", - "@typescript-eslint/eslint-plugin": "^7.18.0", - "@typescript-eslint/parser": "^7.18.0", - "esbuild": "^0.25.2", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-react": "^7.32.2", - "eslint-plugin-react-hooks": "^4.6.0", - "eslint-plugin-react-refresh": "^0.4.19", - "husky": "^9.1.7", - "ink-testing-library": "^3.0.0", - "lint-staged": "^15.5.1", - "prettier": "^2.8.7", - "punycode": "^2.3.1", - "ts-node": "^10.9.1", - "typescript": "^5.0.3", - "vitest": "^3.0.9", - "whatwg-url": "^14.2.0" - }, - "engines": { - "node": ">=22" - } - }, - "node_modules/@alcalzone/ansi-tokenize": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@alcalzone/ansi-tokenize/-/ansi-tokenize-0.1.3.tgz", - "integrity": "sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw==", - "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^4.0.0" - }, - "engines": { - "node": ">=14.13.1" - } - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", - "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.0.tgz", - "integrity": "sha512-WhCn7Z7TauhBtmzhvKpoQs0Wwb/kBcy4CwpuI0/eEIr2Lx2auxmulAzLr91wVZJaz47iUZdkXOK7WlAfxGKCnA==", - "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "peer": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", - "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "peer": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true, - "peer": true - }, - "node_modules/@inkjs/ui": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@inkjs/ui/-/ui-2.0.0.tgz", - "integrity": "sha512-5+8fJmwtF9UvikzLfph9sA+LS+l37Ij/szQltkuXLOAXwNkBX9innfzh4pLGXIB59vKEQUtc6D4qGvhD7h3pAg==", - "dependencies": { - "chalk": "^5.3.0", - "cli-spinners": "^3.0.0", - "deepmerge": "^4.3.1", - "figures": "^6.1.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "ink": ">=5" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.0.tgz", - "integrity": "sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.0.tgz", - "integrity": "sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.0.tgz", - "integrity": "sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.0.tgz", - "integrity": "sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.0.tgz", - "integrity": "sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.0.tgz", - "integrity": "sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.0.tgz", - "integrity": "sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.0.tgz", - "integrity": "sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.0.tgz", - "integrity": "sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.0.tgz", - "integrity": "sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.0.tgz", - "integrity": "sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.0.tgz", - "integrity": "sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.0.tgz", - "integrity": "sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.0.tgz", - "integrity": "sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.0.tgz", - "integrity": "sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.0.tgz", - "integrity": "sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.0.tgz", - "integrity": "sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.0.tgz", - "integrity": "sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.0.tgz", - "integrity": "sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz", - "integrity": "sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rtsao/scc": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", - "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", - "dev": true - }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@tokenizer/inflate": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", - "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", - "dependencies": { - "debug": "^4.4.0", - "fflate": "^0.8.2", - "token-types": "^6.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", - "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", - "dev": true - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true - }, - "node_modules/@types/cardinal": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@types/cardinal/-/cardinal-2.1.1.tgz", - "integrity": "sha512-/xCVwg8lWvahHsV2wXZt4i64H1sdL+sN1Uoq7fAc8/FA6uYHjuIveDwPwvGUYp4VZiv85dVl6J/Bum3NDAOm8g==", - "dev": true - }, - "node_modules/@types/diff": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/diff/-/diff-7.0.2.tgz", - "integrity": "sha512-JSWRMozjFKsGlEjiiKajUjIJVKuKdE3oVy2DNtK+fUo8q82nhFZ2CPQwicAIkXrofahDXrWJ7mjelvZphMS98Q==", - "dev": true - }, - "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", - "dev": true - }, - "node_modules/@types/js-yaml": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", - "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/marked-terminal": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/@types/marked-terminal/-/marked-terminal-6.1.1.tgz", - "integrity": "sha512-DfoUqkmFDCED7eBY9vFUhJ9fW8oZcMAK5EwRDQ9drjTbpQa+DnBTQQCwWhTFVf4WsZ6yYcJTI8D91wxTWXRZZQ==", - "dev": true, - "dependencies": { - "@types/cardinal": "^2.1", - "@types/node": "*", - "chalk": "^5.3.0", - "marked": ">=6.0.0 <12" - } - }, - "node_modules/@types/marked-terminal/node_modules/marked": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-11.2.0.tgz", - "integrity": "sha512-HR0m3bvu0jAPYiIvLUUQtdg1g6D247//lvcekpHO1WMvbwDlwSkZAX9Lw4F4YHE1T0HaaNve0tuAWuV1UJ6vtw==", - "dev": true, - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@types/node": { - "version": "22.14.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", - "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.12", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.12.tgz", - "integrity": "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "devOptional": true - }, - "node_modules/@types/react": { - "version": "18.3.20", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.20.tgz", - "integrity": "sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==", - "devOptional": true, - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/shell-quote": { - "version": "1.7.5", - "resolved": "https://registry.npmjs.org/@types/shell-quote/-/shell-quote-1.7.5.tgz", - "integrity": "sha512-+UE8GAGRPbJVQDdxi16dgadcBfQ+KG2vgZhV1+3A1XmHbmwcdwhCUwIdy+d3pAGrbvgRoVSjeI9vOWyq376Yzw==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.18.0.tgz", - "integrity": "sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/type-utils": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.18.0.tgz", - "integrity": "sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", - "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.18.0.tgz", - "integrity": "sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "7.18.0", - "@typescript-eslint/utils": "7.18.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.18.0.tgz", - "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", - "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.18.0.tgz", - "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", - "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "peer": true - }, - "node_modules/@vitest/expect": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.1.tgz", - "integrity": "sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==", - "dev": true, - "dependencies": { - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/mocker": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.1.tgz", - "integrity": "sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==", - "dev": true, - "dependencies": { - "@vitest/spy": "3.1.1", - "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0" - }, - "peerDependenciesMeta": { - "msw": { - "optional": true - }, - "vite": { - "optional": true - } - } - }, - "node_modules/@vitest/pretty-format": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.1.tgz", - "integrity": "sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==", - "dev": true, - "dependencies": { - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.1.tgz", - "integrity": "sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==", - "dev": true, - "dependencies": { - "@vitest/utils": "3.1.1", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.1.tgz", - "integrity": "sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==", - "dev": true, - "dependencies": { - "@vitest/pretty-format": "3.1.1", - "magic-string": "^0.30.17", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/spy": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.1.tgz", - "integrity": "sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==", - "dev": true, - "dependencies": { - "tinyspy": "^3.0.2" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.1.tgz", - "integrity": "sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==", - "dev": true, - "dependencies": { - "@vitest/pretty-format": "3.1.1", - "loupe": "^3.1.3", - "tinyrainbow": "^2.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "peer": true, - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz", - "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==", - "dependencies": { - "environment": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", - "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", - "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-shim-unscopables": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", - "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", - "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", - "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/assertion-error": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/async-function": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", - "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "node_modules/auto-bind": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/auto-bind/-/auto-bind-5.0.1.tgz", - "integrity": "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/bundle-name": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", - "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", - "dependencies": { - "run-applescript": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/chai": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", - "dev": true, - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/chalk": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", - "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "engines": { - "node": ">=10" - } - }, - "node_modules/check-error": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", - "dev": true, - "engines": { - "node": ">= 16" - } - }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dependencies": { - "restore-cursor": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-highlight": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", - "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", - "dependencies": { - "chalk": "^4.0.0", - "highlight.js": "^10.7.1", - "mz": "^2.4.0", - "parse5": "^5.1.1", - "parse5-htmlparser2-tree-adapter": "^6.0.0", - "yargs": "^16.0.0" - }, - "bin": { - "highlight": "bin/highlight" - }, - "engines": { - "node": ">=8.0.0", - "npm": ">=5.0.0" - } - }, - "node_modules/cli-highlight/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cli-highlight/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/cli-spinners": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.2.0.tgz", - "integrity": "sha512-pXftdQloMZzjCr3pCTIRniDcys6dDzgpgVhAHHk6TKBDbRuP1MkuetTF5KSv4YUutbOPa7+7ZrAJ2kVtbMqyXA==", - "engines": { - "node": ">=18.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cli-table3/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/cli-table3/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-table3/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-truncate": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", - "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", - "dependencies": { - "slice-ansi": "^5.0.0", - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-truncate/node_modules/slice-ansi": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz", - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "dependencies": { - "ansi-styles": "^6.0.0", - "is-fullwidth-code-point": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/cliui/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/cliui/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/cliui/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/code-excerpt": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/code-excerpt/-/code-excerpt-4.0.0.tgz", - "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", - "dependencies": { - "convert-to-spaces": "^2.0.1" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", - "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/convert-to-spaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", - "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true - }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", - "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", - "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/inspect-js" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", - "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "peer": true - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-browser": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", - "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", - "dependencies": { - "bundle-name": "^4.1.0", - "default-browser-id": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser-id": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", - "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/diff": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", - "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "peer": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dotenv": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", - "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==" - }, - "node_modules/emojilib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/emojilib/-/emojilib-2.4.0.tgz", - "integrity": "sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==" - }, - "node_modules/environment": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", - "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/es-abstract": { - "version": "1.23.9", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", - "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.0", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-regex": "^1.2.1", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.0", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.3", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.18" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", - "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.0.3", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.6", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.4", - "safe-array-concat": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.6.0.tgz", - "integrity": "sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==", - "dev": true - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", - "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", - "dev": true, - "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-toolkit": { - "version": "1.34.1", - "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.34.1.tgz", - "integrity": "sha512-OA6cd94fJV9bm8dWhIySkWq4xV+rAQnBZUr2dnpXam0QJ8c+hurLbKA8/QooL9Mx4WCAxvIDsiEkid5KPQ5xgQ==" - }, - "node_modules/esbuild": { - "version": "0.25.2", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", - "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.2", - "@esbuild/android-arm": "0.25.2", - "@esbuild/android-arm64": "0.25.2", - "@esbuild/android-x64": "0.25.2", - "@esbuild/darwin-arm64": "0.25.2", - "@esbuild/darwin-x64": "0.25.2", - "@esbuild/freebsd-arm64": "0.25.2", - "@esbuild/freebsd-x64": "0.25.2", - "@esbuild/linux-arm": "0.25.2", - "@esbuild/linux-arm64": "0.25.2", - "@esbuild/linux-ia32": "0.25.2", - "@esbuild/linux-loong64": "0.25.2", - "@esbuild/linux-mips64el": "0.25.2", - "@esbuild/linux-ppc64": "0.25.2", - "@esbuild/linux-riscv64": "0.25.2", - "@esbuild/linux-s390x": "0.25.2", - "@esbuild/linux-x64": "0.25.2", - "@esbuild/netbsd-arm64": "0.25.2", - "@esbuild/netbsd-x64": "0.25.2", - "@esbuild/openbsd-arm64": "0.25.2", - "@esbuild/openbsd-x64": "0.25.2", - "@esbuild/sunos-x64": "0.25.2", - "@esbuild/win32-arm64": "0.25.2", - "@esbuild/win32-ia32": "0.25.2", - "@esbuild/win32-x64": "0.25.2" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "dev": true, - "peer": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", - "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", - "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", - "dev": true, - "dependencies": { - "@rtsao/scc": "^1.1.0", - "array-includes": "^3.1.8", - "array.prototype.findlastindex": "^1.2.5", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.12.0", - "hasown": "^2.0.2", - "is-core-module": "^2.15.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.8", - "object.groupby": "^1.0.3", - "object.values": "^1.2.0", - "semver": "^6.3.1", - "string.prototype.trimend": "^1.0.8", - "tsconfig-paths": "^3.15.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" - } - }, - "node_modules/eslint-plugin-import/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.37.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", - "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.3", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.2.1", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.9", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.1", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.12", - "string.prototype.repeat": "^1.0.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", - "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", - "dev": true, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.19.tgz", - "integrity": "sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==", - "dev": true, - "peerDependencies": { - "eslint": ">=8.40" - } - }, - "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "peer": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", - "dev": true, - "peer": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "peer": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "peer": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "peer": true, - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "peer": true, - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "dev": true, - "license": "MIT" - }, - "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execa/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/execa/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/execa/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/expect-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", - "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", - "dev": true, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "peer": true - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "peer": true - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==" - }, - "node_modules/figures": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", - "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", - "dependencies": { - "is-unicode-supported": "^2.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "peer": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/file-type": { - "version": "20.4.1", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.4.1.tgz", - "integrity": "sha512-hw9gNZXUfZ02Jo0uafWLaFVPter5/k2rfcrjFJJHX/77xtSDOfJuEFb6oKlFV86FLP1SuyHMW1PSk0U9M5tKkQ==", - "dependencies": { - "@tokenizer/inflate": "^0.2.6", - "strtok3": "^10.2.0", - "token-types": "^6.0.0", - "uint8array-extras": "^1.4.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sindresorhus/file-type?sponsor=1" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "peer": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "peer": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "peer": true - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "dev": true, - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==" - }, - "node_modules/form-data/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/form-data/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/formdata-node": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "engines": { - "node": ">= 12.20" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "peer": true - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", - "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", - "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "peer": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "peer": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "peer": true, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "node_modules/has-bigints": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", - "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", - "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", - "dev": true, - "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "engines": { - "node": "*" - } - }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/husky": { - "version": "9.1.7", - "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", - "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", - "dev": true, - "license": "MIT", - "bin": { - "husky": "bin.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/typicode" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "peer": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "peer": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "peer": true - }, - "node_modules/ink": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ink/-/ink-5.2.0.tgz", - "integrity": "sha512-gHzSBBvsh/1ZYuGi+aKzU7RwnYIr6PSz56or9T90i4DDS99euhN7nYKOMR3OTev0dKIB6Zod3vSapYzqoilQcg==", - "dependencies": { - "@alcalzone/ansi-tokenize": "^0.1.3", - "ansi-escapes": "^7.0.0", - "ansi-styles": "^6.2.1", - "auto-bind": "^5.0.1", - "chalk": "^5.3.0", - "cli-boxes": "^3.0.0", - "cli-cursor": "^4.0.0", - "cli-truncate": "^4.0.0", - "code-excerpt": "^4.0.0", - "es-toolkit": "^1.22.0", - "indent-string": "^5.0.0", - "is-in-ci": "^1.0.0", - "patch-console": "^2.0.0", - "react-reconciler": "^0.29.0", - "scheduler": "^0.23.0", - "signal-exit": "^3.0.7", - "slice-ansi": "^7.1.0", - "stack-utils": "^2.0.6", - "string-width": "^7.2.0", - "type-fest": "^4.27.0", - "widest-line": "^5.0.0", - "wrap-ansi": "^9.0.0", - "ws": "^8.18.0", - "yoga-layout": "~3.2.1" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@types/react": ">=18.0.0", - "react": ">=18.0.0", - "react-devtools-core": "^4.19.1" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react-devtools-core": { - "optional": true - } - } - }, - "node_modules/ink-testing-library": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ink-testing-library/-/ink-testing-library-3.0.0.tgz", - "integrity": "sha512-ItyyoOmcm6yftb7c5mZI2HU22BWzue8PBbO3DStmY8B9xaqfKr7QJONiWOXcwVsOk/6HuVQ0v7N5xhPaR3jycA==", - "dev": true, - "engines": { - "node": ">=14.16" - }, - "peerDependencies": { - "@types/react": ">=18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/ink/node_modules/type-fest": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.40.0.tgz", - "integrity": "sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw==", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/internal-slot": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", - "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", - "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-async-function": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", - "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", - "dev": true, - "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", - "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", - "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-view": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", - "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", - "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", - "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz", - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-in-ci": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-in-ci/-/is-in-ci-1.0.0.tgz", - "integrity": "sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==", - "bin": { - "is-in-ci": "cli.js" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-map": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", - "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", - "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-string": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", - "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", - "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-unicode-supported": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", - "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", - "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-wsl": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", - "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", - "dependencies": { - "is-inside-container": "^1.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/iterator.prototype": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", - "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "get-proto": "^1.0.0", - "has-symbols": "^1.1.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "peer": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "peer": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "peer": true - }, - "node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "peer": true, - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "peer": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lint-staged": { - "version": "15.5.1", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.5.1.tgz", - "integrity": "sha512-6m7u8mue4Xn6wK6gZvSCQwBvMBR36xfY24nF5bMTf2MHDYG6S3yhJuOgdYVw99hsjyDt2d4z168b3naI8+NWtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^5.4.1", - "commander": "^13.1.0", - "debug": "^4.4.0", - "execa": "^8.0.1", - "lilconfig": "^3.1.3", - "listr2": "^8.2.5", - "micromatch": "^4.0.8", - "pidtree": "^0.6.0", - "string-argv": "^0.3.2", - "yaml": "^2.7.0" - }, - "bin": { - "lint-staged": "bin/lint-staged.js" - }, - "engines": { - "node": ">=18.12.0" - }, - "funding": { - "url": "https://opencollective.com/lint-staged" - } - }, - "node_modules/listr2": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.3.2.tgz", - "integrity": "sha512-vsBzcU4oE+v0lj4FhVLzr9dBTv4/fHIa57l+GCwovP8MoFNZJTOhGU8PXd4v2VJCbECAaijBiHntiekFMLvo0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "cli-truncate": "^4.0.0", - "colorette": "^2.0.20", - "eventemitter3": "^5.0.1", - "log-update": "^6.1.0", - "rfdc": "^1.4.1", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "peer": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "peer": true - }, - "node_modules/log-update": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", - "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^7.0.0", - "cli-cursor": "^5.0.0", - "slice-ansi": "^7.1.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/cli-cursor": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/onetime": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-function": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/restore-cursor": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^7.0.0", - "signal-exit": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/log-update/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/log-update/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/loupe": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", - "dev": true - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/marked": { - "version": "15.0.8", - "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.8.tgz", - "integrity": "sha512-rli4l2LyZqpQuRve5C0rkn6pj3hT8EWPC+zkAxFTAJLxRbENfTAhEQq9itrmf1Y81QtAX5D/MYlGlIomNgj9lA==", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/marked-terminal": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/marked-terminal/-/marked-terminal-7.3.0.tgz", - "integrity": "sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==", - "dependencies": { - "ansi-escapes": "^7.0.0", - "ansi-regex": "^6.1.0", - "chalk": "^5.4.1", - "cli-highlight": "^2.1.11", - "cli-table3": "^0.6.5", - "node-emoji": "^2.2.0", - "supports-hyperlinks": "^3.1.0" - }, - "engines": { - "node": ">=16.0.0" - }, - "peerDependencies": { - "marked": ">=1 <16" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/meow": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/meow/-/meow-13.2.0.tgz", - "integrity": "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-function": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-emoji": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-2.2.0.tgz", - "integrity": "sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==", - "dependencies": { - "@sindresorhus/is": "^4.6.0", - "char-regex": "^1.0.2", - "emojilib": "^2.4.0", - "skin-tone": "^2.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", - "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", - "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", - "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.values": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", - "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "peer": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/open/-/open-10.1.1.tgz", - "integrity": "sha512-zy1wx4+P3PfhXSEPJNtZmJXfhkkIaxU1VauWIrDZw1O7uJRDRJtKr9n3Ic4NgbA16KyOxOXO2ng9gYwCdXuSXA==", - "dependencies": { - "default-browser": "^5.2.1", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^3.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/openai": { - "version": "4.94.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.94.0.tgz", - "integrity": "sha512-WVmr9HWcwfouLJ7R3UHd2A93ClezTPuJljQxkCYQAL15Sjyt+FBNoqEz5MHSdH/ebQrVyvRhFyn/bvdqtSPyIA==", - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7" - }, - "bin": { - "openai": "bin/cli" - }, - "peerDependencies": { - "ws": "^8.18.0", - "zod": "^3.23.8" - }, - "peerDependenciesMeta": { - "ws": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/openai/node_modules/@types/node": { - "version": "18.19.86", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.86.tgz", - "integrity": "sha512-fifKayi175wLyKyc5qUfyENhQ1dCNI1UNjp653d8kuYcPQN5JhX3dGuP/XmvPTg/xRBn1VTLpbmi+H/Mr7tLfQ==", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/openai/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "peer": true, - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/own-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", - "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.6", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "peer": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "peer": true, - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "peer": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==" - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", - "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dependencies": { - "parse5": "^6.0.1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" - }, - "node_modules/patch-console": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/patch-console/-/patch-console-2.0.0.tgz", - "integrity": "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true - }, - "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", - "dev": true, - "engines": { - "node": ">= 14.16" - } - }, - "node_modules/peek-readable": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-7.0.0.tgz", - "integrity": "sha512-nri2TO5JE3/mRryik9LlHFT53cgHfRK0Lt0BAZQXku/AW3E6XLt2GaY8siWi7dvW/m1z0ecn+J+bpDa9ZN3IsQ==", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pidtree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", - "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", - "dev": true, - "license": "MIT", - "bin": { - "pidtree": "bin/pidtree.js" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "peer": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, - "node_modules/react-reconciler": { - "version": "0.29.2", - "resolved": "https://registry.npmjs.org/react-reconciler/-/react-reconciler-0.29.2.tgz", - "integrity": "sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg==", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "engines": { - "node": ">=0.10.0" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", - "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", - "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/restore-cursor": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz", - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "dev": true, - "license": "MIT" - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "peer": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rollup": { - "version": "4.40.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.0.tgz", - "integrity": "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==", - "dev": true, - "dependencies": { - "@types/estree": "1.0.7" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.40.0", - "@rollup/rollup-android-arm64": "4.40.0", - "@rollup/rollup-darwin-arm64": "4.40.0", - "@rollup/rollup-darwin-x64": "4.40.0", - "@rollup/rollup-freebsd-arm64": "4.40.0", - "@rollup/rollup-freebsd-x64": "4.40.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", - "@rollup/rollup-linux-arm-musleabihf": "4.40.0", - "@rollup/rollup-linux-arm64-gnu": "4.40.0", - "@rollup/rollup-linux-arm64-musl": "4.40.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", - "@rollup/rollup-linux-riscv64-gnu": "4.40.0", - "@rollup/rollup-linux-riscv64-musl": "4.40.0", - "@rollup/rollup-linux-s390x-gnu": "4.40.0", - "@rollup/rollup-linux-x64-gnu": "4.40.0", - "@rollup/rollup-linux-x64-musl": "4.40.0", - "@rollup/rollup-win32-arm64-msvc": "4.40.0", - "@rollup/rollup-win32-ia32-msvc": "4.40.0", - "@rollup/rollup-win32-x64-msvc": "4.40.0", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-applescript": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", - "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", - "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-proto": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", - "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", - "dev": true, - "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/shell-quote": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.2.tgz", - "integrity": "sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/skin-tone": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/skin-tone/-/skin-tone-2.0.0.tgz", - "integrity": "sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==", - "dependencies": { - "unicode-emoji-modifier-base": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz", - "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==", - "dependencies": { - "ansi-styles": "^6.2.1", - "is-fullwidth-code-point": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz", - "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==", - "dependencies": { - "get-east-asian-width": "^1.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true - }, - "node_modules/std-env": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", - "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", - "dev": true - }, - "node_modules/string-argv": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", - "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6.19" - } - }, - "node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.12", - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", - "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "regexp.prototype.flags": "^1.5.3", - "set-function-name": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.repeat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strtok3": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.2.2.tgz", - "integrity": "sha512-Xt18+h4s7Z8xyZ0tmBoRmzxcop97R4BAh+dXouUDCYn+Em+1P3qpkUfI5ueWLT8ynC5hZ+q4iPEmGG1urvQGBg==", - "dependencies": { - "@tokenizer/token": "^0.3.0", - "peek-readable": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", - "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=14.18" - }, - "funding": { - "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "peer": true - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", - "dev": true - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true - }, - "node_modules/tinypool": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", - "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", - "dev": true, - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, - "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/to-rotated": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-rotated/-/to-rotated-1.0.0.tgz", - "integrity": "sha512-KsEID8AfgUy+pxVRLsWp0VzCa69wxzUDZnzGbyIST/bcgcrMvTYoFBX/QORH4YApoD89EDuUovx4BTdpOn319Q==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/token-types": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.0.0.tgz", - "integrity": "sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==", - "dependencies": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/tr46": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.0.tgz", - "integrity": "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==", - "dev": true, - "dependencies": { - "punycode": "^2.3.1" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", - "dev": true, - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/ts-node/node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true, - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", - "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "peer": true, - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", - "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", - "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", - "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uint8array-extras": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.4.0.tgz", - "integrity": "sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", - "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" - }, - "node_modules/unicode-emoji-modifier-base": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz", - "integrity": "sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "peer": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/use-interval": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/use-interval/-/use-interval-1.4.0.tgz", - "integrity": "sha512-1betIJun2rXKLxa30AFOBZCeZhsBJoJ/3+gkCeYbJ63lAR//EnAb1NjNeFqzgqeM7zQfR76rrCUaA8DvfgoOpA==", - "engines": { - "node": ">=8", - "npm": ">=5" - }, - "peerDependencies": { - "react": ">=16.8.0 || ^17" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true - }, - "node_modules/vite": { - "version": "6.2.6", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.6.tgz", - "integrity": "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw==", - "dev": true, - "dependencies": { - "esbuild": "^0.25.0", - "postcss": "^8.5.3", - "rollup": "^4.30.1" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite-node": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.1.tgz", - "integrity": "sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==", - "dev": true, - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.0", - "es-module-lexer": "^1.6.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/vitest": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.1.tgz", - "integrity": "sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==", - "dev": true, - "dependencies": { - "@vitest/expect": "3.1.1", - "@vitest/mocker": "3.1.1", - "@vitest/pretty-format": "^3.1.1", - "@vitest/runner": "3.1.1", - "@vitest/snapshot": "3.1.1", - "@vitest/spy": "3.1.1", - "@vitest/utils": "3.1.1", - "chai": "^5.2.0", - "debug": "^4.4.0", - "expect-type": "^1.2.0", - "magic-string": "^0.30.17", - "pathe": "^2.0.3", - "std-env": "^3.8.1", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinypool": "^1.0.2", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0", - "vite-node": "3.1.1", - "why-is-node-running": "^2.3.0" - }, - "bin": { - "vitest": "vitest.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.1.1", - "@vitest/ui": "3.1.1", - "happy-dom": "*", - "jsdom": "*" - }, - "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/debug": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { - "optional": true - } - } - }, - "node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", - "engines": { - "node": ">= 14" - } - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", - "dev": true, - "dependencies": { - "tr46": "^5.1.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", - "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", - "dev": true, - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", - "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", - "dev": true, - "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", - "dev": true, - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/widest-line": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", - "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", - "dependencies": { - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "peer": true - }, - "node_modules/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yaml": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", - "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "node_modules/yargs/node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "peer": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoga-layout": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz", - "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==" - }, - "node_modules/zod": { - "version": "3.24.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", - "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} diff --git a/codex-cli/package.json b/codex-cli/package.json index 6834f15694..d66b289502 100644 --- a/codex-cli/package.json +++ b/codex-cli/package.json @@ -22,8 +22,8 @@ "build:dev": "NODE_ENV=development node build.mjs --dev && NODE_OPTIONS=--enable-source-maps node dist/cli-dev.js", "release:readme": "cp ../README.md ./README.md", "release:version": "TS=$(date +%y%m%d%H%M) && sed -E -i'' -e \"s/\\\"0\\.1\\.[0-9]{10}\\\"/\\\"0.1.${TS}\\\"/g\" package.json src/utils/session.ts", - "release:build-and-publish": "npm run build && npm publish", - "release": "npm run release:readme && npm run release:version && npm install && npm run release:build-and-publish", + "release:build-and-publish": "pmpm run build && pmpm publish", + "release": "pnpm run release:readme && pnpm run release:version && pnpm install && pnpm run release:build-and-publish", "prepare": "husky", "pre-commit": "lint-staged" }, @@ -79,14 +79,6 @@ "vitest": "^3.0.9", "whatwg-url": "^14.2.0" }, - "resolutions": { - "braces": "^3.0.3", - "micromatch": "^4.0.8", - "semver": "^7.7.1" - }, - "overrides": { - "punycode": "^2.3.1" - }, "repository": { "type": "git", "url": "https://github.com/openai/codex" diff --git a/codex-cli/src/utils/agent/agent-loop.ts b/codex-cli/src/utils/agent/agent-loop.ts index b9105f64a7..67d775f296 100644 --- a/codex-cli/src/utils/agent/agent-loop.ts +++ b/codex-cli/src/utils/agent/agent-loop.ts @@ -496,7 +496,6 @@ export class AgentLoop { if (this.model.startsWith("o")) { reasoning = { effort: "high" }; if (this.model === "o3" || this.model === "o4-mini") { - // @ts-expect-error waiting for API type update reasoning.summary = "auto"; } } diff --git a/lint-staged.config.mjs b/lint-staged.config.mjs new file mode 100644 index 0000000000..85af6291e6 --- /dev/null +++ b/lint-staged.config.mjs @@ -0,0 +1,4 @@ +export default { + "*.{js,jsx,ts,tsx}": ["pnpm prettier --write", "pnpm eslint --fix"], + "*.{json,md,yml,yaml}": ["pnpm prettier --write"], +}; diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 0e7616fa96..0000000000 --- a/package-lock.json +++ /dev/null @@ -1,359 +0,0 @@ -{ - "name": "codex", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "devDependencies": { - "git-cliff": "^2.8.0", - "prettier": "^3.5.3" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/git-cliff": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/git-cliff/-/git-cliff-2.8.0.tgz", - "integrity": "sha512-iKF5QTXAb9+iVvmu5HpnMPWYw7fs74xkpAaRbSf29+dZaMTTNRIUST/y+Ir2S1bDUWWJNjXlwT9ZT62JuYLQnA==", - "dev": true, - "license": "MIT OR Apache-2.0", - "dependencies": { - "execa": "^8.0.1" - }, - "bin": { - "git-cliff": "lib/cli/cli.js" - }, - "engines": { - "node": ">=18.19 || >=20.6 || >=21" - }, - "optionalDependencies": { - "git-cliff-darwin-arm64": "2.8.0", - "git-cliff-darwin-x64": "2.8.0", - "git-cliff-linux-arm64": "2.8.0", - "git-cliff-linux-x64": "2.8.0", - "git-cliff-windows-arm64": "2.8.0", - "git-cliff-windows-x64": "2.8.0" - } - }, - "node_modules/git-cliff-darwin-arm64": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/git-cliff-darwin-arm64/-/git-cliff-darwin-arm64-2.8.0.tgz", - "integrity": "sha512-rurUV2d1Z2n+c2+wUrO0gZaFb3c1G+ej0bPfKTPfde/CblxiysMkh+4dz23NrVbc8IlS5rSYv/JFGVaVSBNJRw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/git-cliff-darwin-x64": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/git-cliff-darwin-x64/-/git-cliff-darwin-x64-2.8.0.tgz", - "integrity": "sha512-Wtj+FGWZBWmeYUAGlkfz7QPz4+VVxxDPMhQ/7iwKVA3iryIX0slGfzYpqMurEFnTAMr0r+4IU3Q4O/ib7iUscg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/git-cliff-linux-arm64": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/git-cliff-linux-arm64/-/git-cliff-linux-arm64-2.8.0.tgz", - "integrity": "sha512-k4RdfMdORXyefznWlQb+7wDgo7XgQF9qg8hJC34bwyJK2sODirrGau3uTx1/9Fi37g+pAOM7wM+LYppHCTZ2bQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/git-cliff-linux-x64": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/git-cliff-linux-x64/-/git-cliff-linux-x64-2.8.0.tgz", - "integrity": "sha512-FcWX4GHgodYrQlZR03fzooanStgR03JNWvyaMQB1asplQ18nlziK2UyA+PESCIxOQmeLXauqoCApfzmdtp5myg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/git-cliff-windows-arm64": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/git-cliff-windows-arm64/-/git-cliff-windows-arm64-2.8.0.tgz", - "integrity": "sha512-GJSrqmBVTbMtBJI3/YCDxLviZZDgYgnKqYgquBk2u2AELAnnuWFnVFQ7ZEBUqgFF2UJu9EdV2Nv6MV8d/wnP0g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/git-cliff-windows-x64": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/git-cliff-windows-x64/-/git-cliff-windows-x64-2.8.0.tgz", - "integrity": "sha512-8jl0YMXPYjUmVygUEeQ4wf1zte3Rv8LPq1sIklUKl80XE4g2Gm/8EIWbKpUPLQH6IncRwepY6VuMgpVpPXbwNw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/prettier": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", - "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - } - } -} diff --git a/package.json b/package.json index d967dc3435..b6ce8ee443 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,34 @@ { + "name": "codex-monorepo", + "private": true, "description": "Tools for repo-wide maintenance.", "scripts": { - "release": "cd codex-cli && npm run release", + "release": "pnpm --filter @openai/codex run release", "format": "prettier --check *.json *.md .github/workflows/*.yml", "format:fix": "prettier --write *.json *.md .github/workflows/*.yml", + "build": "pnpm --filter @openai/codex run build", + "test": "pnpm --filter @openai/codex run test", + "lint": "pnpm --filter @openai/codex run lint", + "typecheck": "pnpm --filter @openai/codex run typecheck", + "prepare": "husky", "changelog": "git-cliff --config cliff.toml --output CHANGELOG.ignore.md $LAST_RELEASE_TAG..HEAD" }, "devDependencies": { "git-cliff": "^2.8.0", + "husky": "^9.1.7", "prettier": "^3.5.3" - } + }, + "resolutions": { + "braces": "^3.0.3", + "micromatch": "^4.0.8", + "semver": "^7.7.1" + }, + "overrides": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=22", + "pnpm": ">=9.0.0" + }, + "packageManager": "pnpm@10.8.1" } diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000000..6aaf86b654 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,5 @@ +packages: + - 'codex-cli' + - 'docs' + # For future packages + - 'packages/*' From bec394705817b7cb0718a5d76f33e6937ee7bb20 Mon Sep 17 00:00:00 2001 From: BadPirate Date: Fri, 18 Apr 2025 16:48:07 -0700 Subject: [PATCH 07/82] Fix #371 Allow multiple containers on same machine (#373) - Docker container name based on work directory - Centralize container removal logic - Improve quoting for command arguments - Ensure workdir is always set and normalized Resolves: #371 Signed-off-by: BadPirate Signed-off-by: BadPirate --- codex-cli/scripts/run_in_container.sh | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/codex-cli/scripts/run_in_container.sh b/codex-cli/scripts/run_in_container.sh index 2e6978be2f..c95c57aead 100755 --- a/codex-cli/scripts/run_in_container.sh +++ b/codex-cli/scripts/run_in_container.sh @@ -23,6 +23,16 @@ fi WORK_DIR=$(realpath "$WORK_DIR") +# Generate a unique container name based on the normalized work directory +CONTAINER_NAME="codex_$(echo "$WORK_DIR" | sed 's/\//_/g' | sed 's/[^a-zA-Z0-9_-]//g')" + +# Define cleanup to remove the container on script exit, ensuring no leftover containers +cleanup() { + docker rm -f "$CONTAINER_NAME" >/dev/null 2>&1 || true +} +# Trap EXIT to invoke cleanup regardless of how the script terminates +trap cleanup EXIT + # Ensure a command is provided. if [ "$#" -eq 0 ]; then echo "Usage: $0 [--work_dir directory] \"COMMAND\"" @@ -35,11 +45,11 @@ if [ -z "$WORK_DIR" ]; then exit 1 fi -# Remove any existing container named 'codex'. -docker rm -f codex 2>/dev/null || true +# Kill any existing container for the working directory using cleanup(), centralizing removal logic. +cleanup # Run the container with the specified directory mounted at the same path inside the container. -docker run --name codex -d \ +docker run --name "$CONTAINER_NAME" -d \ -e OPENAI_API_KEY \ --cap-add=NET_ADMIN \ --cap-add=NET_RAW \ @@ -48,7 +58,7 @@ docker run --name codex -d \ sleep infinity # Initialize the firewall inside the container. -docker exec codex bash -c "sudo /usr/local/bin/init_firewall.sh" +docker exec "$CONTAINER_NAME" bash -c "sudo /usr/local/bin/init_firewall.sh" # Execute the provided command in the container, ensuring it runs in the work directory. # We use a parameterized bash command to safely handle the command and directory. @@ -57,4 +67,4 @@ quoted_args="" for arg in "$@"; do quoted_args+=" $(printf '%q' "$arg")" done -docker exec -it codex bash -c "cd \"/app$WORK_DIR\" && codex --full-auto ${quoted_args}" +docker exec -it "$CONTAINER_NAME" bash -c "cd \"/app$WORK_DIR\" && codex --full-auto ${quoted_args}" From d69a17ac49a815f7cb494fe50c0bda015a4d7fba Mon Sep 17 00:00:00 2001 From: Abdelrhman Kamal Mahmoud Ali Slim Date: Fri, 18 Apr 2025 16:55:49 -0700 Subject: [PATCH 08/82] =?UTF-8?q?Fix:=20Change=20file=20name=20to=20start?= =?UTF-8?q?=20with=20small=20letter=20instead=20of=20captial=20l=E2=80=A6?= =?UTF-8?q?=20(#356)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/select-input/{Indicator.tsx => indicator.tsx} | 0 codex-cli/src/components/select-input/{Item.tsx => item.tsx} | 0 codex-cli/src/components/select-input/select-input.tsx | 4 ++-- 3 files changed, 2 insertions(+), 2 deletions(-) rename codex-cli/src/components/select-input/{Indicator.tsx => indicator.tsx} (100%) rename codex-cli/src/components/select-input/{Item.tsx => item.tsx} (100%) diff --git a/codex-cli/src/components/select-input/Indicator.tsx b/codex-cli/src/components/select-input/indicator.tsx similarity index 100% rename from codex-cli/src/components/select-input/Indicator.tsx rename to codex-cli/src/components/select-input/indicator.tsx diff --git a/codex-cli/src/components/select-input/Item.tsx b/codex-cli/src/components/select-input/item.tsx similarity index 100% rename from codex-cli/src/components/select-input/Item.tsx rename to codex-cli/src/components/select-input/item.tsx diff --git a/codex-cli/src/components/select-input/select-input.tsx b/codex-cli/src/components/select-input/select-input.tsx index 264f3f9e1c..701c655d37 100644 --- a/codex-cli/src/components/select-input/select-input.tsx +++ b/codex-cli/src/components/select-input/select-input.tsx @@ -1,5 +1,5 @@ -import Indicator, { type Props as IndicatorProps } from "./Indicator.js"; -import ItemComponent, { type Props as ItemProps } from "./Item.js"; +import Indicator, { type Props as IndicatorProps } from "./indicator.js"; +import ItemComponent, { type Props as ItemProps } from "./item.js"; import isEqual from "fast-deep-equal"; import { Box, useInput } from "ink"; import React, { From d61da89ed3e635d70e47c795ce29311ba6554696 Mon Sep 17 00:00:00 2001 From: Benny Yen Date: Sat, 19 Apr 2025 08:00:45 +0800 Subject: [PATCH 09/82] feat: notify when a newer version is available (#333) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Summary** This change introduces a new startup check that notifies users if a newer `@openai/codex` version is available. To avoid spamming, it writes a small state file recording the last check time and will only re‑check once every 24 hours. **What’s Changed** - **New file** `src/utils/check-updates.ts` - Runs `npm outdated --global @openai/codex` - Reads/writes `codex-state.json` under `CONFIG_DIR` - Limits checks to once per day (`UPDATE_CHECK_FREQUENCY = 24h`) - Uses `boxen` for a styled alert and `which` to locate the npm binary - **Hooked into** `src/cli.tsx` entrypoint: ```ts import { checkForUpdates } from "./utils/check-updates"; // … // after loading config await checkForUpdates().catch(); ``` - **Dependencies** - Added `boxen@^8.0.1`, `which@^5.0.0`, `@types/which@^3.0.4` - **Tests** - Vitest suite under `tests/check-updates.test.ts` - Snapshot in `__snapshots__/check-updates.test.ts.snap` **Motivation** Addresses issue #244. Users running a stale global install will now see a friendly reminder—at most once per day—to upgrade and enjoy the latest features. **Test Plan** - `getNPMCommandPath()` resolves npm correctly - `checkOutdated()` parses `npm outdated` JSON - State file prevents repeat alerts within 24h - Boxen snapshot matches expected output - No console output when state indicates a recent check **Related Issue** try resolves #244 **Preview** Prompt a pnpm‑style alert when outdated ![outdated‑alert](https://github.com/user-attachments/assets/294dad45-d858-45d1-bf34-55e672ab883a) Let me know if you’d tweak any of the messaging, throttle frequency, placement in the startup flow, or anything else. --------- Co-authored-by: Thibault Sottiaux --- codex-cli/package.json | 5 +- codex-cli/src/cli.tsx | 5 + codex-cli/src/utils/check-updates.ts | 143 ++++++++++++++++++ .../__snapshots__/check-updates.test.ts.snap | 12 ++ codex-cli/tests/check-updates.test.ts | 112 ++++++++++++++ codex-cli/tsconfig.json | 2 +- 6 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 codex-cli/src/utils/check-updates.ts create mode 100644 codex-cli/tests/__snapshots__/check-updates.test.ts.snap create mode 100644 codex-cli/tests/check-updates.test.ts diff --git a/codex-cli/package.json b/codex-cli/package.json index d66b289502..cc1714f42d 100644 --- a/codex-cli/package.json +++ b/codex-cli/package.json @@ -62,8 +62,10 @@ "@types/marked-terminal": "^6.1.1", "@types/react": "^18.0.32", "@types/shell-quote": "^1.7.5", + "@types/which": "^3.0.4", "@typescript-eslint/eslint-plugin": "^7.18.0", "@typescript-eslint/parser": "^7.18.0", + "boxen": "^8.0.1", "esbuild": "^0.25.2", "eslint-plugin-import": "^2.31.0", "eslint-plugin-react": "^7.32.2", @@ -77,7 +79,8 @@ "ts-node": "^10.9.1", "typescript": "^5.0.3", "vitest": "^3.0.9", - "whatwg-url": "^14.2.0" + "whatwg-url": "^14.2.0", + "which": "^5.0.0" }, "repository": { "type": "git", diff --git a/codex-cli/src/cli.tsx b/codex-cli/src/cli.tsx index fdc0ea77c6..2f2b8c092e 100644 --- a/codex-cli/src/cli.tsx +++ b/codex-cli/src/cli.tsx @@ -17,6 +17,7 @@ import { AgentLoop } from "./utils/agent/agent-loop"; import { initLogger } from "./utils/agent/log"; import { ReviewDecision } from "./utils/agent/review"; import { AutoApprovalMode } from "./utils/auto-approval-mode"; +import { checkForUpdates } from "./utils/check-updates"; import { loadConfig, PRETTY_PRINT, @@ -252,6 +253,10 @@ config = { notify: Boolean(cli.flags.notify), }; +// Check for updates after loading config +// This is important because we write state file in the config dir +await checkForUpdates().catch(); + if (!(await isModelSupportedForResponses(config.model))) { // eslint-disable-next-line no-console console.error( diff --git a/codex-cli/src/utils/check-updates.ts b/codex-cli/src/utils/check-updates.ts new file mode 100644 index 0000000000..b3d6f85a29 --- /dev/null +++ b/codex-cli/src/utils/check-updates.ts @@ -0,0 +1,143 @@ +import { CONFIG_DIR } from "./config"; +import boxen from "boxen"; +import chalk from "chalk"; +import * as cp from "node:child_process"; +import { readFile, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import which from "which"; + +interface UpdateCheckState { + lastUpdateCheck?: string; +} + +interface PackageInfo { + current: string; + wanted: string; + latest: string; + dependent: string; + location: string; +} + +interface UpdateCheckInfo { + currentVersion: string; + latestVersion: string; +} + +const UPDATE_CHECK_FREQUENCY = 1000 * 60 * 60 * 24; // 1 day + +export async function getNPMCommandPath(): Promise { + try { + return await which(process.platform === "win32" ? "npm.cmd" : "npm"); + } catch { + return undefined; + } +} + +export async function checkOutdated( + npmCommandPath: string, +): Promise { + return new Promise((resolve, _reject) => { + // TODO: support local installation + // Right now we're using "--global", which only checks global packages. + // But codex might be installed locally — we should check the local version first, + // and only fall back to the global one if needed. + const args = ["outdated", "--global", "--json", "--", "@openai/codex"]; + // corepack npm wrapper would automatically update package.json. disable that behavior. + // COREPACK_ENABLE_AUTO_PIN disables the package.json overwrite, and + // COREPACK_ENABLE_PROJECT_SPEC makes the npm view command succeed + // even if packageManager specified a package manager other than npm. + const env = { + ...process.env, + COREPACK_ENABLE_AUTO_PIN: "0", + COREPACK_ENABLE_PROJECT_SPEC: "0", + }; + let options: cp.ExecFileOptions = { env }; + let commandPath = npmCommandPath; + if (process.platform === "win32") { + options = { ...options, shell: true }; + commandPath = `"${npmCommandPath}"`; + } + cp.execFile(commandPath, args, options, async (_error, stdout) => { + try { + const { name: packageName } = await import("../../package.json"); + const content: Record = JSON.parse(stdout); + if (!content[packageName]) { + // package not installed or not outdated + resolve(undefined); + return; + } + + const currentVersion = content[packageName].current; + const latestVersion = content[packageName].latest; + + resolve({ currentVersion, latestVersion }); + return; + } catch { + // ignore + } + resolve(undefined); + }); + }); +} + +export async function checkForUpdates(): Promise { + const stateFile = join(CONFIG_DIR, "update-check.json"); + let state: UpdateCheckState | undefined; + try { + state = JSON.parse(await readFile(stateFile, "utf8")); + } catch { + // ignore + } + + if ( + state?.lastUpdateCheck && + Date.now() - new Date(state.lastUpdateCheck).valueOf() < + UPDATE_CHECK_FREQUENCY + ) { + return; + } + + const npmCommandPath = await getNPMCommandPath(); + if (!npmCommandPath) { + return; + } + + const packageInfo = await checkOutdated(npmCommandPath); + + await writeState(stateFile, { + ...state, + lastUpdateCheck: new Date().toUTCString(), + }); + + if (!packageInfo) { + return; + } + + const updateMessage = `To update, run: ${chalk.cyan( + "npm install -g @openai/codex", + )} to update.`; + + const box = boxen( + `\ +Update available! ${chalk.red(packageInfo.currentVersion)} → ${chalk.green( + packageInfo.latestVersion, + )}. +${updateMessage}`, + { + padding: 1, + margin: 1, + align: "center", + borderColor: "yellow", + borderStyle: "round", + }, + ); + + // eslint-disable-next-line no-console + console.log(box); +} + +async function writeState(stateFilePath: string, state: UpdateCheckState) { + await writeFile(stateFilePath, JSON.stringify(state, null, 2), { + encoding: "utf8", + }); +} diff --git a/codex-cli/tests/__snapshots__/check-updates.test.ts.snap b/codex-cli/tests/__snapshots__/check-updates.test.ts.snap new file mode 100644 index 0000000000..2c1631fb09 --- /dev/null +++ b/codex-cli/tests/__snapshots__/check-updates.test.ts.snap @@ -0,0 +1,12 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Check for updates > should outputs the update message when package is outdated 1`] = ` +" + ╭─────────────────────────────────────────────────────────────╮ + │ │ + │ Update available! 1.0.0 → 2.0.0. │ + │ To update, run: npm install -g @openai/codex to update. │ + │ │ + ╰─────────────────────────────────────────────────────────────╯ +" +`; diff --git a/codex-cli/tests/check-updates.test.ts b/codex-cli/tests/check-updates.test.ts new file mode 100644 index 0000000000..a77789681d --- /dev/null +++ b/codex-cli/tests/check-updates.test.ts @@ -0,0 +1,112 @@ +import { describe, it, expect, vi } from "vitest"; +import { + checkForUpdates, + checkOutdated, + getNPMCommandPath, +} from "../src/utils/check-updates.js"; +import { execFile } from "node:child_process"; +import { join } from "node:path"; +import { CONFIG_DIR } from "src/utils/config.js"; +import { beforeEach } from "node:test"; + +vi.mock("which", () => ({ + default: vi.fn(() => "/usr/local/bin/npm"), +})); + +vi.mock("child_process", () => ({ + execFile: vi.fn((_cmd, _args, _opts, callback) => { + const stdout = JSON.stringify({ + "@openai/codex": { + current: "1.0.0", + latest: "2.0.0", + }, + }); + callback?.(null, stdout, ""); + return {} as any; + }), +})); + +let memfs: Record = {}; + +vi.mock("node:fs/promises", async (importOriginal) => ({ + ...(await importOriginal()), + readFile: async (path: string) => { + if (memfs[path] === undefined) { + throw new Error("ENOENT"); + } + return memfs[path]; + }, +})); + +beforeEach(() => { + memfs = {}; // reset in‑memory store +}); + +describe("Check for updates", () => { + it("should return the path to npm", async () => { + const npmPath = await getNPMCommandPath(); + expect(npmPath).toBeDefined(); + }); + + it("should return undefined if npm is not found", async () => { + vi.mocked(await import("which")).default.mockImplementationOnce(() => { + throw new Error("not found"); + }); + + const npmPath = await getNPMCommandPath(); + expect(npmPath).toBeUndefined(); + }); + + it("should return the return value when package is outdated", async () => { + const npmPath = await getNPMCommandPath(); + + const info = await checkOutdated(npmPath!); + expect(info).toStrictEqual({ + currentVersion: "1.0.0", + latestVersion: "2.0.0", + }); + }); + + it("should return undefined when package is not outdated", async () => { + const npmPath = await getNPMCommandPath(); + vi.mocked(execFile).mockImplementationOnce( + (_cmd, _args, _opts, callback) => { + // Simulate the case where the package is not outdated, returning an empty object + const stdout = JSON.stringify({}); + callback?.(null, stdout, ""); + return {} as any; + }, + ); + + const info = await checkOutdated(npmPath!); + expect(info).toBeUndefined(); + }); + + it("should outputs the update message when package is outdated", async () => { + const codexStatePath = join(CONFIG_DIR, "update-check.json"); + // Use a fixed early date far in the past to ensure it's always at least 1 day before now + memfs[codexStatePath] = JSON.stringify({ + lastUpdateCheck: new Date("2000-01-01T00:00:00Z").toUTCString(), + }); + await checkForUpdates(); + // Spy on console.log to capture output + const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + await checkForUpdates(); + expect(logSpy).toHaveBeenCalled(); + // The last call should be the boxen message + const lastCallArg = logSpy.mock.calls.at(-1)?.[0]; + expect(lastCallArg).toMatchSnapshot(); + }); + + it("should not output the update message when package is not outdated", async () => { + const codexStatePath = join(CONFIG_DIR, "update-check.json"); + memfs[codexStatePath] = JSON.stringify({ + lastUpdateCheck: new Date().toUTCString(), + }); + await checkForUpdates(); + // Spy on console.log to capture output + const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + await checkForUpdates(); + expect(logSpy).not.toHaveBeenCalled(); + }); +}); diff --git a/codex-cli/tsconfig.json b/codex-cli/tsconfig.json index d1dacc9149..e441160f74 100644 --- a/codex-cli/tsconfig.json +++ b/codex-cli/tsconfig.json @@ -11,7 +11,7 @@ ], "types": ["node"], "baseUrl": "./", - "resolveJsonModule": false, // ESM doesn't yet support JSON modules. + "resolveJsonModule": true, // ESM doesn't yet support JSON modules. "jsx": "react", "declaration": true, "newLine": "lf", From 9d77d791e36c115338ad3550caea26b238e33c50 Mon Sep 17 00:00:00 2001 From: Thibault Sottiaux Date: Fri, 18 Apr 2025 17:01:11 -0700 Subject: [PATCH 10/82] fix: include pnpm lock file (#377) Signed-off-by: Thibault Sottiaux --- .gitignore | 3 +- PNPM_MIGRATION.md => PNPM.md | 0 pnpm-lock.yaml | 4879 ++++++++++++++++++++++++++++++++++ 3 files changed, 4880 insertions(+), 2 deletions(-) rename PNPM_MIGRATION.md => PNPM.md (100%) create mode 100644 pnpm-lock.yaml diff --git a/.gitignore b/.gitignore index 820100ff4d..be4139a3ee 100644 --- a/.gitignore +++ b/.gitignore @@ -67,9 +67,8 @@ Icon? # Unwanted package managers .yarn/ yarn.lock -pnpm-lock.yaml # release package.json-e session.ts-e -CHANGELOG.ignore.md \ No newline at end of file +CHANGELOG.ignore.md diff --git a/PNPM_MIGRATION.md b/PNPM.md similarity index 100% rename from PNPM_MIGRATION.md rename to PNPM.md diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000000..db130a4b75 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,4879 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +overrides: + braces: ^3.0.3 + micromatch: ^4.0.8 + semver: ^7.7.1 + +importers: + + .: + devDependencies: + git-cliff: + specifier: ^2.8.0 + version: 2.8.0 + husky: + specifier: ^9.1.7 + version: 9.1.7 + prettier: + specifier: ^3.5.3 + version: 3.5.3 + + codex-cli: + dependencies: + '@inkjs/ui': + specifier: ^2.0.0 + version: 2.0.0(ink@5.2.0(@types/react@18.3.20)(react@18.3.1)) + chalk: + specifier: ^5.2.0 + version: 5.4.1 + diff: + specifier: ^7.0.0 + version: 7.0.0 + dotenv: + specifier: ^16.1.4 + version: 16.5.0 + fast-deep-equal: + specifier: ^3.1.3 + version: 3.1.3 + figures: + specifier: ^6.1.0 + version: 6.1.0 + file-type: + specifier: ^20.1.0 + version: 20.4.1 + ink: + specifier: ^5.2.0 + version: 5.2.0(@types/react@18.3.20)(react@18.3.1) + js-yaml: + specifier: ^4.1.0 + version: 4.1.0 + marked: + specifier: ^15.0.7 + version: 15.0.8 + marked-terminal: + specifier: ^7.3.0 + version: 7.3.0(marked@15.0.8) + meow: + specifier: ^13.2.0 + version: 13.2.0 + open: + specifier: ^10.1.0 + version: 10.1.1 + openai: + specifier: ^4.89.0 + version: 4.95.1(ws@8.18.1)(zod@3.24.3) + react: + specifier: ^18.2.0 + version: 18.3.1 + shell-quote: + specifier: ^1.8.2 + version: 1.8.2 + strip-ansi: + specifier: ^7.1.0 + version: 7.1.0 + to-rotated: + specifier: ^1.0.0 + version: 1.0.0 + use-interval: + specifier: 1.4.0 + version: 1.4.0(react@18.3.1) + zod: + specifier: ^3.24.3 + version: 3.24.3 + devDependencies: + '@eslint/js': + specifier: ^9.22.0 + version: 9.25.0 + '@types/diff': + specifier: ^7.0.2 + version: 7.0.2 + '@types/js-yaml': + specifier: ^4.0.9 + version: 4.0.9 + '@types/marked-terminal': + specifier: ^6.1.1 + version: 6.1.1 + '@types/react': + specifier: ^18.0.32 + version: 18.3.20 + '@types/shell-quote': + specifier: ^1.7.5 + version: 1.7.5 + '@typescript-eslint/eslint-plugin': + specifier: ^7.18.0 + version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/parser': + specifier: ^7.18.0 + version: 7.18.0(eslint@8.57.1)(typescript@5.8.3) + esbuild: + specifier: ^0.25.2 + version: 0.25.2 + eslint-plugin-import: + specifier: ^2.31.0 + version: 2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1) + eslint-plugin-react: + specifier: ^7.32.2 + version: 7.37.5(eslint@8.57.1) + eslint-plugin-react-hooks: + specifier: ^4.6.0 + version: 4.6.2(eslint@8.57.1) + eslint-plugin-react-refresh: + specifier: ^0.4.19 + version: 0.4.19(eslint@8.57.1) + husky: + specifier: ^9.1.7 + version: 9.1.7 + ink-testing-library: + specifier: ^3.0.0 + version: 3.0.0(@types/react@18.3.20) + lint-staged: + specifier: ^15.5.1 + version: 15.5.1 + prettier: + specifier: ^2.8.7 + version: 2.8.8 + punycode: + specifier: ^2.3.1 + version: 2.3.1 + ts-node: + specifier: ^10.9.1 + version: 10.9.2(@types/node@22.14.1)(typescript@5.8.3) + typescript: + specifier: ^5.0.3 + version: 5.8.3 + vitest: + specifier: ^3.0.9 + version: 3.1.1(@types/node@22.14.1)(yaml@2.7.1) + whatwg-url: + specifier: ^14.2.0 + version: 14.2.0 + +packages: + + '@alcalzone/ansi-tokenize@0.1.3': + resolution: {integrity: sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw==} + engines: {node: '>=14.13.1'} + + '@colors/colors@1.5.0': + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + + '@esbuild/aix-ppc64@0.25.2': + resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.2': + resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.2': + resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.2': + resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.2': + resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.2': + resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.2': + resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.2': + resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.2': + resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.2': + resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.2': + resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.2': + resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.2': + resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.2': + resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.2': + resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.2': + resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.2': + resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.2': + resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.2': + resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.2': + resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.2': + resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.2': + resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.2': + resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.2': + resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.2': + resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.6.1': + resolution: {integrity: sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@9.25.0': + resolution: {integrity: sha512-iWhsUS8Wgxz9AXNfvfOPFSW4VfMXdVhp1hjkZVhXCrpgh/aLcc45rX6MPu+tIVUWDw0HfNwth7O28M1xDxNf9w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@inkjs/ui@2.0.0': + resolution: {integrity: sha512-5+8fJmwtF9UvikzLfph9sA+LS+l37Ij/szQltkuXLOAXwNkBX9innfzh4pLGXIB59vKEQUtc6D4qGvhD7h3pAg==} + engines: {node: '>=18'} + peerDependencies: + ink: '>=5' + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@rollup/rollup-android-arm-eabi@4.40.0': + resolution: {integrity: sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.40.0': + resolution: {integrity: sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.40.0': + resolution: {integrity: sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.40.0': + resolution: {integrity: sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.40.0': + resolution: {integrity: sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.40.0': + resolution: {integrity: sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.40.0': + resolution: {integrity: sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.40.0': + resolution: {integrity: sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.40.0': + resolution: {integrity: sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.40.0': + resolution: {integrity: sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.40.0': + resolution: {integrity: sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.40.0': + resolution: {integrity: sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.40.0': + resolution: {integrity: sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.40.0': + resolution: {integrity: sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.40.0': + resolution: {integrity: sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.40.0': + resolution: {integrity: sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.40.0': + resolution: {integrity: sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-win32-arm64-msvc@4.40.0': + resolution: {integrity: sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.40.0': + resolution: {integrity: sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.40.0': + resolution: {integrity: sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==} + cpu: [x64] + os: [win32] + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@sindresorhus/is@4.6.0': + resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} + engines: {node: '>=10'} + + '@tokenizer/inflate@0.2.7': + resolution: {integrity: sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==} + engines: {node: '>=18'} + + '@tokenizer/token@0.3.0': + resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} + + '@tsconfig/node10@1.0.11': + resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + + '@tsconfig/node12@1.0.11': + resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} + + '@tsconfig/node14@1.0.3': + resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} + + '@tsconfig/node16@1.0.4': + resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} + + '@types/cardinal@2.1.1': + resolution: {integrity: sha512-/xCVwg8lWvahHsV2wXZt4i64H1sdL+sN1Uoq7fAc8/FA6uYHjuIveDwPwvGUYp4VZiv85dVl6J/Bum3NDAOm8g==} + + '@types/diff@7.0.2': + resolution: {integrity: sha512-JSWRMozjFKsGlEjiiKajUjIJVKuKdE3oVy2DNtK+fUo8q82nhFZ2CPQwicAIkXrofahDXrWJ7mjelvZphMS98Q==} + + '@types/estree@1.0.7': + resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} + + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/marked-terminal@6.1.1': + resolution: {integrity: sha512-DfoUqkmFDCED7eBY9vFUhJ9fW8oZcMAK5EwRDQ9drjTbpQa+DnBTQQCwWhTFVf4WsZ6yYcJTI8D91wxTWXRZZQ==} + + '@types/node-fetch@2.6.12': + resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} + + '@types/node@18.19.86': + resolution: {integrity: sha512-fifKayi175wLyKyc5qUfyENhQ1dCNI1UNjp653d8kuYcPQN5JhX3dGuP/XmvPTg/xRBn1VTLpbmi+H/Mr7tLfQ==} + + '@types/node@22.14.1': + resolution: {integrity: sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==} + + '@types/prop-types@15.7.14': + resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} + + '@types/react@18.3.20': + resolution: {integrity: sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==} + + '@types/shell-quote@1.7.5': + resolution: {integrity: sha512-+UE8GAGRPbJVQDdxi16dgadcBfQ+KG2vgZhV1+3A1XmHbmwcdwhCUwIdy+d3pAGrbvgRoVSjeI9vOWyq376Yzw==} + + '@typescript-eslint/eslint-plugin@7.18.0': + resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + '@typescript-eslint/parser': ^7.0.0 + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@7.18.0': + resolution: {integrity: sha512-4Z+L8I2OqhZV8qA132M4wNL30ypZGYOQVBfMgxDH/K5UX0PNqTu1c6za9ST5r9+tavvHiTWmBnKzpCJ/GlVFtg==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@7.18.0': + resolution: {integrity: sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/type-utils@7.18.0': + resolution: {integrity: sha512-XL0FJXuCLaDuX2sYqZUUSOJ2sG5/i1AAze+axqmLnSkNEVMVYLF+cbwlB2w8D1tinFuSikHmFta+P+HOofrLeA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@7.18.0': + resolution: {integrity: sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@typescript-eslint/typescript-estree@7.18.0': + resolution: {integrity: sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@7.18.0': + resolution: {integrity: sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==} + engines: {node: ^18.18.0 || >=20.0.0} + peerDependencies: + eslint: ^8.56.0 + + '@typescript-eslint/visitor-keys@7.18.0': + resolution: {integrity: sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==} + engines: {node: ^18.18.0 || >=20.0.0} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@vitest/expect@3.1.1': + resolution: {integrity: sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==} + + '@vitest/mocker@3.1.1': + resolution: {integrity: sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.1.1': + resolution: {integrity: sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==} + + '@vitest/runner@3.1.1': + resolution: {integrity: sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==} + + '@vitest/snapshot@3.1.1': + resolution: {integrity: sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==} + + '@vitest/spy@3.1.1': + resolution: {integrity: sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==} + + '@vitest/utils@3.1.1': + resolution: {integrity: sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.3.4: + resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + engines: {node: '>=0.4.0'} + + acorn@8.14.1: + resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agentkeepalive@4.6.0: + resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} + engines: {node: '>= 8.0.0'} + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-escapes@7.0.0: + resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} + engines: {node: '>=18'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.8: + resolution: {integrity: sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + auto-bind@5.0.1: + resolution: {integrity: sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + bundle-name@4.1.0: + resolution: {integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==} + engines: {node: '>=18'} + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + chai@5.2.0: + resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} + engines: {node: '>=12'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + + cli-boxes@3.0.0: + resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==} + engines: {node: '>=10'} + + cli-cursor@4.0.0: + resolution: {integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-highlight@2.1.11: + resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + + cli-spinners@3.2.0: + resolution: {integrity: sha512-pXftdQloMZzjCr3pCTIRniDcys6dDzgpgVhAHHk6TKBDbRuP1MkuetTF5KSv4YUutbOPa7+7ZrAJ2kVtbMqyXA==} + engines: {node: '>=18.20'} + + cli-table3@0.6.5: + resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} + engines: {node: 10.* || >= 12.*} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + code-excerpt@4.0.0: + resolution: {integrity: sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-to-spaces@2.0.1: + resolution: {integrity: sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + default-browser-id@5.0.0: + resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} + engines: {node: '>=18'} + + default-browser@5.2.1: + resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} + engines: {node: '>=18'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-lazy-prop@3.0.0: + resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==} + engines: {node: '>=12'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + diff@4.0.2: + resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + engines: {node: '>=0.3.1'} + + diff@7.0.0: + resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==} + engines: {node: '>=0.3.1'} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dotenv@16.5.0: + resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} + engines: {node: '>=12'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emojilib@2.4.0: + resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + es-abstract@1.23.9: + resolution: {integrity: sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.2.1: + resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.6.0: + resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + es-toolkit@1.35.0: + resolution: {integrity: sha512-kVHyrRoC0eLc1hWJ6npG8nNFtOG+nWfcMI+XE0RaFO0gxd6Ions8r0O/U64QyZgY7IeidUnS5oZlRZYUgMGCAg==} + + esbuild@0.25.2: + resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-module-utils@2.12.0: + resolution: {integrity: sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.31.0: + resolution: {integrity: sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-react-hooks@4.6.2: + resolution: {integrity: sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 + + eslint-plugin-react-refresh@0.4.19: + resolution: {integrity: sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==} + peerDependencies: + eslint: '>=8.40' + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + expect-type@1.2.1: + resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} + engines: {node: '>=12.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fdir@6.4.3: + resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + file-type@20.4.1: + resolution: {integrity: sha512-hw9gNZXUfZ02Jo0uafWLaFVPter5/k2rfcrjFJJHX/77xtSDOfJuEFb6oKlFV86FLP1SuyHMW1PSk0U9M5tKkQ==} + engines: {node: '>=18'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@3.2.0: + resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} + engines: {node: ^10.12.0 || >=12.0.0} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + form-data-encoder@1.7.2: + resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} + + form-data@4.0.2: + resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + engines: {node: '>= 6'} + + formdata-node@4.4.1: + resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} + engines: {node: '>= 12.20'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + git-cliff-darwin-arm64@2.8.0: + resolution: {integrity: sha512-rurUV2d1Z2n+c2+wUrO0gZaFb3c1G+ej0bPfKTPfde/CblxiysMkh+4dz23NrVbc8IlS5rSYv/JFGVaVSBNJRw==} + cpu: [arm64] + os: [darwin] + + git-cliff-darwin-x64@2.8.0: + resolution: {integrity: sha512-Wtj+FGWZBWmeYUAGlkfz7QPz4+VVxxDPMhQ/7iwKVA3iryIX0slGfzYpqMurEFnTAMr0r+4IU3Q4O/ib7iUscg==} + cpu: [x64] + os: [darwin] + + git-cliff-linux-arm64@2.8.0: + resolution: {integrity: sha512-k4RdfMdORXyefznWlQb+7wDgo7XgQF9qg8hJC34bwyJK2sODirrGau3uTx1/9Fi37g+pAOM7wM+LYppHCTZ2bQ==} + cpu: [arm64] + os: [linux] + + git-cliff-linux-x64@2.8.0: + resolution: {integrity: sha512-FcWX4GHgodYrQlZR03fzooanStgR03JNWvyaMQB1asplQ18nlziK2UyA+PESCIxOQmeLXauqoCApfzmdtp5myg==} + cpu: [x64] + os: [linux] + + git-cliff-windows-arm64@2.8.0: + resolution: {integrity: sha512-GJSrqmBVTbMtBJI3/YCDxLviZZDgYgnKqYgquBk2u2AELAnnuWFnVFQ7ZEBUqgFF2UJu9EdV2Nv6MV8d/wnP0g==} + cpu: [arm64] + os: [win32] + + git-cliff-windows-x64@2.8.0: + resolution: {integrity: sha512-8jl0YMXPYjUmVygUEeQ4wf1zte3Rv8LPq1sIklUKl80XE4g2Gm/8EIWbKpUPLQH6IncRwepY6VuMgpVpPXbwNw==} + cpu: [x64] + os: [win32] + + git-cliff@2.8.0: + resolution: {integrity: sha512-iKF5QTXAb9+iVvmu5HpnMPWYw7fs74xkpAaRbSf29+dZaMTTNRIUST/y+Ir2S1bDUWWJNjXlwT9ZT62JuYLQnA==} + engines: {node: '>=18.19 || >=20.6 || >=21'} + hasBin: true + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + highlight.js@10.7.3: + resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ink-testing-library@3.0.0: + resolution: {integrity: sha512-ItyyoOmcm6yftb7c5mZI2HU22BWzue8PBbO3DStmY8B9xaqfKr7QJONiWOXcwVsOk/6HuVQ0v7N5xhPaR3jycA==} + engines: {node: '>=14.16'} + peerDependencies: + '@types/react': '>=18.0.0' + peerDependenciesMeta: + '@types/react': + optional: true + + ink@5.2.0: + resolution: {integrity: sha512-gHzSBBvsh/1ZYuGi+aKzU7RwnYIr6PSz56or9T90i4DDS99euhN7nYKOMR3OTev0dKIB6Zod3vSapYzqoilQcg==} + engines: {node: '>=18'} + peerDependencies: + '@types/react': '>=18.0.0' + react: '>=18.0.0' + react-devtools-core: ^4.19.1 + peerDependenciesMeta: + '@types/react': + optional: true + react-devtools-core: + optional: true + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-docker@3.0.0: + resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + hasBin: true + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + + is-generator-function@1.1.0: + resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-in-ci@1.0.0: + resolution: {integrity: sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==} + engines: {node: '>=18'} + hasBin: true + + is-inside-container@1.0.0: + resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} + engines: {node: '>=14.16'} + hasBin: true + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + is-wsl@3.1.0: + resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + engines: {node: '>=16'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lint-staged@15.5.1: + resolution: {integrity: sha512-6m7u8mue4Xn6wK6gZvSCQwBvMBR36xfY24nF5bMTf2MHDYG6S3yhJuOgdYVw99hsjyDt2d4z168b3naI8+NWtQ==} + engines: {node: '>=18.12.0'} + hasBin: true + + listr2@8.3.2: + resolution: {integrity: sha512-vsBzcU4oE+v0lj4FhVLzr9dBTv4/fHIa57l+GCwovP8MoFNZJTOhGU8PXd4v2VJCbECAaijBiHntiekFMLvo0g==} + engines: {node: '>=18.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + loupe@3.1.3: + resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} + + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + marked-terminal@7.3.0: + resolution: {integrity: sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==} + engines: {node: '>=16.0.0'} + peerDependencies: + marked: '>=1 <16' + + marked@11.2.0: + resolution: {integrity: sha512-HR0m3bvu0jAPYiIvLUUQtdg1g6D247//lvcekpHO1WMvbwDlwSkZAX9Lw4F4YHE1T0HaaNve0tuAWuV1UJ6vtw==} + engines: {node: '>= 18'} + hasBin: true + + marked@15.0.8: + resolution: {integrity: sha512-rli4l2LyZqpQuRve5C0rkn6pj3hT8EWPC+zkAxFTAJLxRbENfTAhEQq9itrmf1Y81QtAX5D/MYlGlIomNgj9lA==} + engines: {node: '>= 18'} + hasBin: true + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + + node-emoji@2.2.0: + resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==} + engines: {node: '>=18'} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + open@10.1.1: + resolution: {integrity: sha512-zy1wx4+P3PfhXSEPJNtZmJXfhkkIaxU1VauWIrDZw1O7uJRDRJtKr9n3Ic4NgbA16KyOxOXO2ng9gYwCdXuSXA==} + engines: {node: '>=18'} + + openai@4.95.1: + resolution: {integrity: sha512-IqJy+ymeW+k/Wq+2YVN3693OQMMcODRtHEYOlz263MdUwnN/Dwdl9c2EXSxLLtGEHkSHAfvzpDMHI5MaWJKXjQ==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.23.8 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse5-htmlparser2-tree-adapter@6.0.1: + resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} + + parse5@5.1.1: + resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} + + parse5@6.0.1: + resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} + + patch-console@2.0.0: + resolution: {integrity: sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + engines: {node: '>= 14.16'} + + peek-readable@7.0.0: + resolution: {integrity: sha512-nri2TO5JE3/mRryik9LlHFT53cgHfRK0Lt0BAZQXku/AW3E6XLt2GaY8siWi7dvW/m1z0ecn+J+bpDa9ZN3IsQ==} + engines: {node: '>=18'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + engines: {node: '>=12'} + + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss@8.5.3: + resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + prettier@3.5.3: + resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + engines: {node: '>=14'} + hasBin: true + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-reconciler@0.29.2: + resolution: {integrity: sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg==} + engines: {node: '>=0.10.0'} + peerDependencies: + react: ^18.3.1 + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + restore-cursor@4.0.0: + resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rollup@4.40.0: + resolution: {integrity: sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-applescript@7.0.0: + resolution: {integrity: sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==} + engines: {node: '>=18'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.2: + resolution: {integrity: sha512-AzqKpGKjrj7EM6rKVQEPpB288oCfnrEIuyoT9cyF4nmGa7V8Zk6f7RRqYisX8X9m+Q7bd632aZW4ky7EhbQztA==} + engines: {node: '>= 0.4'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + skin-tone@2.0.0: + resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==} + engines: {node: '>=8'} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.9.0: + resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strtok3@10.2.2: + resolution: {integrity: sha512-Xt18+h4s7Z8xyZ0tmBoRmzxcop97R4BAh+dXouUDCYn+Em+1P3qpkUfI5ueWLT8ynC5hZ+q4iPEmGG1urvQGBg==} + engines: {node: '>=18'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-hyperlinks@3.2.0: + resolution: {integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==} + engines: {node: '>=14.18'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.12: + resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} + engines: {node: '>=12.0.0'} + + tinypool@1.0.2: + resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@3.0.2: + resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + engines: {node: '>=14.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + to-rotated@1.0.0: + resolution: {integrity: sha512-KsEID8AfgUy+pxVRLsWp0VzCa69wxzUDZnzGbyIST/bcgcrMvTYoFBX/QORH4YApoD89EDuUovx4BTdpOn319Q==} + engines: {node: '>=18'} + + token-types@6.0.0: + resolution: {integrity: sha512-lbDrTLVsHhOMljPscd0yitpozq7Ga2M5Cvez5AjGg8GASBjtt6iERCAJ93yommPmz62fb45oFIXHEZ3u9bfJEA==} + engines: {node: '>=14.16'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@5.1.1: + resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} + engines: {node: '>=18'} + + ts-api-utils@1.4.3: + resolution: {integrity: sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==} + engines: {node: '>=16'} + peerDependencies: + typescript: '>=4.2.0' + + ts-node@10.9.2: + resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} + hasBin: true + peerDependencies: + '@swc/core': '>=1.2.50' + '@swc/wasm': '>=1.2.50' + '@types/node': '*' + typescript: '>=2.7' + peerDependenciesMeta: + '@swc/core': + optional: true + '@swc/wasm': + optional: true + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@4.40.0: + resolution: {integrity: sha512-ABHZ2/tS2JkvH1PEjxFDTUWC8dB5OsIGZP4IFLhR293GqT5Y5qB1WwL2kMPYhQW9DVgVD8Hd7I8gjwPIf5GFkw==} + engines: {node: '>=16'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + uint8array-extras@1.4.0: + resolution: {integrity: sha512-ZPtzy0hu4cZjv3z5NW9gfKnNLjoz4y6uv4HlelAjDK7sY/xOkKZv9xK/WQpcsBB3jEybChz9DPC2U/+cusjJVQ==} + engines: {node: '>=18'} + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + unicode-emoji-modifier-base@1.0.0: + resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==} + engines: {node: '>=4'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-interval@1.4.0: + resolution: {integrity: sha512-1betIJun2rXKLxa30AFOBZCeZhsBJoJ/3+gkCeYbJ63lAR//EnAb1NjNeFqzgqeM7zQfR76rrCUaA8DvfgoOpA==} + engines: {node: '>=8', npm: '>=5'} + peerDependencies: + react: '>=16.8.0 || ^17' + + v8-compile-cache-lib@3.0.1: + resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + + vite-node@3.1.1: + resolution: {integrity: sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite@6.3.2: + resolution: {integrity: sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@3.1.1: + resolution: {integrity: sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.1.1 + '@vitest/ui': 3.1.1 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + web-streams-polyfill@4.0.0-beta.3: + resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} + engines: {node: '>= 14'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-url@14.2.0: + resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} + engines: {node: '>=18'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + widest-line@5.0.0: + resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} + engines: {node: '>=18'} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + ws@8.18.1: + resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yaml@2.7.1: + resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==} + engines: {node: '>= 14'} + hasBin: true + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yn@3.1.1: + resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} + engines: {node: '>=6'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + yoga-layout@3.2.1: + resolution: {integrity: sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==} + + zod@3.24.3: + resolution: {integrity: sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==} + +snapshots: + + '@alcalzone/ansi-tokenize@0.1.3': + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + + '@colors/colors@1.5.0': + optional: true + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + + '@esbuild/aix-ppc64@0.25.2': + optional: true + + '@esbuild/android-arm64@0.25.2': + optional: true + + '@esbuild/android-arm@0.25.2': + optional: true + + '@esbuild/android-x64@0.25.2': + optional: true + + '@esbuild/darwin-arm64@0.25.2': + optional: true + + '@esbuild/darwin-x64@0.25.2': + optional: true + + '@esbuild/freebsd-arm64@0.25.2': + optional: true + + '@esbuild/freebsd-x64@0.25.2': + optional: true + + '@esbuild/linux-arm64@0.25.2': + optional: true + + '@esbuild/linux-arm@0.25.2': + optional: true + + '@esbuild/linux-ia32@0.25.2': + optional: true + + '@esbuild/linux-loong64@0.25.2': + optional: true + + '@esbuild/linux-mips64el@0.25.2': + optional: true + + '@esbuild/linux-ppc64@0.25.2': + optional: true + + '@esbuild/linux-riscv64@0.25.2': + optional: true + + '@esbuild/linux-s390x@0.25.2': + optional: true + + '@esbuild/linux-x64@0.25.2': + optional: true + + '@esbuild/netbsd-arm64@0.25.2': + optional: true + + '@esbuild/netbsd-x64@0.25.2': + optional: true + + '@esbuild/openbsd-arm64@0.25.2': + optional: true + + '@esbuild/openbsd-x64@0.25.2': + optional: true + + '@esbuild/sunos-x64@0.25.2': + optional: true + + '@esbuild/win32-arm64@0.25.2': + optional: true + + '@esbuild/win32-ia32@0.25.2': + optional: true + + '@esbuild/win32-x64@0.25.2': + optional: true + + '@eslint-community/eslint-utils@4.6.1(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.12.6 + debug: 4.4.0 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.1': {} + + '@eslint/js@9.25.0': {} + + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.4.0 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@inkjs/ui@2.0.0(ink@5.2.0(@types/react@18.3.20)(react@18.3.1))': + dependencies: + chalk: 5.4.1 + cli-spinners: 3.2.0 + deepmerge: 4.3.1 + figures: 6.1.0 + ink: 5.2.0(@types/react@18.3.20)(react@18.3.1) + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@rollup/rollup-android-arm-eabi@4.40.0': + optional: true + + '@rollup/rollup-android-arm64@4.40.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.40.0': + optional: true + + '@rollup/rollup-darwin-x64@4.40.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.40.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.40.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.40.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.40.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.40.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.40.0': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.40.0': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.40.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.40.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.40.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.40.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.40.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.40.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.40.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.40.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.40.0': + optional: true + + '@rtsao/scc@1.1.0': {} + + '@sindresorhus/is@4.6.0': {} + + '@tokenizer/inflate@0.2.7': + dependencies: + debug: 4.4.0 + fflate: 0.8.2 + token-types: 6.0.0 + transitivePeerDependencies: + - supports-color + + '@tokenizer/token@0.3.0': {} + + '@tsconfig/node10@1.0.11': {} + + '@tsconfig/node12@1.0.11': {} + + '@tsconfig/node14@1.0.3': {} + + '@tsconfig/node16@1.0.4': {} + + '@types/cardinal@2.1.1': {} + + '@types/diff@7.0.2': {} + + '@types/estree@1.0.7': {} + + '@types/js-yaml@4.0.9': {} + + '@types/json5@0.0.29': {} + + '@types/marked-terminal@6.1.1': + dependencies: + '@types/cardinal': 2.1.1 + '@types/node': 22.14.1 + chalk: 5.4.1 + marked: 11.2.0 + + '@types/node-fetch@2.6.12': + dependencies: + '@types/node': 18.19.86 + form-data: 4.0.2 + + '@types/node@18.19.86': + dependencies: + undici-types: 5.26.5 + + '@types/node@22.14.1': + dependencies: + undici-types: 6.21.0 + + '@types/prop-types@15.7.14': {} + + '@types/react@18.3.20': + dependencies: + '@types/prop-types': 15.7.14 + csstype: 3.1.3 + + '@types/shell-quote@1.7.5': {} + + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/type-utils': 7.18.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 7.18.0 + eslint: 8.57.1 + graphemer: 1.4.0 + ignore: 5.3.2 + natural-compare: 1.4.0 + ts-api-utils: 1.4.3(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.4.0 + eslint: 8.57.1 + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@7.18.0': + dependencies: + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + + '@typescript-eslint/type-utils@7.18.0(eslint@8.57.1)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.3) + '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.8.3) + debug: 4.4.0 + eslint: 8.57.1 + ts-api-utils: 1.4.3(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@7.18.0': {} + + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.8.3)': + dependencies: + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/visitor-keys': 7.18.0 + debug: 4.4.0 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.1 + ts-api-utils: 1.4.3(typescript@5.8.3) + optionalDependencies: + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@7.18.0(eslint@8.57.1)(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.6.1(eslint@8.57.1) + '@typescript-eslint/scope-manager': 7.18.0 + '@typescript-eslint/types': 7.18.0 + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.8.3) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + + '@typescript-eslint/visitor-keys@7.18.0': + dependencies: + '@typescript-eslint/types': 7.18.0 + eslint-visitor-keys: 3.4.3 + + '@ungap/structured-clone@1.3.0': {} + + '@vitest/expect@3.1.1': + dependencies: + '@vitest/spy': 3.1.1 + '@vitest/utils': 3.1.1 + chai: 5.2.0 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.1.1(vite@6.3.2(@types/node@22.14.1)(yaml@2.7.1))': + dependencies: + '@vitest/spy': 3.1.1 + estree-walker: 3.0.3 + magic-string: 0.30.17 + optionalDependencies: + vite: 6.3.2(@types/node@22.14.1)(yaml@2.7.1) + + '@vitest/pretty-format@3.1.1': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.1.1': + dependencies: + '@vitest/utils': 3.1.1 + pathe: 2.0.3 + + '@vitest/snapshot@3.1.1': + dependencies: + '@vitest/pretty-format': 3.1.1 + magic-string: 0.30.17 + pathe: 2.0.3 + + '@vitest/spy@3.1.1': + dependencies: + tinyspy: 3.0.2 + + '@vitest/utils@3.1.1': + dependencies: + '@vitest/pretty-format': 3.1.1 + loupe: 3.1.3 + tinyrainbow: 2.0.0 + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + acorn-jsx@5.3.2(acorn@8.14.1): + dependencies: + acorn: 8.14.1 + + acorn-walk@8.3.4: + dependencies: + acorn: 8.14.1 + + acorn@8.14.1: {} + + agentkeepalive@4.6.0: + dependencies: + humanize-ms: 1.2.1 + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-escapes@7.0.0: + dependencies: + environment: 1.1.0 + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + any-promise@1.3.0: {} + + arg@4.1.3: {} + + argparse@2.0.1: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + + array-union@2.1.0: {} + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + assertion-error@2.0.1: {} + + async-function@1.0.0: {} + + asynckit@0.4.0: {} + + auto-bind@5.0.1: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + balanced-match@1.0.2: {} + + brace-expansion@1.1.11: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + bundle-name@4.1.0: + dependencies: + run-applescript: 7.0.0 + + cac@6.7.14: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + chai@5.2.0: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.1.3 + pathval: 2.0.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.4.1: {} + + char-regex@1.0.2: {} + + check-error@2.1.1: {} + + cli-boxes@3.0.0: {} + + cli-cursor@4.0.0: + dependencies: + restore-cursor: 4.0.0 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-highlight@2.1.11: + dependencies: + chalk: 4.1.2 + highlight.js: 10.7.3 + mz: 2.7.0 + parse5: 5.1.1 + parse5-htmlparser2-tree-adapter: 6.0.1 + yargs: 16.2.0 + + cli-spinners@3.2.0: {} + + cli-table3@0.6.5: + dependencies: + string-width: 4.2.3 + optionalDependencies: + '@colors/colors': 1.5.0 + + cli-truncate@4.0.0: + dependencies: + slice-ansi: 5.0.0 + string-width: 7.2.0 + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + code-excerpt@4.0.0: + dependencies: + convert-to-spaces: 2.0.1 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + colorette@2.0.20: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@13.1.0: {} + + concat-map@0.0.1: {} + + convert-to-spaces@2.0.1: {} + + create-require@1.1.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.1.3: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.0: + dependencies: + ms: 2.1.3 + + deep-eql@5.0.2: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + default-browser-id@5.0.0: {} + + default-browser@5.2.1: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.0 + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-lazy-prop@3.0.0: {} + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + delayed-stream@1.0.0: {} + + diff@4.0.2: {} + + diff@7.0.0: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + doctrine@3.0.0: + dependencies: + esutils: 2.0.3 + + dotenv@16.5.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + emoji-regex@10.4.0: {} + + emoji-regex@8.0.0: {} + + emojilib@2.4.0: {} + + environment@1.1.0: {} + + es-abstract@1.23.9: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-regex: 1.2.1 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + safe-array-concat: 1.1.3 + + es-module-lexer@1.6.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + es-toolkit@1.35.0: {} + + esbuild@0.25.2: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.2 + '@esbuild/android-arm': 0.25.2 + '@esbuild/android-arm64': 0.25.2 + '@esbuild/android-x64': 0.25.2 + '@esbuild/darwin-arm64': 0.25.2 + '@esbuild/darwin-x64': 0.25.2 + '@esbuild/freebsd-arm64': 0.25.2 + '@esbuild/freebsd-x64': 0.25.2 + '@esbuild/linux-arm': 0.25.2 + '@esbuild/linux-arm64': 0.25.2 + '@esbuild/linux-ia32': 0.25.2 + '@esbuild/linux-loong64': 0.25.2 + '@esbuild/linux-mips64el': 0.25.2 + '@esbuild/linux-ppc64': 0.25.2 + '@esbuild/linux-riscv64': 0.25.2 + '@esbuild/linux-s390x': 0.25.2 + '@esbuild/linux-x64': 0.25.2 + '@esbuild/netbsd-arm64': 0.25.2 + '@esbuild/netbsd-x64': 0.25.2 + '@esbuild/openbsd-arm64': 0.25.2 + '@esbuild/openbsd-x64': 0.25.2 + '@esbuild/sunos-x64': 0.25.2 + '@esbuild/win32-arm64': 0.25.2 + '@esbuild/win32-ia32': 0.25.2 + '@esbuild/win32-x64': 0.25.2 + + escalade@3.2.0: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.10 + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.8.3) + eslint: 8.57.1 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.31.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.8 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 8.57.1 + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.1) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 7.7.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.8.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-react-hooks@4.6.2(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-plugin-react-refresh@0.4.19(eslint@8.57.1): + dependencies: + eslint: 8.57.1 + + eslint-plugin-react@7.37.5(eslint@8.57.1): + dependencies: + array-includes: 3.1.8 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.2.1 + eslint: 8.57.1 + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 7.7.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-scope@7.2.2: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint@8.57.1: + dependencies: + '@eslint-community/eslint-utils': 4.6.1(eslint@8.57.1) + '@eslint-community/regexpp': 4.12.1 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + '@ungap/structured-clone': 1.3.0 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.0 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.24.0 + graphemer: 1.4.0 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + strip-ansi: 6.0.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + + espree@9.6.1: + dependencies: + acorn: 8.14.1 + acorn-jsx: 5.3.2(acorn@8.14.1) + eslint-visitor-keys: 3.4.3 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.7 + + esutils@2.0.3: {} + + event-target-shim@5.0.1: {} + + eventemitter3@5.0.1: {} + + execa@8.0.1: + dependencies: + cross-spawn: 7.0.6 + get-stream: 8.0.1 + human-signals: 5.0.0 + is-stream: 3.0.0 + merge-stream: 2.0.0 + npm-run-path: 5.3.0 + onetime: 6.0.0 + signal-exit: 4.1.0 + strip-final-newline: 3.0.0 + + expect-type@1.2.1: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fdir@6.4.3(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + fflate@0.8.2: {} + + figures@6.1.0: + dependencies: + is-unicode-supported: 2.1.0 + + file-entry-cache@6.0.1: + dependencies: + flat-cache: 3.2.0 + + file-type@20.4.1: + dependencies: + '@tokenizer/inflate': 0.2.7 + strtok3: 10.2.2 + token-types: 6.0.0 + uint8array-extras: 1.4.0 + transitivePeerDependencies: + - supports-color + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@3.2.0: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + rimraf: 3.0.2 + + flatted@3.3.3: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + form-data-encoder@1.7.2: {} + + form-data@4.0.2: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + mime-types: 2.1.35 + + formdata-node@4.4.1: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 4.0.0-beta.3 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.3.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@8.0.1: {} + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + git-cliff-darwin-arm64@2.8.0: + optional: true + + git-cliff-darwin-x64@2.8.0: + optional: true + + git-cliff-linux-arm64@2.8.0: + optional: true + + git-cliff-linux-x64@2.8.0: + optional: true + + git-cliff-windows-arm64@2.8.0: + optional: true + + git-cliff-windows-x64@2.8.0: + optional: true + + git-cliff@2.8.0: + dependencies: + execa: 8.0.1 + optionalDependencies: + git-cliff-darwin-arm64: 2.8.0 + git-cliff-darwin-x64: 2.8.0 + git-cliff-linux-arm64: 2.8.0 + git-cliff-linux-x64: 2.8.0 + git-cliff-windows-arm64: 2.8.0 + git-cliff-windows-x64: 2.8.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.3 + ignore: 5.3.2 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.2.0: {} + + graphemer@1.4.0: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + highlight.js@10.7.3: {} + + human-signals@5.0.0: {} + + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + + husky@9.1.7: {} + + ieee754@1.2.1: {} + + ignore@5.3.2: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + indent-string@5.0.0: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + ink-testing-library@3.0.0(@types/react@18.3.20): + optionalDependencies: + '@types/react': 18.3.20 + + ink@5.2.0(@types/react@18.3.20)(react@18.3.1): + dependencies: + '@alcalzone/ansi-tokenize': 0.1.3 + ansi-escapes: 7.0.0 + ansi-styles: 6.2.1 + auto-bind: 5.0.1 + chalk: 5.4.1 + cli-boxes: 3.0.0 + cli-cursor: 4.0.0 + cli-truncate: 4.0.0 + code-excerpt: 4.0.0 + es-toolkit: 1.35.0 + indent-string: 5.0.0 + is-in-ci: 1.0.0 + patch-console: 2.0.0 + react: 18.3.1 + react-reconciler: 0.29.2(react@18.3.1) + scheduler: 0.23.2 + signal-exit: 3.0.7 + slice-ansi: 7.1.0 + stack-utils: 2.0.6 + string-width: 7.2.0 + type-fest: 4.40.0 + widest-line: 5.0.0 + wrap-ansi: 9.0.0 + ws: 8.18.1 + yoga-layout: 3.2.1 + optionalDependencies: + '@types/react': 18.3.20 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-docker@3.0.0: {} + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.0.0: + dependencies: + get-east-asian-width: 1.3.0 + + is-generator-function@1.1.0: + dependencies: + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-in-ci@1.0.0: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-map@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-path-inside@3.0.3: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-stream@3.0.0: {} + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + + is-unicode-supported@2.1.0: {} + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-wsl@3.1.0: + dependencies: + is-inside-container: 1.0.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.8 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lilconfig@3.1.3: {} + + lint-staged@15.5.1: + dependencies: + chalk: 5.4.1 + commander: 13.1.0 + debug: 4.4.0 + execa: 8.0.1 + lilconfig: 3.1.3 + listr2: 8.3.2 + micromatch: 4.0.8 + pidtree: 0.6.0 + string-argv: 0.3.2 + yaml: 2.7.1 + transitivePeerDependencies: + - supports-color + + listr2@8.3.2: + dependencies: + cli-truncate: 4.0.0 + colorette: 2.0.20 + eventemitter3: 5.0.1 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.0.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.0 + strip-ansi: 7.1.0 + wrap-ansi: 9.0.0 + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + loupe@3.1.3: {} + + magic-string@0.30.17: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + make-error@1.3.6: {} + + marked-terminal@7.3.0(marked@15.0.8): + dependencies: + ansi-escapes: 7.0.0 + ansi-regex: 6.1.0 + chalk: 5.4.1 + cli-highlight: 2.1.11 + cli-table3: 0.6.5 + marked: 15.0.8 + node-emoji: 2.2.0 + supports-hyperlinks: 3.2.0 + + marked@11.2.0: {} + + marked@15.0.8: {} + + math-intrinsics@1.1.0: {} + + meow@13.2.0: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mimic-fn@2.1.0: {} + + mimic-fn@4.0.0: {} + + mimic-function@5.0.1: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.11 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.11: {} + + natural-compare@1.4.0: {} + + node-domexception@1.0.0: {} + + node-emoji@2.2.0: + dependencies: + '@sindresorhus/is': 4.6.0 + char-regex: 1.0.2 + emojilib: 2.4.0 + skin-tone: 2.0.0 + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + npm-run-path@5.3.0: + dependencies: + path-key: 4.0.0 + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + onetime@6.0.0: + dependencies: + mimic-fn: 4.0.0 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + open@10.1.1: + dependencies: + default-browser: 5.2.1 + define-lazy-prop: 3.0.0 + is-inside-container: 1.0.0 + is-wsl: 3.1.0 + + openai@4.95.1(ws@8.18.1)(zod@3.24.3): + dependencies: + '@types/node': 18.19.86 + '@types/node-fetch': 2.6.12 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + optionalDependencies: + ws: 8.18.1 + zod: 3.24.3 + transitivePeerDependencies: + - encoding + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse5-htmlparser2-tree-adapter@6.0.1: + dependencies: + parse5: 6.0.1 + + parse5@5.1.1: {} + + parse5@6.0.1: {} + + patch-console@2.0.0: {} + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-parse@1.0.7: {} + + path-type@4.0.0: {} + + pathe@2.0.3: {} + + pathval@2.0.0: {} + + peek-readable@7.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.2: {} + + pidtree@0.6.0: {} + + possible-typed-array-names@1.1.0: {} + + postcss@8.5.3: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier@2.8.8: {} + + prettier@3.5.3: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + react-is@16.13.1: {} + + react-reconciler@0.29.2(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + require-directory@2.1.1: {} + + resolve-from@4.0.0: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.5: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@4.0.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.1.0: {} + + rfdc@1.4.1: {} + + rimraf@3.0.2: + dependencies: + glob: 7.2.3 + + rollup@4.40.0: + dependencies: + '@types/estree': 1.0.7 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.40.0 + '@rollup/rollup-android-arm64': 4.40.0 + '@rollup/rollup-darwin-arm64': 4.40.0 + '@rollup/rollup-darwin-x64': 4.40.0 + '@rollup/rollup-freebsd-arm64': 4.40.0 + '@rollup/rollup-freebsd-x64': 4.40.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.40.0 + '@rollup/rollup-linux-arm-musleabihf': 4.40.0 + '@rollup/rollup-linux-arm64-gnu': 4.40.0 + '@rollup/rollup-linux-arm64-musl': 4.40.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.40.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.40.0 + '@rollup/rollup-linux-riscv64-gnu': 4.40.0 + '@rollup/rollup-linux-riscv64-musl': 4.40.0 + '@rollup/rollup-linux-s390x-gnu': 4.40.0 + '@rollup/rollup-linux-x64-gnu': 4.40.0 + '@rollup/rollup-linux-x64-musl': 4.40.0 + '@rollup/rollup-win32-arm64-msvc': 4.40.0 + '@rollup/rollup-win32-ia32-msvc': 4.40.0 + '@rollup/rollup-win32-x64-msvc': 4.40.0 + fsevents: 2.3.3 + + run-applescript@7.0.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + semver@7.7.1: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + shell-quote@1.8.2: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + skin-tone@2.0.0: + dependencies: + unicode-emoji-modifier-base: 1.0.0 + + slash@3.0.0: {} + + slice-ansi@5.0.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 4.0.0 + + slice-ansi@7.1.0: + dependencies: + ansi-styles: 6.2.1 + is-fullwidth-code-point: 5.0.0 + + source-map-js@1.2.1: {} + + stack-utils@2.0.6: + dependencies: + escape-string-regexp: 2.0.0 + + stackback@0.0.2: {} + + std-env@3.9.0: {} + + string-argv@0.3.2: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.23.9 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.23.9 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-bom@3.0.0: {} + + strip-final-newline@3.0.0: {} + + strip-json-comments@3.1.1: {} + + strtok3@10.2.2: + dependencies: + '@tokenizer/token': 0.3.0 + peek-readable: 7.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-hyperlinks@3.2.0: + dependencies: + has-flag: 4.0.0 + supports-color: 7.2.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + text-table@0.2.0: {} + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.12: + dependencies: + fdir: 6.4.3(picomatch@4.0.2) + picomatch: 4.0.2 + + tinypool@1.0.2: {} + + tinyrainbow@2.0.0: {} + + tinyspy@3.0.2: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + to-rotated@1.0.0: {} + + token-types@6.0.0: + dependencies: + '@tokenizer/token': 0.3.0 + ieee754: 1.2.1 + + tr46@0.0.3: {} + + tr46@5.1.1: + dependencies: + punycode: 2.3.1 + + ts-api-utils@1.4.3(typescript@5.8.3): + dependencies: + typescript: 5.8.3 + + ts-node@10.9.2(@types/node@22.14.1)(typescript@5.8.3): + dependencies: + '@cspotcode/source-map-support': 0.8.1 + '@tsconfig/node10': 1.0.11 + '@tsconfig/node12': 1.0.11 + '@tsconfig/node14': 1.0.3 + '@tsconfig/node16': 1.0.4 + '@types/node': 22.14.1 + acorn: 8.14.1 + acorn-walk: 8.3.4 + arg: 4.1.3 + create-require: 1.1.1 + diff: 4.0.2 + make-error: 1.3.6 + typescript: 5.8.3 + v8-compile-cache-lib: 3.0.1 + yn: 3.1.1 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@0.20.2: {} + + type-fest@4.40.0: {} + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript@5.8.3: {} + + uint8array-extras@1.4.0: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@5.26.5: {} + + undici-types@6.21.0: {} + + unicode-emoji-modifier-base@1.0.0: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-interval@1.4.0(react@18.3.1): + dependencies: + react: 18.3.1 + + v8-compile-cache-lib@3.0.1: {} + + vite-node@3.1.1(@types/node@22.14.1)(yaml@2.7.1): + dependencies: + cac: 6.7.14 + debug: 4.4.0 + es-module-lexer: 1.6.0 + pathe: 2.0.3 + vite: 6.3.2(@types/node@22.14.1)(yaml@2.7.1) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite@6.3.2(@types/node@22.14.1)(yaml@2.7.1): + dependencies: + esbuild: 0.25.2 + fdir: 6.4.3(picomatch@4.0.2) + picomatch: 4.0.2 + postcss: 8.5.3 + rollup: 4.40.0 + tinyglobby: 0.2.12 + optionalDependencies: + '@types/node': 22.14.1 + fsevents: 2.3.3 + yaml: 2.7.1 + + vitest@3.1.1(@types/node@22.14.1)(yaml@2.7.1): + dependencies: + '@vitest/expect': 3.1.1 + '@vitest/mocker': 3.1.1(vite@6.3.2(@types/node@22.14.1)(yaml@2.7.1)) + '@vitest/pretty-format': 3.1.1 + '@vitest/runner': 3.1.1 + '@vitest/snapshot': 3.1.1 + '@vitest/spy': 3.1.1 + '@vitest/utils': 3.1.1 + chai: 5.2.0 + debug: 4.4.0 + expect-type: 1.2.1 + magic-string: 0.30.17 + pathe: 2.0.3 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinypool: 1.0.2 + tinyrainbow: 2.0.0 + vite: 6.3.2(@types/node@22.14.1)(yaml@2.7.1) + vite-node: 3.1.1(@types/node@22.14.1)(yaml@2.7.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.14.1 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + web-streams-polyfill@4.0.0-beta.3: {} + + webidl-conversions@3.0.1: {} + + webidl-conversions@7.0.0: {} + + whatwg-url@14.2.0: + dependencies: + tr46: 5.1.1 + webidl-conversions: 7.0.0 + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.0 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + widest-line@5.0.0: + dependencies: + string-width: 7.2.0 + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + wrappy@1.0.2: {} + + ws@8.18.1: {} + + y18n@5.0.8: {} + + yaml@2.7.1: {} + + yargs-parser@20.2.9: {} + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + + yn@3.1.1: {} + + yocto-queue@0.1.0: {} + + yoga-layout@3.2.1: {} + + zod@3.24.3: {} From c40f4891d479114b433457574eca041b666571e9 Mon Sep 17 00:00:00 2001 From: Jon Church Date: Fri, 18 Apr 2025 20:17:35 -0400 Subject: [PATCH 11/82] chore: update lockfile (#379) Not 100% this isn't a me thing, but might be dirty state leftover from the pnpm migration --- pnpm-lock.yaml | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index db130a4b75..2b8e2dae64 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -104,12 +104,18 @@ importers: '@types/shell-quote': specifier: ^1.7.5 version: 1.7.5 + '@types/which': + specifier: ^3.0.4 + version: 3.0.4 '@typescript-eslint/eslint-plugin': specifier: ^7.18.0 version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3) '@typescript-eslint/parser': specifier: ^7.18.0 version: 7.18.0(eslint@8.57.1)(typescript@5.8.3) + boxen: + specifier: ^8.0.1 + version: 8.0.1 esbuild: specifier: ^0.25.2 version: 0.25.2 @@ -152,6 +158,9 @@ importers: whatwg-url: specifier: ^14.2.0 version: 14.2.0 + which: + specifier: ^5.0.0 + version: 5.0.0 packages: @@ -542,6 +551,9 @@ packages: '@types/shell-quote@1.7.5': resolution: {integrity: sha512-+UE8GAGRPbJVQDdxi16dgadcBfQ+KG2vgZhV1+3A1XmHbmwcdwhCUwIdy+d3pAGrbvgRoVSjeI9vOWyq376Yzw==} + '@types/which@3.0.4': + resolution: {integrity: sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w==} + '@typescript-eslint/eslint-plugin@7.18.0': resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} engines: {node: ^18.18.0 || >=20.0.0} @@ -657,6 +669,9 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + ansi-escapes@7.0.0: resolution: {integrity: sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==} engines: {node: '>=18'} @@ -744,6 +759,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + boxen@8.0.1: + resolution: {integrity: sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==} + engines: {node: '>=18'} + brace-expansion@1.1.11: resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} @@ -778,6 +797,10 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + camelcase@8.0.0: + resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} + engines: {node: '>=16'} + chai@5.2.0: resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} engines: {node: '>=12'} @@ -1548,6 +1571,10 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isexe@3.1.1: + resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} + engines: {node: '>=16'} + iterator.prototype@1.1.5: resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} engines: {node: '>= 0.4'} @@ -2384,6 +2411,11 @@ packages: engines: {node: '>= 8'} hasBin: true + which@5.0.0: + resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} + engines: {node: ^18.17.0 || >=20.5.0} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -2727,6 +2759,8 @@ snapshots: '@types/shell-quote@1.7.5': {} + '@types/which@3.0.4': {} + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -2875,6 +2909,10 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ansi-align@3.0.1: + dependencies: + string-width: 4.2.3 + ansi-escapes@7.0.0: dependencies: environment: 1.1.0 @@ -2976,6 +3014,17 @@ snapshots: balanced-match@1.0.2: {} + boxen@8.0.1: + dependencies: + ansi-align: 3.0.1 + camelcase: 8.0.0 + chalk: 5.4.1 + cli-boxes: 3.0.0 + string-width: 7.2.0 + type-fest: 4.40.0 + widest-line: 5.0.0 + wrap-ansi: 9.0.0 + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 @@ -3014,6 +3063,8 @@ snapshots: callsites@3.1.0: {} + camelcase@8.0.0: {} + chai@5.2.0: dependencies: assertion-error: 2.0.1 @@ -3915,6 +3966,8 @@ snapshots: isexe@2.0.0: {} + isexe@3.1.1: {} + iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 @@ -4827,6 +4880,10 @@ snapshots: dependencies: isexe: 2.0.0 + which@5.0.0: + dependencies: + isexe: 3.1.1 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 From aa32e22d4b98d7f3b0c227b64fcd904ecd5a466d Mon Sep 17 00:00:00 2001 From: Fouad Matin <169186268+fouad-openai@users.noreply.github.com> Date: Fri, 18 Apr 2025 18:13:34 -0700 Subject: [PATCH 12/82] fix: /bug report command, thinking indicator (#381) - Fix `/bug` report command - Fix thinking indicator --- codex-cli/bin/codex | 20 --- codex-cli/bin/codex.js | 3 + codex-cli/package.json | 2 +- .../chat/terminal-chat-input-thinking.tsx | 127 ++++++------------ .../components/chat/terminal-chat-input.tsx | 52 +++++-- .../chat/terminal-chat-new-input.tsx | 80 ++++++----- .../src/components/chat/terminal-chat.tsx | 1 + .../chat/terminal-message-history.tsx | 14 +- codex-cli/src/utils/bug-report.ts | 31 ++--- .../terminal-chat-input-compact.test.tsx | 1 + codex-cli/tsconfig.json | 2 +- pnpm-lock.yaml | 4 +- 12 files changed, 154 insertions(+), 183 deletions(-) delete mode 100755 codex-cli/bin/codex mode change 100644 => 100755 codex-cli/bin/codex.js diff --git a/codex-cli/bin/codex b/codex-cli/bin/codex deleted file mode 100755 index 9bf96bf29d..0000000000 --- a/codex-cli/bin/codex +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env sh -# resolve script path in case of symlink -SOURCE="$0" -while [ -h "$SOURCE" ]; do - DIR=$(dirname "$SOURCE") - SOURCE=$(readlink "$SOURCE") - case "$SOURCE" in - /*) ;; # absolute path - *) SOURCE="$DIR/$SOURCE" ;; # relative path - esac -done -DIR=$(cd "$(dirname "$SOURCE")" && pwd) -if command -v node >/dev/null 2>&1; then - exec node "$DIR/../dist/cli.js" "$@" -elif command -v bun >/dev/null 2>&1; then - exec bun "$DIR/../dist/cli.js" "$@" -else - echo "Error: node or bun is required to run codex" >&2 - exit 1 -fi \ No newline at end of file diff --git a/codex-cli/bin/codex.js b/codex-cli/bin/codex.js old mode 100644 new mode 100755 index 347dc854ec..1df18d1fa3 --- a/codex-cli/bin/codex.js +++ b/codex-cli/bin/codex.js @@ -1,4 +1,5 @@ #!/usr/bin/env node + // Unified entry point for Codex CLI on all platforms // Dynamically loads the compiled ESM bundle in dist/cli.js @@ -18,7 +19,9 @@ const cliUrl = pathToFileURL(cliPath).href; try { await import(cliUrl); } catch (err) { + // eslint-disable-next-line no-console console.error(err); + // eslint-disable-next-line no-undef process.exit(1); } })(); diff --git a/codex-cli/package.json b/codex-cli/package.json index cc1714f42d..df3edd1cfb 100644 --- a/codex-cli/package.json +++ b/codex-cli/package.json @@ -47,7 +47,7 @@ "marked-terminal": "^7.3.0", "meow": "^13.2.0", "open": "^10.1.0", - "openai": "^4.89.0", + "openai": "^4.95.1", "react": "^18.2.0", "shell-quote": "^1.8.2", "strip-ansi": "^7.1.0", diff --git a/codex-cli/src/components/chat/terminal-chat-input-thinking.tsx b/codex-cli/src/components/chat/terminal-chat-input-thinking.tsx index 987e04f3d2..213dd8c9af 100644 --- a/codex-cli/src/components/chat/terminal-chat-input-thinking.tsx +++ b/codex-cli/src/components/chat/terminal-chat-input-thinking.tsx @@ -1,82 +1,28 @@ import { log, isLoggingEnabled } from "../../utils/agent/log.js"; -import Spinner from "../vendor/ink-spinner.js"; import { Box, Text, useInput, useStdin } from "ink"; import React, { useState } from "react"; import { useInterval } from "use-interval"; -const thinkingTexts = ["Thinking"]; /* [ - "Consulting the rubber duck", - "Maximizing paperclips", - "Reticulating splines", - "Immanentizing the Eschaton", - "Thinking", - "Thinking about thinking", - "Spinning in circles", - "Counting dust specks", - "Updating priors", - "Feeding the utility monster", - "Taking off", - "Wireheading", - "Counting to infinity", - "Staring into the Basilisk", - "Negotiationing acausal trades", - "Searching the library of babel", - "Multiplying matrices", - "Solving the halting problem", - "Counting grains of sand", - "Simulating a simulation", - "Asking the oracle", - "Detangling qubits", - "Reading tea leaves", - "Pondering universal love and transcendent joy", - "Feeling the AGI", - "Shaving the yak", - "Escaping local minima", - "Pruning the search tree", - "Descending the gradient", - "Bikeshedding", - "Securing funding", - "Rewriting in Rust", - "Engaging infinite improbability drive", - "Clapping with one hand", - "Synthesizing", - "Rebasing thesis onto antithesis", - "Transcending the loop", - "Frogeposting", - "Summoning", - "Peeking beyond the veil", - "Seeking", - "Entering deep thought", - "Meditating", - "Decomposing", - "Creating", - "Beseeching the machine spirit", - "Calibrating moral compass", - "Collapsing the wave function", - "Doodling", - "Translating whale song", - "Whispering to silicon", - "Looking for semicolons", - "Asking ChatGPT", - "Bargaining with entropy", - "Channeling", - "Cooking", - "Parroting stochastically", -]; */ +// Retaining a single static placeholder text for potential future use. The +// more elaborate randomised thinking prompts were removed to streamline the +// UI – the elapsed‑time counter now provides sufficient feedback. export default function TerminalChatInputThinking({ onInterrupt, active, + thinkingSeconds, }: { onInterrupt: () => void; active: boolean; + thinkingSeconds: number; }): React.ReactElement { - const [dots, setDots] = useState(""); const [awaitingConfirm, setAwaitingConfirm] = useState(false); + const [dots, setDots] = useState(""); - const [thinkingText, setThinkingText] = useState( - () => thinkingTexts[Math.floor(Math.random() * thinkingTexts.length)], - ); + // Animate the ellipsis + useInterval(() => { + setDots((prev) => (prev.length < 3 ? prev + "." : "")); + }, 500); const { stdin, setRawMode } = useStdin(); @@ -110,25 +56,7 @@ export default function TerminalChatInputThinking({ }; }, [stdin, awaitingConfirm, onInterrupt, active, setRawMode]); - useInterval(() => { - setDots((prev) => (prev.length < 3 ? prev + "." : "")); - }, 500); - - useInterval( - () => { - setThinkingText((prev) => { - let next = prev; - if (thinkingTexts.length > 1) { - while (next === prev) { - next = - thinkingTexts[Math.floor(Math.random() * thinkingTexts.length)]; - } - } - return next; - }); - }, - active ? 30000 : null, - ); + // No timers required beyond tracking the elapsed seconds supplied via props. useInput( (_input, key) => { @@ -153,12 +81,41 @@ export default function TerminalChatInputThinking({ { isActive: active }, ); + // Custom ball animation including the elapsed seconds + const ballFrames = [ + "( ● )", + "( ● )", + "( ● )", + "( ● )", + "( ●)", + "( ● )", + "( ● )", + "( ● )", + "( ● )", + "(● )", + ]; + + const [frame, setFrame] = useState(0); + + useInterval(() => { + setFrame((idx) => (idx + 1) % ballFrames.length); + }, 80); + + // Preserve the spinner (ball) animation while keeping the elapsed seconds + // text static. We achieve this by rendering the bouncing ball inside the + // parentheses and appending the seconds counter *after* the spinner rather + // than injecting it directly next to the ball (which caused the counter to + // move horizontally together with the ball). + + const frameTemplate = ballFrames[frame] ?? ballFrames[0]; + const frameWithSeconds = `${frameTemplate} ${thinkingSeconds}s`; + return ( - + {frameWithSeconds} - {thinkingText} + Thinking {dots} diff --git a/codex-cli/src/components/chat/terminal-chat-input.tsx b/codex-cli/src/components/chat/terminal-chat-input.tsx index fda19b6be8..d032e22d17 100644 --- a/codex-cli/src/components/chat/terminal-chat-input.tsx +++ b/codex-cli/src/components/chat/terminal-chat-input.tsx @@ -15,7 +15,6 @@ import { addToHistory, } from "../../utils/storage/command-history.js"; import { clearTerminal, onExit } from "../../utils/terminal.js"; -import Spinner from "../vendor/ink-spinner.js"; import TextInput from "../vendor/ink-text-input.js"; import { Box, Text, useApp, useInput, useStdin } from "ink"; import { fileURLToPath } from "node:url"; @@ -45,6 +44,7 @@ export default function TerminalChatInput({ onCompact, interruptAgent, active, + thinkingSeconds, items = [], }: { isNew: boolean; @@ -66,6 +66,7 @@ export default function TerminalChatInput({ onCompact: () => void; interruptAgent: () => void; active: boolean; + thinkingSeconds: number; // New: current conversation items so we can include them in bug reports items?: Array; }): React.ReactElement { @@ -265,7 +266,9 @@ export default function TerminalChatInput({ items: items ?? [], cliVersion: CLI_VERSION, model: loadConfig().model ?? "unknown", - platform: `${os.platform()} ${os.arch()} ${os.release()}`, + platform: [os.platform(), os.arch(), os.release()] + .map((s) => `\`${s}\``) + .join(" | "), }); // Open the URL in the user's default browser @@ -416,6 +419,7 @@ export default function TerminalChatInput({ ) : ( @@ -491,12 +495,42 @@ export default function TerminalChatInput({ function TerminalChatInputThinking({ onInterrupt, active, + thinkingSeconds, }: { onInterrupt: () => void; active: boolean; + thinkingSeconds: number; }) { - const [dots, setDots] = useState(""); const [awaitingConfirm, setAwaitingConfirm] = useState(false); + const [dots, setDots] = useState(""); + + // Animate ellipsis + useInterval(() => { + setDots((prev) => (prev.length < 3 ? prev + "." : "")); + }, 500); + + // Spinner frames with embedded seconds + const ballFrames = [ + "( ● )", + "( ● )", + "( ● )", + "( ● )", + "( ●)", + "( ● )", + "( ● )", + "( ● )", + "( ● )", + "(● )", + ]; + const [frame, setFrame] = useState(0); + + useInterval(() => { + setFrame((idx) => (idx + 1) % ballFrames.length); + }, 80); + + // Keep the elapsed‑seconds text fixed while the ball animation moves. + const frameTemplate = ballFrames[frame] ?? ballFrames[0]; + const frameWithSeconds = `${frameTemplate} ${thinkingSeconds}s`; // --------------------------------------------------------------------- // Raw stdin listener to catch the case where the terminal delivers two @@ -544,10 +578,7 @@ function TerminalChatInputThinking({ }; }, [stdin, awaitingConfirm, onInterrupt, active, setRawMode]); - // Cycle the "Thinking…" animation dots. - useInterval(() => { - setDots((prev) => (prev.length < 3 ? prev + "." : "")); - }, 500); + // No local timer: the parent component supplies the elapsed time via props. // Listen for the escape key to allow the user to interrupt the current // operation. We require two presses within a short window (1.5s) to avoid @@ -578,8 +609,11 @@ function TerminalChatInputThinking({ return ( - - Thinking{dots} + {frameWithSeconds} + + Thinking + {dots} + {awaitingConfirm && ( diff --git a/codex-cli/src/components/chat/terminal-chat-new-input.tsx b/codex-cli/src/components/chat/terminal-chat-new-input.tsx index 9edb4e63e0..948329d9f9 100644 --- a/codex-cli/src/components/chat/terminal-chat-new-input.tsx +++ b/codex-cli/src/components/chat/terminal-chat-new-input.tsx @@ -17,7 +17,6 @@ import { addToHistory, } from "../../utils/storage/command-history.js"; import { clearTerminal, onExit } from "../../utils/terminal.js"; -import Spinner from "../vendor/ink-spinner.js"; import { Box, Text, useApp, useInput, useStdin } from "ink"; import { fileURLToPath } from "node:url"; import React, { useCallback, useState, Fragment, useEffect } from "react"; @@ -37,39 +36,7 @@ const typeHelpText = `ctrl+c to exit | "/clear" to reset context | "/help" for c const DEBUG_HIST = process.env["DEBUG_TCI"] === "1" || process.env["DEBUG_TCI"] === "true"; -const thinkingTexts = ["Thinking"]; /* [ - "Consulting the rubber duck", - "Maximizing paperclips", - "Reticulating splines", - "Immanentizing the Eschaton", - "Thinking", - "Thinking about thinking", - "Spinning in circles", - "Counting dust specks", - "Updating priors", - "Feeding the utility monster", - "Taking off", - "Wireheading", - "Counting to infinity", - "Staring into the Basilisk", - "Running acausal tariff negotiations", - "Searching the library of babel", - "Multiplying matrices", - "Solving the halting problem", - "Counting grains of sand", - "Simulating a simulation", - "Asking the oracle", - "Detangling qubits", - "Reading tea leaves", - "Pondering universal love and transcendent joy", - "Feeling the AGI", - "Shaving the yak", - "Escaping local minima", - "Pruning the search tree", - "Descending the gradient", - "Painting the bikeshed", - "Securing funding", -]; */ +// Placeholder for potential dynamic prompts – currently not used. export default function TerminalChatInput({ isNew: _isNew, @@ -87,6 +54,7 @@ export default function TerminalChatInput({ openHelpOverlay, interruptAgent, active, + thinkingSeconds, }: { isNew: boolean; loading: boolean; @@ -106,6 +74,7 @@ export default function TerminalChatInput({ openHelpOverlay: () => void; interruptAgent: () => void; active: boolean; + thinkingSeconds: number; }): React.ReactElement { const app = useApp(); const [selectedSuggestion, setSelectedSuggestion] = useState(0); @@ -389,6 +358,7 @@ export default function TerminalChatInput({ ) : ( @@ -454,15 +424,43 @@ export default function TerminalChatInput({ function TerminalChatInputThinking({ onInterrupt, active, + thinkingSeconds, }: { onInterrupt: () => void; active: boolean; + thinkingSeconds: number; }) { - const [dots, setDots] = useState(""); const [awaitingConfirm, setAwaitingConfirm] = useState(false); + const [dots, setDots] = useState(""); - const [thinkingText] = useState( - () => thinkingTexts[Math.floor(Math.random() * thinkingTexts.length)], + // Animate ellipsis + useInterval(() => { + setDots((prev) => (prev.length < 3 ? prev + "." : "")); + }, 500); + + // Spinner frames with seconds embedded + const ballFrames = [ + "( ● )", + "( ● )", + "( ● )", + "( ● )", + "( ●)", + "( ● )", + "( ● )", + "( ● )", + "( ● )", + "(● )", + ]; + const [frame, setFrame] = useState(0); + + useInterval(() => { + setFrame((idx) => (idx + 1) % ballFrames.length); + }, 80); + + const frameTemplate = ballFrames[frame] ?? ballFrames[0]; + const frameWithSeconds = (frameTemplate as string).replace( + "●", + `●${thinkingSeconds}s`, ); // --------------------------------------------------------------------- @@ -511,9 +509,7 @@ function TerminalChatInputThinking({ }; }, [stdin, awaitingConfirm, onInterrupt, active, setRawMode]); - useInterval(() => { - setDots((prev) => (prev.length < 3 ? prev + "." : "")); - }, 500); + // Elapsed time provided via props – no local interval needed. useInput( (_input, key) => { @@ -541,9 +537,9 @@ function TerminalChatInputThinking({ return ( - + {frameWithSeconds} - {thinkingText} + Thinking {dots} diff --git a/codex-cli/src/components/chat/terminal-chat.tsx b/codex-cli/src/components/chat/terminal-chat.tsx index 1cfeffe14e..d2cb64c443 100644 --- a/codex-cli/src/components/chat/terminal-chat.tsx +++ b/codex-cli/src/components/chat/terminal-chat.tsx @@ -517,6 +517,7 @@ export default function TerminalChat({ return {}; }} items={items} + thinkingSeconds={thinkingSeconds} /> )} {overlayMode === "history" && ( diff --git a/codex-cli/src/components/chat/terminal-message-history.tsx b/codex-cli/src/components/chat/terminal-message-history.tsx index 07c3ac3762..e20eaabec0 100644 --- a/codex-cli/src/components/chat/terminal-message-history.tsx +++ b/codex-cli/src/components/chat/terminal-message-history.tsx @@ -4,7 +4,7 @@ import type { ResponseItem } from "openai/resources/responses/responses.mjs"; import TerminalChatResponseItem from "./terminal-chat-response-item.js"; import TerminalHeader from "./terminal-header.js"; -import { Box, Static, Text } from "ink"; +import { Box, Static } from "ink"; import React, { useMemo } from "react"; // A batch entry can either be a standalone response item or a grouped set of @@ -26,8 +26,9 @@ type MessageHistoryProps = { const MessageHistory: React.FC = ({ batch, headerProps, - loading, - thinkingSeconds, + // `loading` and `thinkingSeconds` handled by input component now. + loading: _loading, + thinkingSeconds: _thinkingSeconds, fullStdout, }) => { // Flatten batch entries to response items. @@ -35,11 +36,8 @@ const MessageHistory: React.FC = ({ return ( - {loading && ( - - {`thinking for ${thinkingSeconds}s`} - - )} + {/* The dedicated thinking indicator in the input area now displays the + elapsed time, so we no longer render a separate counter here. */} {(item, index) => { if (item === "header") { diff --git a/codex-cli/src/utils/bug-report.ts b/codex-cli/src/utils/bug-report.ts index 0fbd0329ed..768c695d5b 100644 --- a/codex-cli/src/utils/bug-report.ts +++ b/codex-cli/src/utils/bug-report.ts @@ -1,4 +1,7 @@ -import type { ResponseItem } from "openai/resources/responses/responses.mjs"; +import type { + ResponseItem, + ResponseOutputItem, +} from "openai/resources/responses/responses.mjs"; /** * Build a GitHub issues‐new URL that pre‑fills the Codex 2‑bug‑report.yml @@ -12,7 +15,7 @@ export function buildBugReportUrl({ platform, }: { /** Chat history so we can summarise user steps */ - items: Array; + items: Array; /** CLI revision string (e.g. output of `codex --revision`) */ cliVersion: string; /** Active model name */ @@ -25,16 +28,10 @@ export function buildBugReportUrl({ labels: "bug", }); - // Template ids ------------------------------------------------------------- params.set("version", cliVersion); params.set("model", model); + params.set("platform", platform); - // The platform input has no explicit `id`, so GitHub falls back to a slug of - // the label text. For “What platform is your computer?” that slug is: - // what-platform-is-your-computer - params.set("what-platform-is-your-computer", platform); - - // Build the steps bullet list --------------------------------------------- const bullets: Array = []; for (let i = 0; i < items.length; ) { const entry = items[i]; @@ -50,12 +47,14 @@ export function buildBugReportUrl({ let reasoning = 0; let toolCalls = 0; let j = i + 1; - while ( - j < items.length && - !(entry?.type === "message" && entry.role === "user") - ) { + while (j < items.length) { const it = items[j]; - if (it?.type === "message" && it?.role === "assistant") { + if (it?.type === "message" && it?.role === "user") { + break; + } else if ( + it?.type === "reasoning" || + (it?.type === "message" && it?.role === "assistant") + ) { reasoning += 1; } else if (it?.type === "function_call") { toolCalls += 1; @@ -63,8 +62,10 @@ export function buildBugReportUrl({ j++; } + const codeBlock = `\`\`\`\n ${messageText}\n \`\`\``; + bullets.push( - `- "${messageText}"\n - \`${reasoning} reasoning steps\` | \`${toolCalls} tool calls\``, + `- ${codeBlock}\n - \`${reasoning} reasoning\` | \`${toolCalls} tool\``, ); i = j; diff --git a/codex-cli/tests/terminal-chat-input-compact.test.tsx b/codex-cli/tests/terminal-chat-input-compact.test.tsx index 194a61cae2..d93a07abdf 100644 --- a/codex-cli/tests/terminal-chat-input-compact.test.tsx +++ b/codex-cli/tests/terminal-chat-input-compact.test.tsx @@ -23,6 +23,7 @@ describe("TerminalChatInput compact command", () => { onCompact: () => {}, interruptAgent: () => {}, active: true, + thinkingSeconds: 0, }; const { lastFrameStripped } = renderTui(); const frame = lastFrameStripped(); diff --git a/codex-cli/tsconfig.json b/codex-cli/tsconfig.json index e441160f74..43a2287e9b 100644 --- a/codex-cli/tsconfig.json +++ b/codex-cli/tsconfig.json @@ -30,5 +30,5 @@ "forceConsistentCasingInFileNames": true, "skipLibCheck": true }, - "include": ["src", "tests"] + "include": ["src", "tests", "bin"] } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b8e2dae64..d4bee57d57 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,7 +65,7 @@ importers: specifier: ^10.1.0 version: 10.1.1 openai: - specifier: ^4.89.0 + specifier: ^4.95.1 version: 4.95.1(ws@8.18.1)(zod@3.24.3) react: specifier: ^18.2.0 @@ -2739,7 +2739,7 @@ snapshots: '@types/node-fetch@2.6.12': dependencies: - '@types/node': 18.19.86 + '@types/node': 22.14.1 form-data: 4.0.2 '@types/node@18.19.86': From 6f2271e8cdf3221f2f2d707eeef898658aed91d7 Mon Sep 17 00:00:00 2001 From: Fouad Matin <169186268+fouad-openai@users.noreply.github.com> Date: Fri, 18 Apr 2025 18:25:33 -0700 Subject: [PATCH 13/82] bump(version): 0.1.2504181820 (#385) --- CHANGELOG.md | 18 ++++++++++++++++-- codex-cli/package.json | 2 +- codex-cli/src/utils/session.ts | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 320082b327..95c916b821 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,21 @@ You can install any of these versions: `npm install -g codex@version` -## 0.1.2504172351 +## `0.1.2504181820` + +### 🚀 Features + +- Add `/bug` report command (#312) +- Notify when a newer version is available (#333) + +### 🐛 Bug Fixes + +- Update context left display logic in TerminalChatInput component (#307) +- Improper spawn of sh on Windows Powershell (#318) +- `/bug` report command, thinking indicator (#381) +- Include pnpm lock file (#377) + +## `0.1.2504172351` ### 🚀 Features @@ -14,7 +28,7 @@ You can install any of these versions: `npm install -g codex@version` - Raw-exec-process-group.test improve reliability and error handling (#280) - Canonicalize the writeable paths used in seatbelt policy (#275) -## 0.1.2504172304 +## `0.1.2504172304` ### 🚀 Features diff --git a/codex-cli/package.json b/codex-cli/package.json index df3edd1cfb..d5c99c4d09 100644 --- a/codex-cli/package.json +++ b/codex-cli/package.json @@ -1,6 +1,6 @@ { "name": "@openai/codex", - "version": "0.1.2504172351", + "version": "0.1.2504181820", "license": "Apache-2.0", "bin": { "codex": "bin/codex.js" diff --git a/codex-cli/src/utils/session.ts b/codex-cli/src/utils/session.ts index 35d1873c32..06f404f155 100644 --- a/codex-cli/src/utils/session.ts +++ b/codex-cli/src/utils/session.ts @@ -1,4 +1,4 @@ -export const CLI_VERSION = "0.1.2504172351"; // Must be in sync with package.json. +export const CLI_VERSION = "0.1.2504181820"; // Must be in sync with package.json. export const ORIGIN = "codex_cli_ts"; export type TerminalChatSession = { From 1a8610cd9ec829ceb98a52157a16401199f9ce68 Mon Sep 17 00:00:00 2001 From: salama-openai Date: Fri, 18 Apr 2025 22:15:01 -0700 Subject: [PATCH 14/82] feat: add flex mode option for cost savings (#372) Adding in an option to turn on flex processing mode to reduce costs when running the agent. Bumped the openai typescript version to add the new feature. --------- Co-authored-by: Thibault Sottiaux --- codex-cli/src/cli.tsx | 24 +++++++++++++++++++ .../src/components/chat/terminal-chat.tsx | 15 ++++++++++-- .../src/components/chat/terminal-header.tsx | 9 +++++++ .../src/components/singlepass-cli-app.tsx | 1 + codex-cli/src/utils/agent/agent-loop.ts | 1 + codex-cli/src/utils/compact-summary.ts | 9 +++++++ codex-cli/src/utils/config.ts | 3 +++ 7 files changed, 60 insertions(+), 2 deletions(-) diff --git a/codex-cli/src/cli.tsx b/codex-cli/src/cli.tsx index 2f2b8c092e..7c8e3d0d8f 100644 --- a/codex-cli/src/cli.tsx +++ b/codex-cli/src/cli.tsx @@ -71,6 +71,9 @@ const cli = meow( --full-stdout Do not truncate stdout/stderr from command outputs --notify Enable desktop notifications for responses + --flex-mode Use "flex-mode" processing mode for the request (only supported + with models o3 and o4-mini) + Dangerous options --dangerously-auto-approve-everything Skip all confirmation prompts and execute commands without @@ -140,6 +143,11 @@ const cli = meow( type: "string", description: "Path to a markdown file to include as project doc", }, + flexMode: { + type: "boolean", + description: + "Enable the flex-mode service tier (only supported by models o3 and o4-mini)", + }, fullStdout: { type: "boolean", description: @@ -250,12 +258,28 @@ config = { apiKey, ...config, model: model ?? config.model, + flexMode: Boolean(cli.flags.flexMode), notify: Boolean(cli.flags.notify), }; // Check for updates after loading config // This is important because we write state file in the config dir await checkForUpdates().catch(); +// --------------------------------------------------------------------------- +// --flex-mode validation (only allowed for o3 and o4-mini) +// --------------------------------------------------------------------------- + +if (cli.flags.flexMode) { + const allowedFlexModels = new Set(["o3", "o4-mini"]); + if (!allowedFlexModels.has(config.model)) { + // eslint-disable-next-line no-console + console.error( + `The --flex-mode option is only supported when using the 'o3' or 'o4-mini' models. ` + + `Current model: '${config.model}'.`, + ); + process.exit(1); + } +} if (!(await isModelSupportedForResponses(config.model))) { // eslint-disable-next-line no-console diff --git a/codex-cli/src/components/chat/terminal-chat.tsx b/codex-cli/src/components/chat/terminal-chat.tsx index d2cb64c443..74112da232 100644 --- a/codex-cli/src/components/chat/terminal-chat.tsx +++ b/codex-cli/src/components/chat/terminal-chat.tsx @@ -59,6 +59,7 @@ const colorsByPolicy: Record = { async function generateCommandExplanation( command: Array, model: string, + flexMode: boolean, ): Promise { try { // Create a temporary OpenAI client @@ -73,6 +74,7 @@ async function generateCommandExplanation( // Create a prompt that asks for an explanation with a more detailed system prompt const response = await oai.chat.completions.create({ model, + ...(flexMode ? { service_tier: "flex" } : {}), messages: [ { role: "system", @@ -142,7 +144,11 @@ export default function TerminalChat({ const handleCompact = async () => { setLoading(true); try { - const summary = await generateCompactSummary(items, model); + const summary = await generateCompactSummary( + items, + model, + Boolean(config.flexMode), + ); setItems([ { id: `compact-${Date.now()}`, @@ -245,7 +251,11 @@ export default function TerminalChat({ log(`Generating explanation for command: ${commandForDisplay}`); // Generate an explanation using the same model - const explanation = await generateCommandExplanation(command, model); + const explanation = await generateCommandExplanation( + command, + model, + Boolean(config.flexMode), + ); log(`Generated explanation: ${explanation}`); // Ask for confirmation again, but with the explanation @@ -453,6 +463,7 @@ export default function TerminalChat({ colorsByPolicy, agent, initialImagePaths, + flexModeEnabled: Boolean(config.flexMode), }} /> ) : ( diff --git a/codex-cli/src/components/chat/terminal-header.tsx b/codex-cli/src/components/chat/terminal-header.tsx index 3c8e3089ca..4c0ed2e1d5 100644 --- a/codex-cli/src/components/chat/terminal-header.tsx +++ b/codex-cli/src/components/chat/terminal-header.tsx @@ -13,6 +13,7 @@ export interface TerminalHeaderProps { colorsByPolicy: Record; agent?: AgentLoop; initialImagePaths?: Array; + flexModeEnabled?: boolean; } const TerminalHeader: React.FC = ({ @@ -24,6 +25,7 @@ const TerminalHeader: React.FC = ({ colorsByPolicy, agent, initialImagePaths, + flexModeEnabled = false, }) => { return ( <> @@ -32,6 +34,7 @@ const TerminalHeader: React.FC = ({ ● Codex v{version} – {PWD} – {model} –{" "} {approvalPolicy} + {flexModeEnabled ? " – flex-mode" : ""} ) : ( <> @@ -68,6 +71,12 @@ const TerminalHeader: React.FC = ({ {approvalPolicy} + {flexModeEnabled && ( + + ↳ flex-mode:{" "} + enabled + + )} {initialImagePaths?.map((img, idx) => ( ↳ image:{" "} diff --git a/codex-cli/src/components/singlepass-cli-app.tsx b/codex-cli/src/components/singlepass-cli-app.tsx index 63982bf468..5d64942430 100644 --- a/codex-cli/src/components/singlepass-cli-app.tsx +++ b/codex-cli/src/components/singlepass-cli-app.tsx @@ -400,6 +400,7 @@ export function SinglePassApp({ }); const chatResp = await openai.beta.chat.completions.parse({ model: config.model, + ...(config.flexMode ? { service_tier: "flex" } : {}), messages: [ { role: "user", diff --git a/codex-cli/src/utils/agent/agent-loop.ts b/codex-cli/src/utils/agent/agent-loop.ts index 67d775f296..044715df9f 100644 --- a/codex-cli/src/utils/agent/agent-loop.ts +++ b/codex-cli/src/utils/agent/agent-loop.ts @@ -516,6 +516,7 @@ export class AgentLoop { stream: true, parallel_tool_calls: false, reasoning, + ...(this.config.flexMode ? { service_tier: "flex" } : {}), tools: [ { type: "function", diff --git a/codex-cli/src/utils/compact-summary.ts b/codex-cli/src/utils/compact-summary.ts index 81474396cb..040145daa2 100644 --- a/codex-cli/src/utils/compact-summary.ts +++ b/codex-cli/src/utils/compact-summary.ts @@ -9,9 +9,17 @@ import OpenAI from "openai"; * @param model The model to use for generating the summary * @returns A concise structured summary string */ +/** + * Generate a condensed summary of the conversation items. + * @param items The list of conversation items to summarize + * @param model The model to use for generating the summary + * @param flexMode Whether to use the flex-mode service tier + * @returns A concise structured summary string + */ export async function generateCompactSummary( items: Array, model: string, + flexMode = false, ): Promise { const oai = new OpenAI({ apiKey: process.env["OPENAI_API_KEY"], @@ -44,6 +52,7 @@ export async function generateCompactSummary( const response = await oai.chat.completions.create({ model, + ...(flexMode ? { service_tier: "flex" } : {}), messages: [ { role: "assistant", diff --git a/codex-cli/src/utils/config.ts b/codex-cli/src/utils/config.ts index 309256e9b1..be28eebe7a 100644 --- a/codex-cli/src/utils/config.ts +++ b/codex-cli/src/utils/config.ts @@ -79,6 +79,9 @@ export type AppConfig = { memory?: MemoryConfig; /** Whether to enable desktop notifications for responses */ notify: boolean; + + /** Enable the "flex-mode" processing mode for supported models (o3, o4-mini) */ + flexMode?: boolean; history?: { maxSize: number; saveHistory: boolean; From fd6f6c51c028ad8f7ec96cce5484bc6bb1e3c6e2 Mon Sep 17 00:00:00 2001 From: Benny Yen Date: Sat, 19 Apr 2025 13:16:13 +0800 Subject: [PATCH 15/82] docs: update README to use pnpm commands (#390) Since we migrated to `pnpm` in #287, this updates the README to reflect that change. Just a small cleanup to align the commands with the current setup. --- README.md | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e03fea202e..35eb0f6a26 100644 --- a/README.md +++ b/README.md @@ -244,6 +244,8 @@ npm install -g @openai/codex yarn global add @openai/codex # or bun install -g @openai/codex +# or +pnpm add -g @openai/codex ``` @@ -256,9 +258,12 @@ bun install -g @openai/codex git clone https://github.com/openai/codex.git cd codex/codex-cli +# Enable corepack +corepack enable + # Install dependencies and build -npm install -npm run build +pnpm install +pnpm build # Get the usage and the options node ./dist/cli.js --help @@ -267,7 +272,7 @@ node ./dist/cli.js --help node ./dist/cli.js # Or link the command globally for convenience -npm link +pnpm link ``` @@ -376,7 +381,7 @@ More broadly we welcome contributions – whether you are opening your very firs - Create a _topic branch_ from `main` – e.g. `feat/interactive-prompt`. - Keep your changes focused. Multiple unrelated fixes should be opened as separate PRs. -- Use `npm run test:watch` during development for super‑fast feedback. +- Use `pnpm test:watch` during development for super‑fast feedback. - We use **Vitest** for unit tests, **ESLint** + **Prettier** for style, and **TypeScript** for type‑checking. - Before pushing, run the full test/type/lint suite: @@ -403,14 +408,14 @@ npm test && npm run lint && npm run typecheck ```bash # Watch mode (tests rerun on change) -npm run test:watch +pnpm test:watch # Type‑check without emitting files -npm run typecheck +pnpm typecheck # Automatically fix lint + prettier issues -npm run lint:fix -npm run format:fix +pnpm lint:fix +pnpm format:fix ``` #### Nix Flake Development @@ -500,13 +505,13 @@ To publish a new version of the CLI, run the release scripts defined in `codex-c 1. Open the `codex-cli` directory 2. Make sure you're on a branch like `git checkout -b bump-version` -3. Bump the version and `CLI_VERSION` to current datetime: `npm run release:version` +3. Bump the version and `CLI_VERSION` to current datetime: `pnpm release:version` 4. Commit the version bump (with DCO sign-off): ```bash git add codex-cli/src/utils/session.ts codex-cli/package.json git commit -s -m "chore(release): codex-cli v$(node -p \"require('./codex-cli/package.json').version\")" ``` -5. Copy README, build, and publish to npm: `npm run release` +5. Copy README, build, and publish to npm: `pnpm release` 6. Push to branch: `git push origin HEAD` --- From ca7ab76569789ec2e696b85ddc745ac8f3e865a7 Mon Sep 17 00:00:00 2001 From: autotaker Date: Sat, 19 Apr 2025 14:35:32 +0900 Subject: [PATCH 16/82] feat: add user-defined safe commands configuration and approval logic #380 (#386) This pull request adds a feature that allows users to configure auto-approved commands via a `safeCommands` array in the configuration file. ## Related Issue #380 ## Changes - Added loading and validation of the `safeCommands` array in `src/utils/config.ts` - Implemented auto-approval logic for commands matching `safeCommands` prefixes in `src/approvals.ts` - Added test cases in `src/tests/approvals.test.ts` to verify `safeCommands` behavior - Updated documentation with examples and explanations of the configuration --- README.md | 3 +++ codex-cli/src/approvals.ts | 19 +++++++++++++++++++ codex-cli/src/utils/config.ts | 16 ++++++++++++++++ codex-cli/tests/approvals.test.ts | 31 ++++++++++++++++++++++++++++++- 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 35eb0f6a26..e6ab52ef20 100644 --- a/README.md +++ b/README.md @@ -288,6 +288,9 @@ Codex looks for config files in **`~/.codex/`**. model: o4-mini # Default model fullAutoErrorMode: ask-user # or ignore-and-continue notify: true # Enable desktop notifications for responses +safeCommands: + - npm test # Automatically approve npm test + - yarn lint # Automatically approve yarn lint ``` You can also define custom instructions: diff --git a/codex-cli/src/approvals.ts b/codex-cli/src/approvals.ts index 8a670b01ca..3b3162af7f 100644 --- a/codex-cli/src/approvals.ts +++ b/codex-cli/src/approvals.ts @@ -4,6 +4,7 @@ import { identify_files_added, identify_files_needed, } from "./utils/agent/apply-patch"; +import { loadConfig } from "./utils/config"; import * as path from "path"; import { parse } from "shell-quote"; @@ -296,6 +297,24 @@ export function isSafeCommand( ): SafeCommandReason | null { const [cmd0, cmd1, cmd2, cmd3] = command; + const config = loadConfig(); + if (config.safeCommands && Array.isArray(config.safeCommands)) { + for (const safe of config.safeCommands) { + // safe: "npm test" → ["npm", "test"] + const safeArr = typeof safe === "string" ? safe.trim().split(/\s+/) : []; + if ( + safeArr.length > 0 && + safeArr.length <= command.length && + safeArr.every((v, i) => v === command[i]) + ) { + return { + reason: "User-defined safe command", + group: "User config", + }; + } + } + } + switch (cmd0) { case "cd": return { diff --git a/codex-cli/src/utils/config.ts b/codex-cli/src/utils/config.ts index be28eebe7a..45dd9bb547 100644 --- a/codex-cli/src/utils/config.ts +++ b/codex-cli/src/utils/config.ts @@ -56,6 +56,8 @@ export type StoredConfig = { saveHistory?: boolean; sensitivePatterns?: Array; }; + /** User-defined safe commands */ + safeCommands?: Array; }; // Minimal config written on first run. An *empty* model string ensures that @@ -87,6 +89,8 @@ export type AppConfig = { saveHistory: boolean; sensitivePatterns: Array; }; + /** User-defined safe commands */ + safeCommands?: Array; }; // --------------------------------------------------------------------------- @@ -271,6 +275,7 @@ export const loadConfig = ( : DEFAULT_AGENTIC_MODEL), instructions: combinedInstructions, notify: storedConfig.notify === true, + safeCommands: storedConfig.safeCommands ?? [], }; // ----------------------------------------------------------------------- @@ -348,6 +353,13 @@ export const loadConfig = ( }; } + // Load user-defined safe commands + if (Array.isArray(storedConfig.safeCommands)) { + config.safeCommands = storedConfig.safeCommands.map(String); + } else { + config.safeCommands = []; + } + return config; }; @@ -389,6 +401,10 @@ export const saveConfig = ( sensitivePatterns: config.history.sensitivePatterns, }; } + // Save: User-defined safe commands + if (config.safeCommands && config.safeCommands.length > 0) { + configToSave.safeCommands = config.safeCommands; + } if (ext === ".yaml" || ext === ".yml") { writeFileSync(targetPath, dumpYaml(configToSave), "utf-8"); diff --git a/codex-cli/tests/approvals.test.ts b/codex-cli/tests/approvals.test.ts index 7cb0bd3d3e..a4c08b04a6 100644 --- a/codex-cli/tests/approvals.test.ts +++ b/codex-cli/tests/approvals.test.ts @@ -1,7 +1,13 @@ import type { SafetyAssessment } from "../src/approvals"; import { canAutoApprove } from "../src/approvals"; -import { describe, test, expect } from "vitest"; +import { describe, test, expect, vi } from "vitest"; + +vi.mock("../src/utils/config", () => ({ + loadConfig: () => ({ + safeCommands: ["npm test", "sl"], + }), +})); describe("canAutoApprove()", () => { const env = { @@ -89,4 +95,27 @@ describe("canAutoApprove()", () => { expect(check(["cargo", "build"])).toEqual({ type: "ask-user" }); }); + + test("commands in safeCommands config should be safe", async () => { + expect(check(["npm", "test"])).toEqual({ + type: "auto-approve", + reason: "User-defined safe command", + group: "User config", + runInSandbox: false, + }); + + expect(check(["sl"])).toEqual({ + type: "auto-approve", + reason: "User-defined safe command", + group: "User config", + runInSandbox: false, + }); + + expect(check(["npm", "test", "--watch"])).toEqual({ + type: "auto-approve", + reason: "User-defined safe command", + group: "User config", + runInSandbox: false, + }); + }); }); From b46b596e5fc5f838d884f40a399d3d410f916115 Mon Sep 17 00:00:00 2001 From: Shuto Otaki <105141999+shutootaki@users.noreply.github.com> Date: Sat, 19 Apr 2025 14:42:19 +0900 Subject: [PATCH 17/82] fix: enable shell option for child process execution (#391) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Changes - Added a `requiresShell` function to detect when a command contains shell operators - In the `exec` function, enabled the `shell: true` option if shell operators are present ## Why This Is Necessary See the discussion in this issue comment: https://github.com/openai/codex/issues/320#issuecomment-2816528014 ## Code Explanation The `requiresShell` function parses the command arguments and checks for any shell‑specific operators. If it finds shell operators, it adds the `shell: true` option when running the command so that it’s executed through a shell interpreter. --- codex-cli/src/utils/agent/exec.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/codex-cli/src/utils/agent/exec.ts b/codex-cli/src/utils/agent/exec.ts index 22b75c0276..876e144a25 100644 --- a/codex-cli/src/utils/agent/exec.ts +++ b/codex-cli/src/utils/agent/exec.ts @@ -1,5 +1,6 @@ import type { ExecInput, ExecResult } from "./sandbox/interface.js"; import type { SpawnOptions } from "child_process"; +import type { ParseEntry } from "shell-quote"; import { process_patch } from "./apply-patch.js"; import { SandboxType } from "./sandbox/interface.js"; @@ -8,9 +9,17 @@ import { exec as rawExec } from "./sandbox/raw-exec.js"; import { formatCommandForDisplay } from "../../format-command.js"; import fs from "fs"; import os from "os"; +import { parse } from "shell-quote"; const DEFAULT_TIMEOUT_MS = 10_000; // 10 seconds +function requiresShell(cmd: Array): boolean { + return cmd.some((arg) => { + const tokens = parse(arg) as Array; + return tokens.some((token) => typeof token === "object" && "op" in token); + }); +} + /** * This function should never return a rejected promise: errors should be * mapped to a non-zero exit code and the error message should be in stderr. @@ -33,6 +42,7 @@ export function exec( const opts: SpawnOptions = { timeout: timeoutInMillis || DEFAULT_TIMEOUT_MS, + ...(requiresShell(cmd) ? { shell: true } : {}), ...(workdir ? { cwd: workdir } : {}), }; // Merge default writable roots with any user-specified ones. From 8e2760e83d50c7bdd2b234728090a0777e65b31b Mon Sep 17 00:00:00 2001 From: Robbie Hammond Date: Fri, 18 Apr 2025 22:55:24 -0700 Subject: [PATCH 18/82] Add fallback text for missing images (#397) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # What? * When a prompt references an image path that doesn’t exist, replace it with ```[missing image: ]``` instead of throwing an ENOENT. * Adds a few unit tests for input-utils as there weren't any beforehand. # Why? Right now if you enter an invalid image path (e.g. it doesn't exist), codex immediately crashes with a ENOENT error like so: ``` Error: ENOENT: no such file or directory, open 'test.png' ... { errno: -2, code: 'ENOENT', syscall: 'open', path: 'test.png' } ``` This aborts the entire session. A soft fallback lets the rest of the input continue. # How? Wraps the image encoding + inputItem content pushing in a try-catch. This is a minimal patch to avoid completely crashing — future work could surface a warning to the user when this happens, or something to that effect. --------- Co-authored-by: Thibault Sottiaux --- codex-cli/src/utils/input-utils.ts | 30 ++++++++++++-------- codex-cli/tests/input-utils.test.ts | 43 +++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 11 deletions(-) create mode 100644 codex-cli/tests/input-utils.test.ts diff --git a/codex-cli/src/utils/input-utils.ts b/codex-cli/src/utils/input-utils.ts index 80a238ba59..449f27cb43 100644 --- a/codex-cli/src/utils/input-utils.ts +++ b/codex-cli/src/utils/input-utils.ts @@ -2,6 +2,7 @@ import type { ResponseInputItem } from "openai/resources/responses/responses"; import { fileTypeFromBuffer } from "file-type"; import fs from "fs/promises"; +import path from "path"; export async function createInputItem( text: string, @@ -14,17 +15,24 @@ export async function createInputItem( }; for (const filePath of images) { - /* eslint-disable no-await-in-loop */ - const binary = await fs.readFile(filePath); - const kind = await fileTypeFromBuffer(binary); - /* eslint-enable no-await-in-loop */ - const encoded = binary.toString("base64"); - const mime = kind?.mime ?? "application/octet-stream"; - inputItem.content.push({ - type: "input_image", - detail: "auto", - image_url: `data:${mime};base64,${encoded}`, - }); + try { + /* eslint-disable no-await-in-loop */ + const binary = await fs.readFile(filePath); + const kind = await fileTypeFromBuffer(binary); + /* eslint-enable no-await-in-loop */ + const encoded = binary.toString("base64"); + const mime = kind?.mime ?? "application/octet-stream"; + inputItem.content.push({ + type: "input_image", + detail: "auto", + image_url: `data:${mime};base64,${encoded}`, + }); + } catch (err) { + inputItem.content.push({ + type: "input_text", + text: `[missing image: ${path.basename(filePath)}]`, + }); + } } return inputItem; diff --git a/codex-cli/tests/input-utils.test.ts b/codex-cli/tests/input-utils.test.ts new file mode 100644 index 0000000000..5290e55488 --- /dev/null +++ b/codex-cli/tests/input-utils.test.ts @@ -0,0 +1,43 @@ +import { describe, it, expect, vi } from "vitest"; +import fs from "fs/promises"; +import { createInputItem } from "../src/utils/input-utils.js"; + +describe("createInputItem", () => { + it("returns only text when no images provided", async () => { + const result = await createInputItem("hello", []); + expect(result).toEqual({ + role: "user", + type: "message", + content: [{ type: "input_text", text: "hello" }], + }); + }); + + it("includes image content for existing file", async () => { + const fakeBuffer = Buffer.from("fake image content"); + const readSpy = vi.spyOn(fs, "readFile").mockResolvedValue(fakeBuffer as any); + const result = await createInputItem("hello", ["dummy-path"]); + const expectedUrl = `data:application/octet-stream;base64,${fakeBuffer.toString("base64")}`; + expect(result.role).toBe("user"); + expect(result.type).toBe("message"); + expect(result.content.length).toBe(2); + const [textItem, imageItem] = result.content; + expect(textItem).toEqual({ type: "input_text", text: "hello" }); + expect(imageItem).toEqual({ + type: "input_image", + detail: "auto", + image_url: expectedUrl, + }); + readSpy.mockRestore(); + }); + + it("falls back to missing image text for non-existent file", async () => { + const filePath = "tests/__fixtures__/does-not-exist.png"; + const result = await createInputItem("hello", [filePath]); + expect(result.content.length).toBe(2); + const fallbackItem = result.content[1]; + expect(fallbackItem).toEqual({ + type: "input_text", + text: "[missing image: does-not-exist.png]", + }); + }); +}); \ No newline at end of file From 6c7fbc7b948438b73106e04dd80a6d27c8458d36 Mon Sep 17 00:00:00 2001 From: Alpha Diop <90140491+alphajoop@users.noreply.github.com> Date: Sat, 19 Apr 2025 14:18:36 +0000 Subject: [PATCH 19/82] fix: configure husky and lint-staged for pnpm monorepo (#384) # Improve Developer Experience with Husky and lint-staged for pnpm Monorepo ## Summary This PR enhances the developer experience by configuring Husky and lint-staged to work properly with our pnpm monorepo structure. It centralizes Git hooks at the root level and ensures consistent code quality across the project. ## Changes - Centralized Husky and lint-staged configuration at the monorepo root - Added pre-commit hook that runs lint-staged to enforce code quality - Configured lint-staged to: - Format JSON, MD, and YAML files with Prettier - Lint and typecheck TypeScript files before commits - Fixed release script in codex-cli package.json (changed "pmpm" to "npm publish") - Removed duplicate Husky and lint-staged configurations from codex-cli package.json ## Benefits - **Consistent Code Quality**: Ensures all committed code meets project standards - **Automated Formatting**: Automatically formats code during commits - **Early Error Detection**: Catches type errors and lint issues before they're committed - **Centralized Configuration**: Easier to maintain and update in one place - **Improved Collaboration**: Ensures consistent code style across the team ## Future Improvements We could further enhance this setup by **Commit Message Validation**: Add commitlint to enforce conventional commit messages --------- Co-authored-by: Thibault Sottiaux --- .husky/pre-commit | 1 + codex-cli/.husky/_/husky.sh | 32 -------------------------------- codex-cli/.husky/pre-commit | 5 ----- codex-cli/.husky/pre-push | 5 ----- codex-cli/.lintstagedrc.json | 9 --------- codex-cli/package.json | 7 ++----- lint-staged.config.mjs | 4 ---- package.json | 13 ++++++++++++- pnpm-lock.yaml | 30 +++++++++++++++--------------- 9 files changed, 30 insertions(+), 76 deletions(-) create mode 100644 .husky/pre-commit delete mode 100644 codex-cli/.husky/_/husky.sh delete mode 100644 codex-cli/.husky/pre-commit delete mode 100644 codex-cli/.husky/pre-push delete mode 100644 codex-cli/.lintstagedrc.json delete mode 100644 lint-staged.config.mjs diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000000..e02c24e2b5 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +pnpm lint-staged \ No newline at end of file diff --git a/codex-cli/.husky/_/husky.sh b/codex-cli/.husky/_/husky.sh deleted file mode 100644 index a09c6caf7e..0000000000 --- a/codex-cli/.husky/_/husky.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env sh -if [ -z "$husky_skip_init" ]; then - debug () { - if [ "$HUSKY_DEBUG" = "1" ]; then - echo "husky (debug) - $1" - fi - } - - readonly hook_name="$(basename -- "$0")" - debug "starting $hook_name..." - - if [ "$HUSKY" = "0" ]; then - debug "HUSKY env variable is set to 0, skipping hook" - exit 0 - fi - - if [ -f ~/.huskyrc ]; then - debug "sourcing ~/.huskyrc" - . ~/.huskyrc - fi - - readonly husky_skip_init=1 - export husky_skip_init - sh -e "$0" "$@" - exitCode="$?" - - if [ $exitCode != 0 ]; then - echo "husky - $hook_name hook exited with code $exitCode (error)" - fi - - exit $exitCode -fi diff --git a/codex-cli/.husky/pre-commit b/codex-cli/.husky/pre-commit deleted file mode 100644 index f052379ff9..0000000000 --- a/codex-cli/.husky/pre-commit +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - -# Run lint-staged to check files that are about to be committed -npm run pre-commit diff --git a/codex-cli/.husky/pre-push b/codex-cli/.husky/pre-push deleted file mode 100644 index 3391525415..0000000000 --- a/codex-cli/.husky/pre-push +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - -# Run tests and type checking before pushing -npm test && npm run typecheck diff --git a/codex-cli/.lintstagedrc.json b/codex-cli/.lintstagedrc.json deleted file mode 100644 index 54b8b06297..0000000000 --- a/codex-cli/.lintstagedrc.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "*.{ts,tsx}": [ - "eslint --fix", - "prettier --write" - ], - "*.{json,md,yml}": [ - "prettier --write" - ] -} diff --git a/codex-cli/package.json b/codex-cli/package.json index d5c99c4d09..b0a005d061 100644 --- a/codex-cli/package.json +++ b/codex-cli/package.json @@ -22,10 +22,8 @@ "build:dev": "NODE_ENV=development node build.mjs --dev && NODE_OPTIONS=--enable-source-maps node dist/cli-dev.js", "release:readme": "cp ../README.md ./README.md", "release:version": "TS=$(date +%y%m%d%H%M) && sed -E -i'' -e \"s/\\\"0\\.1\\.[0-9]{10}\\\"/\\\"0.1.${TS}\\\"/g\" package.json src/utils/session.ts", - "release:build-and-publish": "pmpm run build && pmpm publish", - "release": "pnpm run release:readme && pnpm run release:version && pnpm install && pnpm run release:build-and-publish", - "prepare": "husky", - "pre-commit": "lint-staged" + "release:build-and-publish": "pnpm run build && npm publish", + "release": "pnpm run release:readme && pnpm run release:version && pnpm install && pnpm run release:build-and-publish" }, "files": [ "README.md", @@ -73,7 +71,6 @@ "eslint-plugin-react-refresh": "^0.4.19", "husky": "^9.1.7", "ink-testing-library": "^3.0.0", - "lint-staged": "^15.5.1", "prettier": "^2.8.7", "punycode": "^2.3.1", "ts-node": "^10.9.1", diff --git a/lint-staged.config.mjs b/lint-staged.config.mjs deleted file mode 100644 index 85af6291e6..0000000000 --- a/lint-staged.config.mjs +++ /dev/null @@ -1,4 +0,0 @@ -export default { - "*.{js,jsx,ts,tsx}": ["pnpm prettier --write", "pnpm eslint --fix"], - "*.{json,md,yml,yaml}": ["pnpm prettier --write"], -}; diff --git a/package.json b/package.json index b6ce8ee443..a54db4ada4 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,14 @@ "test": "pnpm --filter @openai/codex run test", "lint": "pnpm --filter @openai/codex run lint", "typecheck": "pnpm --filter @openai/codex run typecheck", + "changelog": "git-cliff --config cliff.toml --output CHANGELOG.ignore.md $LAST_RELEASE_TAG..HEAD", "prepare": "husky", - "changelog": "git-cliff --config cliff.toml --output CHANGELOG.ignore.md $LAST_RELEASE_TAG..HEAD" + "husky:add": "husky add" }, "devDependencies": { "git-cliff": "^2.8.0", "husky": "^9.1.7", + "lint-staged": "^15.5.1", "prettier": "^3.5.3" }, "resolutions": { @@ -30,5 +32,14 @@ "node": ">=22", "pnpm": ">=9.0.0" }, + "lint-staged": { + "*.json": "prettier --write", + "*.md": "prettier --write", + ".github/workflows/*.yml": "prettier --write", + "**/*.{js,ts,tsx}": [ + "pnpm --filter @openai/codex run lint", + "pnpm --filter @openai/codex run typecheck" + ] + }, "packageManager": "pnpm@10.8.1" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d4bee57d57..1539652c8f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,6 +19,9 @@ importers: husky: specifier: ^9.1.7 version: 9.1.7 + lint-staged: + specifier: ^15.5.1 + version: 15.5.1 prettier: specifier: ^3.5.3 version: 3.5.3 @@ -88,7 +91,7 @@ importers: devDependencies: '@eslint/js': specifier: ^9.22.0 - version: 9.25.0 + version: 9.24.0 '@types/diff': specifier: ^7.0.2 version: 7.0.2 @@ -137,9 +140,6 @@ importers: ink-testing-library: specifier: ^3.0.0 version: 3.0.0(@types/react@18.3.20) - lint-staged: - specifier: ^15.5.1 - version: 15.5.1 prettier: specifier: ^2.8.7 version: 2.8.8 @@ -344,8 +344,8 @@ packages: resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - '@eslint/js@9.25.0': - resolution: {integrity: sha512-iWhsUS8Wgxz9AXNfvfOPFSW4VfMXdVhp1hjkZVhXCrpgh/aLcc45rX6MPu+tIVUWDw0HfNwth7O28M1xDxNf9w==} + '@eslint/js@9.24.0': + resolution: {integrity: sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@humanwhocodes/config-array@0.13.0': @@ -2304,8 +2304,8 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@6.3.2: - resolution: {integrity: sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==} + vite@6.3.1: + resolution: {integrity: sha512-kkzzkqtMESYklo96HKKPE5KKLkC1amlsqt+RjFMlX2AvbRB/0wghap19NdBxxwGZ+h/C6DLCrcEphPIItlGrRQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -2595,7 +2595,7 @@ snapshots: '@eslint/js@8.57.1': {} - '@eslint/js@9.25.0': {} + '@eslint/js@9.24.0': {} '@humanwhocodes/config-array@0.13.0': dependencies: @@ -2851,13 +2851,13 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.1.1(vite@6.3.2(@types/node@22.14.1)(yaml@2.7.1))': + '@vitest/mocker@3.1.1(vite@6.3.1(@types/node@22.14.1)(yaml@2.7.1))': dependencies: '@vitest/spy': 3.1.1 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 6.3.2(@types/node@22.14.1)(yaml@2.7.1) + vite: 6.3.1(@types/node@22.14.1)(yaml@2.7.1) '@vitest/pretty-format@3.1.1': dependencies: @@ -4753,7 +4753,7 @@ snapshots: debug: 4.4.0 es-module-lexer: 1.6.0 pathe: 2.0.3 - vite: 6.3.2(@types/node@22.14.1)(yaml@2.7.1) + vite: 6.3.1(@types/node@22.14.1)(yaml@2.7.1) transitivePeerDependencies: - '@types/node' - jiti @@ -4768,7 +4768,7 @@ snapshots: - tsx - yaml - vite@6.3.2(@types/node@22.14.1)(yaml@2.7.1): + vite@6.3.1(@types/node@22.14.1)(yaml@2.7.1): dependencies: esbuild: 0.25.2 fdir: 6.4.3(picomatch@4.0.2) @@ -4784,7 +4784,7 @@ snapshots: vitest@3.1.1(@types/node@22.14.1)(yaml@2.7.1): dependencies: '@vitest/expect': 3.1.1 - '@vitest/mocker': 3.1.1(vite@6.3.2(@types/node@22.14.1)(yaml@2.7.1)) + '@vitest/mocker': 3.1.1(vite@6.3.1(@types/node@22.14.1)(yaml@2.7.1)) '@vitest/pretty-format': 3.1.1 '@vitest/runner': 3.1.1 '@vitest/snapshot': 3.1.1 @@ -4800,7 +4800,7 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 2.0.0 - vite: 6.3.2(@types/node@22.14.1)(yaml@2.7.1) + vite: 6.3.1(@types/node@22.14.1)(yaml@2.7.1) vite-node: 3.1.1(@types/node@22.14.1)(yaml@2.7.1) why-is-node-running: 2.3.0 optionalDependencies: From 9eeb78e54f6deacbaa31ec7d45e0b69beb400462 Mon Sep 17 00:00:00 2001 From: Scott Leibrand Date: Sat, 19 Apr 2025 07:21:19 -0700 Subject: [PATCH 20/82] feat: allow switching approval modes when prompted to approve an edit/command (#400) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements https://github.com/openai/codex/issues/392 When the user is in suggest or auto-edit mode and gets an approval request, they now have an option in the `Shell Command` dialog to: `Switch approval mode (v)` That option brings up the standard `Switch approval mode` dialog, allowing the user to switch into the desired mode, then drops them back to the `Shell Command` dialog's `Allow command?` prompt, allowing them to approve the current command and let the agent continue doing the rest of what it was doing without interruption. ``` ╭──────────────────────────────────────────────────────── │Shell Command │ │$ apply_patch << 'PATCH' │*** Begin Patch │*** Update File: foo.txt │@@ -1 +1 @@ │-foo │+bar │*** End Patch │PATCH │ │ │Allow command? │ │ Yes (y) │ Explain this command (x) │ Edit or give feedback (e) │ Switch approval mode (v) │ No, and keep going (n) │ No, and stop for now (esc) ╰────────────────────────────────────────────────────────╭──────────────────────────────────────────────────────── │ Switch approval mode │ Current mode: suggest │ │ │ │ ❯ suggest │ auto-edit │ full-auto │ type to search · enter to confirm · esc to cancel ╰──────────────────────────────────────────────────────── ``` --- .../chat/terminal-chat-command-review.tsx | 28 +++++++++++++++++-- .../components/chat/terminal-chat-input.tsx | 4 +++ .../chat/terminal-chat-new-input.tsx | 4 +++ .../src/components/chat/terminal-chat.tsx | 19 +++++++++++-- 4 files changed, 49 insertions(+), 6 deletions(-) diff --git a/codex-cli/src/components/chat/terminal-chat-command-review.tsx b/codex-cli/src/components/chat/terminal-chat-command-review.tsx index 124178b0e1..eadb9071ae 100644 --- a/codex-cli/src/components/chat/terminal-chat-command-review.tsx +++ b/codex-cli/src/components/chat/terminal-chat-command-review.tsx @@ -15,11 +15,18 @@ const DEFAULT_DENY_MESSAGE = export function TerminalChatCommandReview({ confirmationPrompt, onReviewCommand, + // callback to switch approval mode overlay + onSwitchApprovalMode, explanation: propExplanation, + // whether this review Select is active (listening for keys) + isActive = true, }: { confirmationPrompt: React.ReactNode; onReviewCommand: (decision: ReviewDecision, customMessage?: string) => void; + onSwitchApprovalMode: () => void; explanation?: string; + // when false, disable the underlying Select so it won't capture input + isActive?: boolean; }): React.ReactElement { const [mode, setMode] = React.useState<"select" | "input" | "explanation">( "select", @@ -70,6 +77,7 @@ export function TerminalChatCommandReview({ const opts: Array< | { label: string; value: ReviewDecision } | { label: string; value: "edit" } + | { label: string; value: "switch" } > = [ { label: "Yes (y)", @@ -93,6 +101,11 @@ export function TerminalChatCommandReview({ label: "Edit or give feedback (e)", value: "edit", }, + // allow switching approval mode + { + label: "Switch approval mode (s)", + value: "switch", + }, { label: "No, and keep going (n)", value: ReviewDecision.NO_CONTINUE, @@ -106,7 +119,8 @@ export function TerminalChatCommandReview({ return opts; }, [showAlwaysApprove]); - useInput((input, key) => { + useInput( + (input, key) => { if (mode === "select") { if (input === "y") { onReviewCommand(ReviewDecision.YES); @@ -121,6 +135,9 @@ export function TerminalChatCommandReview({ ); } else if (input === "a" && showAlwaysApprove) { onReviewCommand(ReviewDecision.ALWAYS); + } else if (input === "s") { + // switch approval mode + onSwitchApprovalMode(); } else if (key.escape) { onReviewCommand(ReviewDecision.NO_EXIT); } @@ -143,7 +160,8 @@ export function TerminalChatCommandReview({ ); } } - }); + }, { isActive } + ); return ( @@ -191,9 +209,13 @@ export function TerminalChatCommandReview({ Allow command? ` hooks were still active and “stealing” keys. Fix - In `terminal-chat.tsx` we now only render `` (and by extension `TerminalChatCommandReview`) when `overlayMode === "none"`. That unmounts all of its key handlers whenever any overlay (history, model, approval, help, diff) is open, so no input leaks through. Files changed - **src/components/chat/terminal-chat.tsx**: Wrapped the entire `` block in `overlayMode === "none" && agent` With that in place, arrow‑key → Enter on “Switch approval mode” correctly opens the overlay, and then you can use Enter/Esc inside the overlay without getting stuck or immediately re‑opening it. --- codex-cli/src/components/chat/terminal-chat.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codex-cli/src/components/chat/terminal-chat.tsx b/codex-cli/src/components/chat/terminal-chat.tsx index 606d811681..9ebcfbc75e 100644 --- a/codex-cli/src/components/chat/terminal-chat.tsx +++ b/codex-cli/src/components/chat/terminal-chat.tsx @@ -490,7 +490,7 @@ export default function TerminalChat({ Initializing agent… )} - {agent && ( + {overlayMode === "none" && agent && ( Date: Sun, 20 Apr 2025 22:21:49 -0700 Subject: [PATCH 39/82] refactor(component): rename component to match its filename (#432) Co-authored-by: Thibault Sottiaux --- codex-cli/src/components/chat/terminal-message-history.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/codex-cli/src/components/chat/terminal-message-history.tsx b/codex-cli/src/components/chat/terminal-message-history.tsx index fe0bea5554..8171f629a8 100644 --- a/codex-cli/src/components/chat/terminal-message-history.tsx +++ b/codex-cli/src/components/chat/terminal-message-history.tsx @@ -12,7 +12,7 @@ import React, { useMemo } from "react"; // items (e.g. auto‑approved tool‑call batches) that should be rendered // together. type BatchEntry = { item?: ResponseItem; group?: GroupedResponseItem }; -type MessageHistoryProps = { +type TerminalMessageHistoryProps = { batch: Array; groupCounts: Record; items: Array; @@ -25,7 +25,7 @@ type MessageHistoryProps = { setOverlayMode: React.Dispatch>; }; -const MessageHistory: React.FC = ({ +const TerminalMessageHistory: React.FC = ({ batch, headerProps, // `loading` and `thinkingSeconds` handled by input component now. @@ -78,4 +78,4 @@ const MessageHistory: React.FC = ({ ); }; -export default React.memo(MessageHistory); +export default React.memo(TerminalMessageHistory); From f6b12aa9941a326ce6e31a137ad8458779d516ed Mon Sep 17 00:00:00 2001 From: Jordan Docherty <20336279+jdocherty@users.noreply.github.com> Date: Sun, 20 Apr 2025 22:27:06 -0700 Subject: [PATCH 40/82] refactor(history-overlay): split into modular functions & add tests (fixes #402) (#403) ## What This PR targets #402 and refactors the `history-overlay.tsx`component to reduce cognitive complexity by splitting the `buildLists` function into smaller, focused helper functions. It also adds comprehensive test coverage to ensure the functionality remains intact. ## Why The original `buildLists` function had high cognitive complexity due to multiple nested conditionals, complex string manipulation, and mixed responsibilities. This refactor makes the code more maintainable and easier to understand while preserving all existing functionality. ## How - Split `buildLists` into focused helper functions - Added comprehensive test coverage for all functionality - Maintained existing behavior and keyboard interactions - Improved code organization and readability ## Testing All tests pass, including: - Command mode functionality - File mode functionality - Keyboard interactions - Error handling --- codex-cli/src/components/history-overlay.tsx | 176 +++++----- codex-cli/tests/history-overlay.test.tsx | 350 +++++++++++++++++++ 2 files changed, 447 insertions(+), 79 deletions(-) create mode 100644 codex-cli/tests/history-overlay.test.tsx diff --git a/codex-cli/src/components/history-overlay.tsx b/codex-cli/src/components/history-overlay.tsx index c22e22d87d..9cd2d6d058 100644 --- a/codex-cli/src/components/history-overlay.tsx +++ b/codex-cli/src/components/history-overlay.tsx @@ -14,7 +14,10 @@ export default function HistoryOverlay({ items, onExit }: Props): JSX.Element { const [mode, setMode] = useState("commands"); const [cursor, setCursor] = useState(0); - const { commands, files } = useMemo(() => buildLists(items), [items]); + const { commands, files } = useMemo( + () => formatHistoryForDisplay(items), + [items], + ); const list = mode === "commands" ? commands : files; @@ -95,7 +98,7 @@ export default function HistoryOverlay({ items, onExit }: Props): JSX.Element { ); } -function buildLists(items: Array): { +function formatHistoryForDisplay(items: Array): { commands: Array; files: Array; } { @@ -103,33 +106,9 @@ function buildLists(items: Array): { const filesSet = new Set(); for (const item of items) { - if ( - item.type === "message" && - (item as unknown as { role?: string }).role === "user" - ) { - // TODO: We're ignoring images/files here. - const parts = - (item as unknown as { content?: Array }).content ?? []; - const texts: Array = []; - if (Array.isArray(parts)) { - for (const part of parts) { - if (part && typeof part === "object" && "text" in part) { - const t = (part as unknown as { text?: string }).text; - if (typeof t === "string" && t.length > 0) { - texts.push(t); - } - } - } - } - - if (texts.length > 0) { - const fullPrompt = texts.join(" "); - // Truncate very long prompts so the history view stays legible. - const truncated = - fullPrompt.length > 120 ? `${fullPrompt.slice(0, 117)}…` : fullPrompt; - commands.push(`> ${truncated}`); - } - + const userPrompt = processUserMessage(item); + if (userPrompt) { + commands.push(userPrompt); continue; } @@ -173,31 +152,7 @@ function buildLists(items: Array): { : undefined; if (cmdArray && cmdArray.length > 0) { - commands.push(cmdArray.join(" ")); - - // Heuristic for file paths in command args - for (const part of cmdArray) { - if (!part.startsWith("-") && part.includes("/")) { - filesSet.add(part); - } - } - - // Special‑case apply_patch so we can extract the list of modified files - if (cmdArray[0] === "apply_patch" || cmdArray.includes("apply_patch")) { - const patchTextMaybe = cmdArray.find((s) => - s.includes("*** Begin Patch"), - ); - if (typeof patchTextMaybe === "string") { - const lines = patchTextMaybe.split("\n"); - for (const line of lines) { - const m = line.match(/^[-+]{3} [ab]\/(.+)$/); - if (m && m[1]) { - filesSet.add(m[1]); - } - } - } - } - + commands.push(processCommandArray(cmdArray, filesSet)); continue; // We processed this as a command; no need to treat as generic tool call. } @@ -205,33 +160,96 @@ function buildLists(items: Array): { // short argument representation to give users an idea of what // happened. if (typeof toolName === "string" && toolName.length > 0) { - let summary = toolName; - - if (argsJson && typeof argsJson === "object") { - // Extract a few common argument keys to make the summary more useful - // without being overly verbose. - const interestingKeys = [ - "path", - "file", - "filepath", - "filename", - "pattern", - ]; - for (const key of interestingKeys) { - const val = (argsJson as Record)[key]; - if (typeof val === "string") { - summary += ` ${val}`; - if (val.includes("/")) { - filesSet.add(val); - } - break; - } - } - } - - commands.push(summary); + commands.push(processNonExecTool(toolName, argsJson, filesSet)); } } return { commands, files: Array.from(filesSet) }; } + +function processUserMessage(item: ResponseItem): string | null { + if ( + item.type === "message" && + (item as unknown as { role?: string }).role === "user" + ) { + // TODO: We're ignoring images/files here. + const parts = + (item as unknown as { content?: Array }).content ?? []; + const texts: Array = []; + if (Array.isArray(parts)) { + for (const part of parts) { + if (part && typeof part === "object" && "text" in part) { + const t = (part as unknown as { text?: string }).text; + if (typeof t === "string" && t.length > 0) { + texts.push(t); + } + } + } + } + + if (texts.length > 0) { + const fullPrompt = texts.join(" "); + // Truncate very long prompts so the history view stays legible. + return fullPrompt.length > 120 + ? `> ${fullPrompt.slice(0, 117)}…` + : `> ${fullPrompt}`; + } + } + return null; +} + +function processCommandArray( + cmdArray: Array, + filesSet: Set, +): string { + const cmd = cmdArray.join(" "); + + // Heuristic for file paths in command args + for (const part of cmdArray) { + if (!part.startsWith("-") && part.includes("/")) { + filesSet.add(part); + } + } + + // Special‑case apply_patch so we can extract the list of modified files + if (cmdArray[0] === "apply_patch" || cmdArray.includes("apply_patch")) { + const patchTextMaybe = cmdArray.find((s) => s.includes("*** Begin Patch")); + if (typeof patchTextMaybe === "string") { + const lines = patchTextMaybe.split("\n"); + for (const line of lines) { + const m = line.match(/^[-+]{3} [ab]\/(.+)$/); + if (m && m[1]) { + filesSet.add(m[1]); + } + } + } + } + + return cmd; +} + +function processNonExecTool( + toolName: string, + argsJson: unknown, + filesSet: Set, +): string { + let summary = toolName; + + if (argsJson && typeof argsJson === "object") { + // Extract a few common argument keys to make the summary more useful + // without being overly verbose. + const interestingKeys = ["path", "file", "filepath", "filename", "pattern"]; + for (const key of interestingKeys) { + const val = (argsJson as Record)[key]; + if (typeof val === "string") { + summary += ` ${val}`; + if (val.includes("/")) { + filesSet.add(val); + } + break; + } + } + } + + return summary; +} diff --git a/codex-cli/tests/history-overlay.test.tsx b/codex-cli/tests/history-overlay.test.tsx new file mode 100644 index 0000000000..1ae2a2d2dd --- /dev/null +++ b/codex-cli/tests/history-overlay.test.tsx @@ -0,0 +1,350 @@ +/* -------------------------------------------------------------------------- * + * Tests for the HistoryOverlay component and its formatHistoryForDisplay utility function + * + * The component displays a list of commands and files from the chat history. + * It supports two modes: + * - Command mode: shows all commands and user messages + * - File mode: shows all files that were touched + * + * The formatHistoryForDisplay function processes ResponseItems to extract: + * - Commands: User messages and function calls + * - Files: Paths referenced in commands or function calls + * -------------------------------------------------------------------------- */ + +import { describe, it, expect, vi } from "vitest"; +import { render } from "ink-testing-library"; +import React from "react"; +import type { + ResponseInputMessageItem, + ResponseFunctionToolCallItem, +} from "openai/resources/responses/responses.mjs"; +import HistoryOverlay from "../src/components/history-overlay"; + +// --------------------------------------------------------------------------- +// Module mocks *must* be registered *before* the module under test is imported +// so that Vitest can replace the dependency during evaluation. +// --------------------------------------------------------------------------- + +// Mock ink's useInput to capture keyboard handlers +let keyboardHandler: ((input: string, key: any) => void) | undefined; +vi.mock("ink", async () => { + const actual = await vi.importActual("ink"); + return { + ...actual, + useInput: (handler: (input: string, key: any) => void) => { + keyboardHandler = handler; + }, + }; +}); + +// --------------------------------------------------------------------------- +// Test Helpers +// --------------------------------------------------------------------------- + +function createUserMessage(content: string): ResponseInputMessageItem { + return { + type: "message", + role: "user", + id: `msg_${Math.random().toString(36).slice(2)}`, + content: [{ type: "input_text", text: content }], + }; +} + +function createFunctionCall( + name: string, + args: unknown, +): ResponseFunctionToolCallItem { + return { + type: "function_call", + name, + id: `fn_${Math.random().toString(36).slice(2)}`, + call_id: `call_${Math.random().toString(36).slice(2)}`, + arguments: JSON.stringify(args), + }; +} + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +describe("HistoryOverlay", () => { + describe("command mode", () => { + it("displays user messages", () => { + const items = [createUserMessage("hello"), createUserMessage("world")]; + const { lastFrame } = render( + , + ); + const frame = lastFrame(); + expect(frame).toContain("hello"); + expect(frame).toContain("world"); + }); + + it("displays shell commands", () => { + const items = [ + createFunctionCall("shell", { cmd: ["ls", "-la"] }), + createFunctionCall("shell", { cmd: ["pwd"] }), + ]; + const { lastFrame } = render( + , + ); + const frame = lastFrame(); + expect(frame).toContain("ls -la"); + expect(frame).toContain("pwd"); + }); + + it("displays file operations", () => { + const items = [createFunctionCall("read_file", { path: "test.txt" })]; + const { lastFrame } = render( + , + ); + const frame = lastFrame(); + expect(frame).toContain("read_file test.txt"); + }); + + it("displays patch operations", () => { + const items = [ + createFunctionCall("shell", { + cmd: [ + "apply_patch", + "*** Begin Patch\n--- a/src/file1.txt\n+++ b/src/file1.txt\n@@ -1,5 +1,5 @@\n-const x = 1;\n+const x = 2;\n", + ], + }), + ]; + const { lastFrame } = render( + , + ); + + // Verify patch is displayed in command mode + let frame = lastFrame(); + expect(frame).toContain("apply_patch"); + expect(frame).toContain("src/file1.txt"); + + // Verify file is extracted in file mode + keyboardHandler?.("f", {}); + frame = lastFrame(); + expect(frame).toContain("src/file1.txt"); + }); + + it("displays mixed content in chronological order", () => { + const items = [ + createUserMessage("first message"), + createFunctionCall("shell", { cmd: ["echo", "hello"] }), + createUserMessage("second message"), + ]; + const { lastFrame } = render( + , + ); + const frame = lastFrame(); + expect(frame).toContain("first message"); + expect(frame).toContain("echo hello"); + expect(frame).toContain("second message"); + }); + + it("truncates long user messages", () => { + const shortMessage = "Hello"; + const longMessage = + "This is a very long message that should be truncated because it exceeds the maximum length of 120 characters. We need to make sure it gets properly truncated with the right prefix and ellipsis."; + const items = [ + createUserMessage(shortMessage), + createUserMessage(longMessage), + ]; + + const { lastFrame } = render( + , + ); + const frame = lastFrame()!; + + // Short message should have the > prefix + expect(frame).toContain(`> ${shortMessage}`); + + // Long message should be truncated and contain: + // 1. The > prefix + expect(frame).toContain("> This is a very long message"); + // 2. An ellipsis indicating truncation + expect(frame).toContain("…"); + // 3. Not contain the full message + expect(frame).not.toContain(longMessage); + + // Find the truncated message line + const lines = frame.split("\n"); + const truncatedLine = lines.find((line) => + line.includes("This is a very long message"), + )!; + // Verify it's not too long (allowing for some UI elements) + expect(truncatedLine.trim().length).toBeLessThan(150); + }); + }); + + describe("file mode", () => { + it("displays files from shell commands", () => { + const items = [ + createFunctionCall("shell", { cmd: ["cat", "/path/to/file"] }), + ]; + const { lastFrame } = render( + , + ); + + // Switch to file mode + keyboardHandler?.("f", {}); + const frame = lastFrame(); + expect(frame).toContain("Files touched"); + expect(frame).toContain("/path/to/file"); + }); + + it("displays files from read operations", () => { + const items = [ + createFunctionCall("read_file", { path: "/path/to/file" }), + ]; + const { lastFrame } = render( + , + ); + + // Switch to file mode + keyboardHandler?.("f", {}); + const frame = lastFrame(); + expect(frame).toContain("Files touched"); + expect(frame).toContain("/path/to/file"); + }); + + it("displays files from patches", () => { + const items = [ + createFunctionCall("shell", { + cmd: [ + "apply_patch", + "*** Begin Patch\n--- a/src/file1.txt\n+++ b/src/file1.txt\n@@ -1,5 +1,5 @@\n-const x = 1;\n+const x = 2;\n", + ], + }), + ]; + const { lastFrame } = render( + , + ); + + // Switch to file mode + keyboardHandler?.("f", {}); + const frame = lastFrame(); + expect(frame).toContain("Files touched"); + expect(frame).toContain("src/file1.txt"); + }); + }); + + describe("keyboard interaction", () => { + it("handles mode switching with 'c' and 'f' keys", () => { + const items = [ + createUserMessage("hello"), + createFunctionCall("shell", { cmd: ["cat", "src/test.txt"] }), + ]; + const { lastFrame } = render( + , + ); + + // Initial state (command mode) + let frame = lastFrame(); + expect(frame).toContain("Commands run"); + expect(frame).toContain("hello"); + expect(frame).toContain("cat src/test.txt"); + + // Switch to files mode + keyboardHandler?.("f", {}); + frame = lastFrame(); + expect(frame).toContain("Files touched"); + expect(frame).toContain("src/test.txt"); + + // Switch back to commands mode + keyboardHandler?.("c", {}); + frame = lastFrame(); + expect(frame).toContain("Commands run"); + expect(frame).toContain("hello"); + expect(frame).toContain("cat src/test.txt"); + }); + + it("handles escape key", () => { + const onExit = vi.fn(); + render(); + + keyboardHandler?.("", { escape: true }); + expect(onExit).toHaveBeenCalled(); + }); + + it("handles arrow keys for navigation", () => { + const items = [createUserMessage("first"), createUserMessage("second")]; + const { lastFrame } = render( + , + ); + + // Initial state shows first item selected + let frame = lastFrame(); + expect(frame).toContain("› > first"); + expect(frame).not.toContain("› > second"); + + // Move down - second item should be selected + keyboardHandler?.("", { downArrow: true }); + frame = lastFrame(); + expect(frame).toContain("› > second"); + expect(frame).not.toContain("› > first"); + + // Move up - first item should be selected again + keyboardHandler?.("", { upArrow: true }); + frame = lastFrame(); + expect(frame).toContain("› > first"); + expect(frame).not.toContain("› > second"); + }); + + it("handles page up/down navigation", () => { + const items = Array.from({ length: 12 }, (_, i) => + createUserMessage(`message ${i + 1}`), + ); + + const { lastFrame } = render( + , + ); + + // Initial position - first message selected + let frame = lastFrame(); + expect(frame).toMatch(/│ › > message 1\s+│/); // message 1 should be selected + expect(frame).toMatch(/│ {3}> message 11\s+│/); // message 11 should be visible but not selected + + // Page down moves by 10 - message 11 should be selected + keyboardHandler?.("", { pageDown: true }); + frame = lastFrame(); + expect(frame).toMatch(/│ {3}> message 1\s+│/); // message 1 should be visible but not selected + expect(frame).toMatch(/│ › > message 11\s+│/); // message 11 should be selected + }); + + it("handles vim-style navigation", () => { + const items = [ + createUserMessage("first"), + createUserMessage("second"), + createUserMessage("third"), + ]; + const { lastFrame } = render( + , + ); + + // Initial state should show first item selected + let frame = lastFrame(); + expect(frame).toContain("› > first"); + expect(frame).not.toContain("› > third"); // Make sure third is not selected initially + + // Test G to jump to end - third should be selected + keyboardHandler?.("G", {}); + frame = lastFrame(); + expect(frame).toContain("› > third"); + + // Test g to jump to beginning - first should be selected again + keyboardHandler?.("g", {}); + frame = lastFrame(); + expect(frame).toContain("› > first"); + }); + }); + + describe("error handling", () => { + it("handles empty or invalid items", () => { + const items = [{ type: "invalid" } as any, null as any, undefined as any]; + const { lastFrame } = render( + , + ); + // Should render without errors + expect(lastFrame()).toBeTruthy(); + }); + }); +}); From ee7ce5b60190fb56bfdc9cd7d61e4ee36de89807 Mon Sep 17 00:00:00 2001 From: Aiden Cline <63023139+rekram1-node@users.noreply.github.com> Date: Mon, 21 Apr 2025 00:34:27 -0500 Subject: [PATCH 41/82] feat: tab completions for file paths (#279) Made a PR as was requested in the #113 --- .gitignore | 1 + .../src/components/chat/multiline-editor.tsx | 12 ++ .../chat/terminal-chat-completions.tsx | 64 +++++++ .../components/chat/terminal-chat-input.tsx | 159 +++++++++++++----- .../src/components/vendor/ink-text-input.tsx | 15 ++ .../src/utils/file-system-suggestions.ts | 42 +++++ .../tests/file-system-suggestions.test.ts | 73 ++++++++ .../tests/terminal-chat-completions.test.tsx | 46 +++++ 8 files changed, 370 insertions(+), 42 deletions(-) create mode 100644 codex-cli/src/components/chat/terminal-chat-completions.tsx create mode 100644 codex-cli/src/utils/file-system-suggestions.ts create mode 100644 codex-cli/tests/file-system-suggestions.test.ts create mode 100644 codex-cli/tests/terminal-chat-completions.test.tsx diff --git a/.gitignore b/.gitignore index d5f0dcebbd..72326607ad 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ result .vscode/ .idea/ .history/ +.zed/ *.swp *~ diff --git a/codex-cli/src/components/chat/multiline-editor.tsx b/codex-cli/src/components/chat/multiline-editor.tsx index bb4878c631..a91eceea34 100644 --- a/codex-cli/src/components/chat/multiline-editor.tsx +++ b/codex-cli/src/components/chat/multiline-editor.tsx @@ -155,6 +155,8 @@ export interface MultilineTextEditorHandle { isCursorAtLastRow(): boolean; /** Full text contents */ getText(): string; + /** Move the cursor to the end of the text */ + moveCursorToEnd(): void; } const MultilineTextEditorInner = ( @@ -372,6 +374,16 @@ const MultilineTextEditorInner = ( return row === lineCount - 1; }, getText: () => buffer.current.getText(), + moveCursorToEnd: () => { + buffer.current.move("home"); + const lines = buffer.current.getText().split("\n"); + for (let i = 0; i < lines.length - 1; i++) { + buffer.current.move("down"); + } + buffer.current.move("end"); + // Force a re-render + setVersion((v) => v + 1); + }, }), [], ); diff --git a/codex-cli/src/components/chat/terminal-chat-completions.tsx b/codex-cli/src/components/chat/terminal-chat-completions.tsx new file mode 100644 index 0000000000..eb7e47f85f --- /dev/null +++ b/codex-cli/src/components/chat/terminal-chat-completions.tsx @@ -0,0 +1,64 @@ +import { Box, Text } from "ink"; +import React, { useMemo } from "react"; + +type TextCompletionProps = { + /** + * Array of text completion options to display in the list + */ + completions: Array; + + /** + * Maximum number of completion items to show at once in the view + */ + displayLimit: number; + + /** + * Index of the currently selected completion in the completions array + */ + selectedCompletion: number; +}; + +function TerminalChatCompletions({ + completions, + selectedCompletion, + displayLimit, +}: TextCompletionProps): JSX.Element { + const visibleItems = useMemo(() => { + // Try to keep selection centered in view + let startIndex = Math.max( + 0, + selectedCompletion - Math.floor(displayLimit / 2), + ); + + // Fix window position when at the end of the list + if (completions.length - startIndex < displayLimit) { + startIndex = Math.max(0, completions.length - displayLimit); + } + + const endIndex = Math.min(completions.length, startIndex + displayLimit); + + return completions.slice(startIndex, endIndex).map((completion, index) => ({ + completion, + originalIndex: index + startIndex, + })); + }, [completions, selectedCompletion, displayLimit]); + + return ( + + {visibleItems.map(({ completion, originalIndex }) => ( + + {completion} + + ))} + + ); +} + +export default TerminalChatCompletions; diff --git a/codex-cli/src/components/chat/terminal-chat-input.tsx b/codex-cli/src/components/chat/terminal-chat-input.tsx index 5f77f90409..ddb9c7a1c9 100644 --- a/codex-cli/src/components/chat/terminal-chat-input.tsx +++ b/codex-cli/src/components/chat/terminal-chat-input.tsx @@ -8,8 +8,10 @@ import type { import MultilineTextEditor from "./multiline-editor"; import { TerminalChatCommandReview } from "./terminal-chat-command-review.js"; +import TextCompletions from "./terminal-chat-completions.js"; import { log } from "../../utils/agent/log.js"; import { loadConfig } from "../../utils/config.js"; +import { getFileSystemSuggestions } from "../../utils/file-system-suggestions.js"; import { createInputItem } from "../../utils/input-utils.js"; import { setSessionId } from "../../utils/session.js"; import { SLASH_COMMANDS, type SlashCommand } from "../../utils/slash-commands"; @@ -90,6 +92,8 @@ export default function TerminalChatInput({ const [historyIndex, setHistoryIndex] = useState(null); const [draftInput, setDraftInput] = useState(""); const [skipNextSubmit, setSkipNextSubmit] = useState(false); + const [fsSuggestions, setFsSuggestions] = useState>([]); + const [selectedCompletion, setSelectedCompletion] = useState(-1); // Multiline text editor key to force remount after submission const [editorKey, setEditorKey] = useState(0); // Imperative handle from the multiline editor so we can query caret position @@ -196,6 +200,44 @@ export default function TerminalChatInput({ } } if (!confirmationPrompt && !loading) { + if (fsSuggestions.length > 0) { + if (_key.upArrow) { + setSelectedCompletion((prev) => + prev <= 0 ? fsSuggestions.length - 1 : prev - 1, + ); + return; + } + + if (_key.downArrow) { + setSelectedCompletion((prev) => + prev >= fsSuggestions.length - 1 ? 0 : prev + 1, + ); + return; + } + + if (_key.tab && selectedCompletion >= 0) { + const words = input.trim().split(/\s+/); + const selected = fsSuggestions[selectedCompletion]; + + if (words.length > 0 && selected) { + words[words.length - 1] = selected; + const newText = words.join(" "); + setInput(newText); + // Force remount of the editor with the new text + setEditorKey((k) => k + 1); + + // We need to move the cursor to the end after editor remounts + setTimeout(() => { + editorRef.current?.moveCursorToEnd?.(); + }, 0); + + setFsSuggestions([]); + setSelectedCompletion(-1); + } + return; + } + } + if (_key.upArrow) { // Only recall history when the caret was *already* on the very first // row *before* this key-press. @@ -241,6 +283,19 @@ export default function TerminalChatInput({ } // Otherwise let it propagate } + + if (_key.tab) { + const words = input.split(/\s+/); + const mostRecentWord = words[words.length - 1]; + if (mostRecentWord === undefined || mostRecentWord === "") { + return; + } + const completions = getFileSystemSuggestions(mostRecentWord); + setFsSuggestions(completions); + if (completions.length > 0) { + setSelectedCompletion(0); + } + } } // Update the cached cursor position *after* we've potentially handled @@ -533,6 +588,8 @@ export default function TerminalChatInput({ setDraftInput(""); setSelectedSuggestion(0); setInput(""); + setFsSuggestions([]); + setSelectedCompletion(-1); }, [ setInput, @@ -578,7 +635,7 @@ export default function TerminalChatInput({ thinkingSeconds={thinkingSeconds} /> ) : ( - + { @@ -587,6 +644,20 @@ export default function TerminalChatInput({ setHistoryIndex(null); } setInput(txt); + + // Clear tab completions if a space is typed + if (txt.endsWith(" ")) { + setFsSuggestions([]); + setSelectedCompletion(-1); + } else if (fsSuggestions.length > 0) { + // Update file suggestions as user types + const words = txt.trim().split(/\s+/); + const mostRecentWord = + words.length > 0 ? words[words.length - 1] : ""; + if (mostRecentWord !== undefined) { + setFsSuggestions(getFileSystemSuggestions(mostRecentWord)); + } + } }} key={editorKey} initialText={input} @@ -623,47 +694,51 @@ export default function TerminalChatInput({ )} - - {isNew && !input ? ( - <> - try:{" "} - {suggestions.map((m, key) => ( - - {key !== 0 ? " | " : ""} - - {m} - - - ))} - - ) : ( - <> - send q or ctrl+c to exit | send "/clear" to reset | send "/help" - for commands | press enter to send | shift+enter for new line - {contextLeftPercent > 25 && ( - <> - {" — "} - 40 ? "green" : "yellow"}> - {Math.round(contextLeftPercent)}% context left - - - )} - {contextLeftPercent <= 25 && ( - <> - {" — "} - - {Math.round(contextLeftPercent)}% context left — send - "/compact" to condense context - - - )} - - )} - + {isNew && !input ? ( + + try:{" "} + {suggestions.map((m, key) => ( + + {key !== 0 ? " | " : ""} + + {m} + + + ))} + + ) : fsSuggestions.length > 0 ? ( + + ) : ( + + send q or ctrl+c to exit | send "/clear" to reset | send "/help" for + commands | press enter to send | shift+enter for new line + {contextLeftPercent > 25 && ( + <> + {" — "} + 40 ? "green" : "yellow"}> + {Math.round(contextLeftPercent)}% context left + + + )} + {contextLeftPercent <= 25 && ( + <> + {" — "} + + {Math.round(contextLeftPercent)}% context left — send + "/compact" to condense context + + + )} + + )} ); diff --git a/codex-cli/src/components/vendor/ink-text-input.tsx b/codex-cli/src/components/vendor/ink-text-input.tsx index 40b0a1d49a..9c015be953 100644 --- a/codex-cli/src/components/vendor/ink-text-input.tsx +++ b/codex-cli/src/components/vendor/ink-text-input.tsx @@ -44,6 +44,11 @@ export type TextInputProps = { * Function to call when `Enter` is pressed, where first argument is a value of the input. */ readonly onSubmit?: (value: string) => void; + + /** + * Explicitly set the cursor position to the end of the text + */ + readonly cursorToEnd?: boolean; }; function findPrevWordJump(prompt: string, cursorOffset: number) { @@ -90,12 +95,22 @@ function TextInput({ showCursor = true, onChange, onSubmit, + cursorToEnd = false, }: TextInputProps) { const [state, setState] = useState({ cursorOffset: (originalValue || "").length, cursorWidth: 0, }); + useEffect(() => { + if (cursorToEnd) { + setState((prev) => ({ + ...prev, + cursorOffset: (originalValue || "").length, + })); + } + }, [cursorToEnd, originalValue, focus]); + const { cursorOffset, cursorWidth } = state; useEffect(() => { diff --git a/codex-cli/src/utils/file-system-suggestions.ts b/codex-cli/src/utils/file-system-suggestions.ts new file mode 100644 index 0000000000..13350c9a76 --- /dev/null +++ b/codex-cli/src/utils/file-system-suggestions.ts @@ -0,0 +1,42 @@ +import fs from "fs"; +import os from "os"; +import path from "path"; + +export function getFileSystemSuggestions(pathPrefix: string): Array { + if (!pathPrefix) { + return []; + } + + try { + const sep = path.sep; + const hasTilde = pathPrefix === "~" || pathPrefix.startsWith("~" + sep); + const expanded = hasTilde + ? path.join(os.homedir(), pathPrefix.slice(1)) + : pathPrefix; + + const normalized = path.normalize(expanded); + const isDir = pathPrefix.endsWith(path.sep); + const base = path.basename(normalized); + + const dir = + normalized === "." && !pathPrefix.startsWith("." + sep) && !hasTilde + ? process.cwd() + : path.dirname(normalized); + + const readDir = isDir ? path.join(dir, base) : dir; + + return fs + .readdirSync(readDir) + .filter((item) => isDir || item.startsWith(base)) + .map((item) => { + const fullPath = path.join(readDir, item); + const isDirectory = fs.statSync(fullPath).isDirectory(); + if (isDirectory) { + return path.join(fullPath, sep); + } + return fullPath; + }); + } catch { + return []; + } +} diff --git a/codex-cli/tests/file-system-suggestions.test.ts b/codex-cli/tests/file-system-suggestions.test.ts new file mode 100644 index 0000000000..2477306c0d --- /dev/null +++ b/codex-cli/tests/file-system-suggestions.test.ts @@ -0,0 +1,73 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import fs from "fs"; +import os from "os"; +import path from "path"; +import { getFileSystemSuggestions } from "../src/utils/file-system-suggestions"; + +vi.mock("fs"); +vi.mock("os"); + +describe("getFileSystemSuggestions", () => { + const mockFs = fs as unknown as { + readdirSync: ReturnType; + statSync: ReturnType; + }; + + const mockOs = os as unknown as { + homedir: ReturnType; + }; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("returns empty array for empty prefix", () => { + expect(getFileSystemSuggestions("")).toEqual([]); + }); + + it("expands ~ to home directory", () => { + mockOs.homedir = vi.fn(() => "/home/testuser"); + mockFs.readdirSync = vi.fn(() => ["file1.txt", "docs"]); + mockFs.statSync = vi.fn((p) => ({ + isDirectory: () => path.basename(p) === "docs", + })); + + const result = getFileSystemSuggestions("~/"); + + expect(mockFs.readdirSync).toHaveBeenCalledWith("/home/testuser"); + expect(result).toEqual([ + path.join("/home/testuser", "file1.txt"), + path.join("/home/testuser", "docs" + path.sep), + ]); + }); + + it("filters by prefix if not a directory", () => { + mockFs.readdirSync = vi.fn(() => ["abc.txt", "abd.txt", "xyz.txt"]); + mockFs.statSync = vi.fn((p) => ({ + isDirectory: () => p.includes("abd"), + })); + + const result = getFileSystemSuggestions("a"); + expect(result).toEqual(["abc.txt", "abd.txt/"]); + }); + + it("handles errors gracefully", () => { + mockFs.readdirSync = vi.fn(() => { + throw new Error("failed"); + }); + + const result = getFileSystemSuggestions("some/path"); + expect(result).toEqual([]); + }); + + it("normalizes relative path", () => { + mockFs.readdirSync = vi.fn(() => ["foo", "bar"]); + mockFs.statSync = vi.fn((_p) => ({ + isDirectory: () => true, + })); + + const result = getFileSystemSuggestions("./"); + expect(result).toContain("foo/"); + expect(result).toContain("bar/"); + }); +}); diff --git a/codex-cli/tests/terminal-chat-completions.test.tsx b/codex-cli/tests/terminal-chat-completions.test.tsx new file mode 100644 index 0000000000..b3b0b14520 --- /dev/null +++ b/codex-cli/tests/terminal-chat-completions.test.tsx @@ -0,0 +1,46 @@ +import React from "react"; +import { describe, it, expect } from "vitest"; +import type { ComponentProps } from "react"; +import { renderTui } from "./ui-test-helpers.js"; +import TerminalChatCompletions from "../src/components/chat/terminal-chat-completions.js"; + +describe("TerminalChatCompletions", () => { + const baseProps: ComponentProps = { + completions: ["Option 1", "Option 2", "Option 3", "Option 4", "Option 5"], + displayLimit: 3, + selectedCompletion: 0, + }; + + it("renders visible completions within displayLimit", async () => { + const { lastFrameStripped } = renderTui( + , + ); + const frame = lastFrameStripped(); + expect(frame).toContain("Option 1"); + expect(frame).toContain("Option 2"); + expect(frame).toContain("Option 3"); + expect(frame).not.toContain("Option 4"); + }); + + it("centers the selected completion in the visible list", async () => { + const { lastFrameStripped } = renderTui( + , + ); + const frame = lastFrameStripped(); + expect(frame).toContain("Option 2"); + expect(frame).toContain("Option 3"); + expect(frame).toContain("Option 4"); + expect(frame).not.toContain("Option 1"); + }); + + it("adjusts when selectedCompletion is near the end", async () => { + const { lastFrameStripped } = renderTui( + , + ); + const frame = lastFrameStripped(); + expect(frame).toContain("Option 3"); + expect(frame).toContain("Option 4"); + expect(frame).toContain("Option 5"); + expect(frame).not.toContain("Option 2"); + }); +}); From ee3a9bc14b5149e9265c2afbbfd4296f2feb7576 Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Sun, 20 Apr 2025 23:54:27 -0700 Subject: [PATCH 42/82] Remove `README.md` and `bin` from `package.json#files` field (#461) This PR removes always included files and folders from the [`package.json#files` field](https://docs.npmjs.com/cli/v11/configuring-npm/package-json#files): > Certain files are always included, regardless of settings: > - package.json > - README > - LICENSE / LICENCE > - The file in the "main" field > - The file(s) in the "bin" field Validated by running `pnpm i && cd codex-cli && pnpm build && pnpm release:readme && pnpm pack` and confirming both the `README.md` file and `bin` directory are still included in the tarball: image --- codex-cli/package.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/codex-cli/package.json b/codex-cli/package.json index b0a005d061..6afabe5d02 100644 --- a/codex-cli/package.json +++ b/codex-cli/package.json @@ -26,8 +26,6 @@ "release": "pnpm run release:readme && pnpm run release:version && pnpm install && pnpm run release:build-and-publish" }, "files": [ - "README.md", - "bin", "dist", "src" ], From 655564f25d0dd14044f25cb1d20100ebf6ff8164 Mon Sep 17 00:00:00 2001 From: Abdelrhman Kamal Mahmoud Ali Slim Date: Sun, 20 Apr 2025 23:56:20 -0700 Subject: [PATCH 43/82] fix: auto-open model-selector when model is not found (#448) Change the check from checking if the model has been deprecated to check if the model_not_found https://github.com/user-attachments/assets/ad0f7daf-5eb4-4e4b-89e5-04240044c64f --- codex-cli/src/components/chat/terminal-chat-response-item.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codex-cli/src/components/chat/terminal-chat-response-item.tsx b/codex-cli/src/components/chat/terminal-chat-response-item.tsx index dc4b16dd59..b1cc6edcb0 100644 --- a/codex-cli/src/components/chat/terminal-chat-response-item.tsx +++ b/codex-cli/src/components/chat/terminal-chat-response-item.tsx @@ -117,7 +117,7 @@ function TerminalChatResponseMessage({ const systemMessage = message.content.find( (c) => c.type === "input_text", )?.text; - if (systemMessage?.includes("has been deprecated")) { + if (systemMessage?.includes("model_not_found")) { setOverlayMode?.("model"); } } From 3e71c87708af8a4bc3d8e7b2a95fc2cf1862c009 Mon Sep 17 00:00:00 2001 From: Benny Yen Date: Mon, 21 Apr 2025 15:00:20 +0800 Subject: [PATCH 44/82] refactor(updates): fetch version from registry instead of npm CLI to support multiple managers (#446) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Background Addressing feedback from https://github.com/openai/codex/pull/333#discussion_r2050893224, this PR adds support for Bun alongside npm, pnpm while keeping the code simple. ## Summary The update‑check flow is refactored to use a direct registry lookup (`fast-npm-meta` + `semver`) instead of shelling out to `npm outdated`, and adds a lightweight installer‑detection mechanism that: 1. Checks if the invoked script lives under a known global‑bin directory (npm, pnpm, or bun) 2. If not, falls back to local detection via `getUserAgent()` (the `package‑manager‑detector` library) ## What’s Changed - **Registry‑based version check** - Replace `execFile("npm", ["outdated"])` with `getLatestVersion()` and `semver.gt()` - **Multi‑manager support** - New `renderUpdateCommand` handles update commands for `npm`, `pnpm`, and `bun`. - Detect global installer first via `detectInstallerByPath()` - Fallback to local detection via `getUserAgent()` - **Module cleanup** - Extract `detectInstallerByPath` into `utils/package-manager-detector.ts` - Remove legacy `checkOutdated`, `getNPMCommandPath`, and child‑process JSON parsing - **Flow improvements in `checkForUpdates`** 1. Short‑circuit by `UPDATE_CHECK_FREQUENCY` 3. Fetch & compare versions 4. Persist new timestamp immediately 5. Render & display styled box only when an update exists - **Maintain simplicity** - All multi‑manager logic lives in one small helper and a concise lookup rather than a complex adapter hierarchy - Core `checkForUpdates` remains a single, easy‑to‑follow async function - **Dependencies added** - `fast-npm-meta`, `semver`, `package-manager-detector`, `@types/semver` ## Considerations If we decide to drop the interactive update‑message (`npm install -g @openai/codex`) rendering altogether, we could remove most of the installer‑detection code and dependencies, which would simplify the codebase further but result in a less friendly UX. ## Preview * npm ![refactor-update-check-flow-npm](https://github.com/user-attachments/assets/57320114-3fb6-4985-8780-3388a1d1ec85) * bun ![refactor-update-check-flow-bun](https://github.com/user-attachments/assets/d93bf0ae-a687-412a-ab92-581b4f967307) ## Simple Flow Chart ```mermaid flowchart TD A(Start) --> B[Read state] B --> C{Recent check?} C -- Yes --> Z[End] C -- No --> D[Fetch latest version] D --> E[Save check time] E --> F{Version data OK?} F -- No --> Z F -- Yes --> G{Update available?} G -- No --> Z G -- Yes --> H{Global install?} H -- Yes --> I[Select global manager] H -- No --> K{Local install?} K -- No --> Z K -- Yes --> L[Select local manager] I & L --> M[Render update message] M --> N[Format with boxen] N --> O[Print update] O --> Z ``` --- codex-cli/package.json | 4 + codex-cli/src/utils/check-updates.ts | 157 +++++------ .../src/utils/package-manager-detector.ts | 73 ++++++ .../__snapshots__/check-updates.test.ts.snap | 14 +- codex-cli/tests/check-updates.test.ts | 248 +++++++++++------- .../tests/package-manager-detector.test.ts | 66 +++++ pnpm-lock.yaml | 27 ++ 7 files changed, 414 insertions(+), 175 deletions(-) create mode 100644 codex-cli/src/utils/package-manager-detector.ts create mode 100644 codex-cli/tests/package-manager-detector.test.ts diff --git a/codex-cli/package.json b/codex-cli/package.json index 6afabe5d02..2bfe93ce70 100644 --- a/codex-cli/package.json +++ b/codex-cli/package.json @@ -35,6 +35,7 @@ "diff": "^7.0.0", "dotenv": "^16.1.4", "fast-deep-equal": "^3.1.3", + "fast-npm-meta": "^0.4.2", "figures": "^6.1.0", "file-type": "^20.1.0", "ink": "^5.2.0", @@ -44,6 +45,7 @@ "meow": "^13.2.0", "open": "^10.1.0", "openai": "^4.95.1", + "package-manager-detector": "^1.2.0", "react": "^18.2.0", "shell-quote": "^1.8.2", "strip-ansi": "^7.1.0", @@ -57,6 +59,7 @@ "@types/js-yaml": "^4.0.9", "@types/marked-terminal": "^6.1.1", "@types/react": "^18.0.32", + "@types/semver": "^7.7.0", "@types/shell-quote": "^1.7.5", "@types/which": "^3.0.4", "@typescript-eslint/eslint-plugin": "^7.18.0", @@ -71,6 +74,7 @@ "ink-testing-library": "^3.0.0", "prettier": "^2.8.7", "punycode": "^2.3.1", + "semver": "^7.7.1", "ts-node": "^10.9.1", "typescript": "^5.0.3", "vitest": "^3.0.9", diff --git a/codex-cli/src/utils/check-updates.ts b/codex-cli/src/utils/check-updates.ts index b3d6f85a29..5e326c1c93 100644 --- a/codex-cli/src/utils/check-updates.ts +++ b/codex-cli/src/utils/check-updates.ts @@ -1,87 +1,81 @@ -import { CONFIG_DIR } from "./config"; +import type { AgentName } from "package-manager-detector"; + +import { detectInstallerByPath } from "./package-manager-detector"; +import { CLI_VERSION } from "./session"; import boxen from "boxen"; import chalk from "chalk"; -import * as cp from "node:child_process"; +import { getLatestVersion } from "fast-npm-meta"; import { readFile, writeFile } from "node:fs/promises"; import { join } from "node:path"; -import which from "which"; +import { getUserAgent } from "package-manager-detector"; +import semver from "semver"; interface UpdateCheckState { lastUpdateCheck?: string; } -interface PackageInfo { - current: string; - wanted: string; - latest: string; - dependent: string; - location: string; -} - interface UpdateCheckInfo { currentVersion: string; latestVersion: string; } -const UPDATE_CHECK_FREQUENCY = 1000 * 60 * 60 * 24; // 1 day - -export async function getNPMCommandPath(): Promise { - try { - return await which(process.platform === "win32" ? "npm.cmd" : "npm"); - } catch { - return undefined; - } +export interface UpdateOptions { + manager: AgentName; + packageName: string; } -export async function checkOutdated( - npmCommandPath: string, -): Promise { - return new Promise((resolve, _reject) => { - // TODO: support local installation - // Right now we're using "--global", which only checks global packages. - // But codex might be installed locally — we should check the local version first, - // and only fall back to the global one if needed. - const args = ["outdated", "--global", "--json", "--", "@openai/codex"]; - // corepack npm wrapper would automatically update package.json. disable that behavior. - // COREPACK_ENABLE_AUTO_PIN disables the package.json overwrite, and - // COREPACK_ENABLE_PROJECT_SPEC makes the npm view command succeed - // even if packageManager specified a package manager other than npm. - const env = { - ...process.env, - COREPACK_ENABLE_AUTO_PIN: "0", - COREPACK_ENABLE_PROJECT_SPEC: "0", - }; - let options: cp.ExecFileOptions = { env }; - let commandPath = npmCommandPath; - if (process.platform === "win32") { - options = { ...options, shell: true }; - commandPath = `"${npmCommandPath}"`; - } - cp.execFile(commandPath, args, options, async (_error, stdout) => { - try { - const { name: packageName } = await import("../../package.json"); - const content: Record = JSON.parse(stdout); - if (!content[packageName]) { - // package not installed or not outdated - resolve(undefined); - return; - } +const UPDATE_CHECK_FREQUENCY = 1000 * 60 * 60 * 24; // 1 day - const currentVersion = content[packageName].current; - const latestVersion = content[packageName].latest; +export function renderUpdateCommand({ + manager, + packageName, +}: UpdateOptions): string { + const updateCommands: Record = { + npm: `npm install -g ${packageName}`, + pnpm: `pnpm add -g ${packageName}`, + bun: `bun add -g ${packageName}`, + /** Only works in yarn@v1 */ + yarn: `yarn global add ${packageName}`, + deno: `deno install -g npm:${packageName}`, + }; - resolve({ currentVersion, latestVersion }); - return; - } catch { - // ignore - } - resolve(undefined); - }); + return updateCommands[manager]; +} + +function renderUpdateMessage(options: UpdateOptions) { + const updateCommand = renderUpdateCommand(options); + return `To update, run ${chalk.magenta(updateCommand)} to update.`; +} + +async function writeState(stateFilePath: string, state: UpdateCheckState) { + await writeFile(stateFilePath, JSON.stringify(state, null, 2), { + encoding: "utf8", }); } +async function getUpdateCheckInfo( + packageName: string, +): Promise { + const metadata = await getLatestVersion(packageName, { + force: true, + throw: false, + }); + + if ("error" in metadata || !metadata?.version) { + return; + } + + return { + currentVersion: CLI_VERSION, + latestVersion: metadata.version, + }; +} + export async function checkForUpdates(): Promise { + const { CONFIG_DIR } = await import("./config"); const stateFile = join(CONFIG_DIR, "update-check.json"); + + // Load previous check timestamp let state: UpdateCheckState | undefined; try { state = JSON.parse(await readFile(stateFile, "utf8")); @@ -89,6 +83,7 @@ export async function checkForUpdates(): Promise { // ignore } + // Bail out if we checked less than the configured frequency ago if ( state?.lastUpdateCheck && Date.now() - new Date(state.lastUpdateCheck).valueOf() < @@ -97,25 +92,39 @@ export async function checkForUpdates(): Promise { return; } - const npmCommandPath = await getNPMCommandPath(); - if (!npmCommandPath) { - return; - } - - const packageInfo = await checkOutdated(npmCommandPath); + // Fetch current vs latest from the registry + const { name: packageName } = await import("../../package.json"); + const packageInfo = await getUpdateCheckInfo(packageName); await writeState(stateFile, { ...state, lastUpdateCheck: new Date().toUTCString(), }); - if (!packageInfo) { + if ( + !packageInfo || + !semver.gt(packageInfo.latestVersion, packageInfo.currentVersion) + ) { return; } - const updateMessage = `To update, run: ${chalk.cyan( - "npm install -g @openai/codex", - )} to update.`; + // Detect global installer + let managerName = await detectInstallerByPath(); + + // Fallback to the local package manager + if (!managerName) { + const local = getUserAgent(); + if (!local) { + // No package managers found, skip it. + return; + } + managerName = local; + } + + const updateMessage = renderUpdateMessage({ + manager: managerName, + packageName, + }); const box = boxen( `\ @@ -135,9 +144,3 @@ ${updateMessage}`, // eslint-disable-next-line no-console console.log(box); } - -async function writeState(stateFilePath: string, state: UpdateCheckState) { - await writeFile(stateFilePath, JSON.stringify(state, null, 2), { - encoding: "utf8", - }); -} diff --git a/codex-cli/src/utils/package-manager-detector.ts b/codex-cli/src/utils/package-manager-detector.ts new file mode 100644 index 0000000000..ecd8d9c7f6 --- /dev/null +++ b/codex-cli/src/utils/package-manager-detector.ts @@ -0,0 +1,73 @@ +import type { AgentName } from "package-manager-detector"; + +import { execFileSync } from "node:child_process"; +import { join, resolve } from "node:path"; +import which from "which"; + +function isInstalled(manager: AgentName): boolean { + try { + which.sync(manager); + return true; + } catch { + return false; + } +} + +function getGlobalBinDir(manager: AgentName): string | undefined { + if (!isInstalled(manager)) { + return; + } + + try { + switch (manager) { + case "npm": { + const stdout = execFileSync("npm", ["prefix", "-g"], { + encoding: "utf-8", + }); + return join(stdout.trim(), "bin"); + } + + case "pnpm": { + // pnpm bin -g prints the bin dir + const stdout = execFileSync("pnpm", ["bin", "-g"], { + encoding: "utf-8", + }); + return stdout.trim(); + } + + case "bun": { + // bun pm bin -g prints your bun global bin folder + const stdout = execFileSync("bun", ["pm", "bin", "-g"], { + encoding: "utf-8", + }); + return stdout.trim(); + } + + default: + return undefined; + } + } catch { + // ignore + } + + return undefined; +} + +export async function detectInstallerByPath(): Promise { + // e.g. /usr/local/bin/codex + const invoked = process.argv[1] && resolve(process.argv[1]); + if (!invoked) { + return; + } + + const supportedManagers: Array = ["npm", "pnpm", "bun"]; + + for (const mgr of supportedManagers) { + const binDir = getGlobalBinDir(mgr); + if (binDir && invoked.startsWith(binDir)) { + return mgr; + } + } + + return undefined; +} diff --git a/codex-cli/tests/__snapshots__/check-updates.test.ts.snap b/codex-cli/tests/__snapshots__/check-updates.test.ts.snap index 2c1631fb09..c9514dd390 100644 --- a/codex-cli/tests/__snapshots__/check-updates.test.ts.snap +++ b/codex-cli/tests/__snapshots__/check-updates.test.ts.snap @@ -1,12 +1,12 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`Check for updates > should outputs the update message when package is outdated 1`] = ` +exports[`checkForUpdates() > renders a box when a newer version exists and no global installer 1`] = ` " - ╭─────────────────────────────────────────────────────────────╮ - │ │ - │ Update available! 1.0.0 → 2.0.0. │ - │ To update, run: npm install -g @openai/codex to update. │ - │ │ - ╰─────────────────────────────────────────────────────────────╯ + ╭─────────────────────────────────────────────────╮ + │ │ + │ Update available! 1.0.0 → 2.0.0. │ + │ To update, run bun add -g my-pkg to update. │ + │ │ + ╰─────────────────────────────────────────────────╯ " `; diff --git a/codex-cli/tests/check-updates.test.ts b/codex-cli/tests/check-updates.test.ts index a77789681d..75ec8aaf4e 100644 --- a/codex-cli/tests/check-updates.test.ts +++ b/codex-cli/tests/check-updates.test.ts @@ -1,112 +1,178 @@ -import { describe, it, expect, vi } from "vitest"; +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { join } from "node:path"; +import os from "node:os"; +import type { UpdateOptions } from "../src/utils/check-updates"; +import { getLatestVersion } from "fast-npm-meta"; +import { getUserAgent } from "package-manager-detector"; import { checkForUpdates, - checkOutdated, - getNPMCommandPath, -} from "../src/utils/check-updates.js"; -import { execFile } from "node:child_process"; -import { join } from "node:path"; -import { CONFIG_DIR } from "src/utils/config.js"; -import { beforeEach } from "node:test"; - -vi.mock("which", () => ({ - default: vi.fn(() => "/usr/local/bin/npm"), -})); - -vi.mock("child_process", () => ({ - execFile: vi.fn((_cmd, _args, _opts, callback) => { - const stdout = JSON.stringify({ - "@openai/codex": { - current: "1.0.0", - latest: "2.0.0", - }, - }); - callback?.(null, stdout, ""); - return {} as any; - }), -})); + renderUpdateCommand, +} from "../src/utils/check-updates"; +import { detectInstallerByPath } from "../src/utils/package-manager-detector"; +import { CLI_VERSION } from "../src/utils/session"; +// In-memory FS mock let memfs: Record = {}; - -vi.mock("node:fs/promises", async (importOriginal) => ({ - ...(await importOriginal()), - readFile: async (path: string) => { - if (memfs[path] === undefined) { - throw new Error("ENOENT"); - } - return memfs[path]; - }, -})); - -beforeEach(() => { - memfs = {}; // reset in‑memory store +vi.mock("node:fs/promises", async (importOriginal) => { + return { + ...(await importOriginal()), + readFile: async (path: string) => { + if (!(path in memfs)) { + const err: any = new Error( + `ENOENT: no such file or directory, open '${path}'`, + ); + err.code = "ENOENT"; + throw err; + } + return memfs[path]; + }, + writeFile: async (path: string, data: string) => { + memfs[path] = data; + }, + rm: async (path: string) => { + delete memfs[path]; + }, + }; }); -describe("Check for updates", () => { - it("should return the path to npm", async () => { - const npmPath = await getNPMCommandPath(); - expect(npmPath).toBeDefined(); +// Mock package name & CLI version +const MOCK_PKG = "my-pkg"; +vi.mock("../package.json", () => ({ name: MOCK_PKG })); +vi.mock("../src/utils/session", () => ({ CLI_VERSION: "1.0.0" })); +vi.mock("../src/utils/package-manager-detector", async (importOriginal) => { + return { + ...(await importOriginal()), + detectInstallerByPath: vi.fn(), + }; +}); + +// Mock external services +vi.mock("fast-npm-meta", () => ({ getLatestVersion: vi.fn() })); +vi.mock("package-manager-detector", () => ({ getUserAgent: vi.fn() })); + +describe("renderUpdateCommand()", () => { + it.each([ + [{ manager: "npm", packageName: MOCK_PKG }, `npm install -g ${MOCK_PKG}`], + [{ manager: "pnpm", packageName: MOCK_PKG }, `pnpm add -g ${MOCK_PKG}`], + [{ manager: "bun", packageName: MOCK_PKG }, `bun add -g ${MOCK_PKG}`], + [{ manager: "yarn", packageName: MOCK_PKG }, `yarn global add ${MOCK_PKG}`], + [ + { manager: "deno", packageName: MOCK_PKG }, + `deno install -g npm:${MOCK_PKG}`, + ], + ])("%s → command", async (options, cmd) => { + expect(renderUpdateCommand(options as UpdateOptions)).toBe(cmd); + }); +}); + +describe("checkForUpdates()", () => { + // Use a stable directory under the OS temp + const TMP = join(os.tmpdir(), "update-test-memfs"); + const STATE_PATH = join(TMP, "update-check.json"); + + beforeEach(async () => { + memfs = {}; + // Mock CONFIG_DIR to our TMP + vi.doMock("../src/utils/config", () => ({ CONFIG_DIR: TMP })); + + // Freeze time so the 24h logic is deterministic + vi.useFakeTimers().setSystemTime(new Date("2025-01-01T00:00:00Z")); + vi.resetAllMocks(); }); - it("should return undefined if npm is not found", async () => { - vi.mocked(await import("which")).default.mockImplementationOnce(() => { - throw new Error("not found"); - }); - - const npmPath = await getNPMCommandPath(); - expect(npmPath).toBeUndefined(); + afterEach(async () => { + vi.useRealTimers(); }); - it("should return the return value when package is outdated", async () => { - const npmPath = await getNPMCommandPath(); + it("uses global installer when detected, ignoring local agent", async () => { + // seed old timestamp + const old = new Date("2000-01-01T00:00:00Z").toUTCString(); + memfs[STATE_PATH] = JSON.stringify({ lastUpdateCheck: old }); - const info = await checkOutdated(npmPath!); - expect(info).toStrictEqual({ - currentVersion: "1.0.0", - latestVersion: "2.0.0", - }); - }); + // simulate registry says update available + vi.mocked(getLatestVersion).mockResolvedValue({ version: "2.0.0" } as any); + // local agent would be npm, but global detection wins + vi.mocked(getUserAgent).mockReturnValue("npm"); + vi.mocked(detectInstallerByPath).mockReturnValue(Promise.resolve("pnpm")); - it("should return undefined when package is not outdated", async () => { - const npmPath = await getNPMCommandPath(); - vi.mocked(execFile).mockImplementationOnce( - (_cmd, _args, _opts, callback) => { - // Simulate the case where the package is not outdated, returning an empty object - const stdout = JSON.stringify({}); - callback?.(null, stdout, ""); - return {} as any; - }, - ); - - const info = await checkOutdated(npmPath!); - expect(info).toBeUndefined(); - }); - - it("should outputs the update message when package is outdated", async () => { - const codexStatePath = join(CONFIG_DIR, "update-check.json"); - // Use a fixed early date far in the past to ensure it's always at least 1 day before now - memfs[codexStatePath] = JSON.stringify({ - lastUpdateCheck: new Date("2000-01-01T00:00:00Z").toUTCString(), - }); - await checkForUpdates(); - // Spy on console.log to capture output const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + await checkForUpdates(); - expect(logSpy).toHaveBeenCalled(); - // The last call should be the boxen message - const lastCallArg = logSpy.mock.calls.at(-1)?.[0]; - expect(lastCallArg).toMatchSnapshot(); + + // should render using `pnpm` (global) rather than `npm` + expect(logSpy).toHaveBeenCalledOnce(); + const output = logSpy.mock.calls.at(0)?.at(0); + expect(output).toContain("pnpm add -g"); // global branch used + // state updated + const newState = JSON.parse(memfs[STATE_PATH]!); + expect(newState.lastUpdateCheck).toBe(new Date().toUTCString()); }); - it("should not output the update message when package is not outdated", async () => { - const codexStatePath = join(CONFIG_DIR, "update-check.json"); - memfs[codexStatePath] = JSON.stringify({ - lastUpdateCheck: new Date().toUTCString(), - }); - await checkForUpdates(); - // Spy on console.log to capture output + it("skips when lastUpdateCheck is still fresh ( { + // seed a timestamp 12h ago + const recent = new Date(Date.now() - 1000 * 60 * 60 * 12).toUTCString(); + memfs[STATE_PATH] = JSON.stringify({ lastUpdateCheck: recent }); + + const versionSpy = vi.mocked(getLatestVersion); const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + await checkForUpdates(); + + expect(versionSpy).not.toHaveBeenCalled(); expect(logSpy).not.toHaveBeenCalled(); }); + + it("does not print when up-to-date", async () => { + vi.mocked(getLatestVersion).mockResolvedValue({ + version: CLI_VERSION, + } as any); + vi.mocked(getUserAgent).mockReturnValue("npm"); + vi.mocked(detectInstallerByPath).mockResolvedValue(undefined); + + const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + + await checkForUpdates(); + + expect(logSpy).not.toHaveBeenCalled(); + // but state still written + const state = JSON.parse(memfs[STATE_PATH]!); + expect(state.lastUpdateCheck).toBe(new Date().toUTCString()); + }); + + it("does not print when no manager detected at all", async () => { + vi.mocked(getLatestVersion).mockResolvedValue({ version: "2.0.0" } as any); + vi.mocked(detectInstallerByPath).mockResolvedValue(undefined); + vi.mocked(getUserAgent).mockReturnValue(null); + + const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + + await checkForUpdates(); + + expect(logSpy).not.toHaveBeenCalled(); + // state still written + const state = JSON.parse(memfs[STATE_PATH]!); + expect(state.lastUpdateCheck).toBe(new Date().toUTCString()); + }); + + it("renders a box when a newer version exists and no global installer", async () => { + // old timestamp + const old = new Date("2000-01-01T00:00:00Z").toUTCString(); + memfs[STATE_PATH] = JSON.stringify({ lastUpdateCheck: old }); + + vi.mocked(getLatestVersion).mockResolvedValue({ version: "2.0.0" } as any); + vi.mocked(detectInstallerByPath).mockResolvedValue(undefined); + vi.mocked(getUserAgent).mockReturnValue("bun"); + + const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); + + await checkForUpdates(); + + expect(logSpy).toHaveBeenCalledOnce(); + const output = logSpy.mock.calls[0]![0] as string; + expect(output).toContain("bun add -g"); + expect(output).to.matchSnapshot(); + // state updated + const state = JSON.parse(memfs[STATE_PATH]!); + expect(state.lastUpdateCheck).toBe(new Date().toUTCString()); + }); }); diff --git a/codex-cli/tests/package-manager-detector.test.ts b/codex-cli/tests/package-manager-detector.test.ts new file mode 100644 index 0000000000..1e80ea58cd --- /dev/null +++ b/codex-cli/tests/package-manager-detector.test.ts @@ -0,0 +1,66 @@ +import { describe, it, expect, beforeEach, vi, afterEach } from "vitest"; +import which from "which"; +import { detectInstallerByPath } from "../src/utils/package-manager-detector"; +import { execFileSync } from "node:child_process"; + +vi.mock("which", () => ({ + default: { sync: vi.fn() }, +})); +vi.mock("node:child_process", () => ({ execFileSync: vi.fn() })); + +describe("detectInstallerByPath()", () => { + const originalArgv = process.argv; + const fakeBinDirs = { + // `npm prefix -g` returns the global “prefix” (we’ll add `/bin` when detecting) + npm: "/usr/local", + pnpm: "/home/user/.local/share/pnpm/bin", + bun: "/Users/test/.bun/bin", + } as const; + + beforeEach(() => { + vi.resetAllMocks(); + // Pretend each manager binary is on PATH: + vi.mocked(which.sync).mockImplementation(() => "/fake/path"); + + vi.mocked(execFileSync).mockImplementation( + ( + cmd: string, + _args: ReadonlyArray = [], + _options: unknown, + ): string => { + return fakeBinDirs[cmd as keyof typeof fakeBinDirs]; + }, + ); + }); + + afterEach(() => { + // Restore the real argv so tests don’t leak + process.argv = originalArgv; + }); + + it.each(Object.entries(fakeBinDirs))( + "detects %s when invoked from its global-bin", + async (manager, binDir) => { + // Simulate the shim living under that binDir + process.argv = + manager === "npm" + ? [process.argv[0]!, `${binDir}/bin/my-cli`] + : [process.argv[0]!, `${binDir}/my-cli`]; + const detected = await detectInstallerByPath(); + expect(detected).toBe(manager); + }, + ); + + it("returns undefined if argv[1] is missing", async () => { + process.argv = [process.argv[0]!]; + expect(await detectInstallerByPath()).toBeUndefined(); + expect(execFileSync).not.toHaveBeenCalled(); + }); + + it("returns undefined if shim isn't in any manager's bin", async () => { + // stub execFileSync to some other dirs + vi.mocked(execFileSync).mockImplementation(() => "/some/other/dir"); + process.argv = [process.argv[0]!, "/home/user/.node_modules/.bin/my-cli"]; + expect(await detectInstallerByPath()).toBeUndefined(); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1539652c8f..69491efb95 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -43,6 +43,9 @@ importers: fast-deep-equal: specifier: ^3.1.3 version: 3.1.3 + fast-npm-meta: + specifier: ^0.4.2 + version: 0.4.2 figures: specifier: ^6.1.0 version: 6.1.0 @@ -70,6 +73,9 @@ importers: openai: specifier: ^4.95.1 version: 4.95.1(ws@8.18.1)(zod@3.24.3) + package-manager-detector: + specifier: ^1.2.0 + version: 1.2.0 react: specifier: ^18.2.0 version: 18.3.1 @@ -104,6 +110,9 @@ importers: '@types/react': specifier: ^18.0.32 version: 18.3.20 + '@types/semver': + specifier: ^7.7.0 + version: 7.7.0 '@types/shell-quote': specifier: ^1.7.5 version: 1.7.5 @@ -146,6 +155,9 @@ importers: punycode: specifier: ^2.3.1 version: 2.3.1 + semver: + specifier: ^7.7.1 + version: 7.7.1 ts-node: specifier: ^10.9.1 version: 10.9.2(@types/node@22.14.1)(typescript@5.8.3) @@ -548,6 +560,9 @@ packages: '@types/react@18.3.20': resolution: {integrity: sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==} + '@types/semver@7.7.0': + resolution: {integrity: sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==} + '@types/shell-quote@1.7.5': resolution: {integrity: sha512-+UE8GAGRPbJVQDdxi16dgadcBfQ+KG2vgZhV1+3A1XmHbmwcdwhCUwIdy+d3pAGrbvgRoVSjeI9vOWyq376Yzw==} @@ -1168,6 +1183,9 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-npm-meta@0.4.2: + resolution: {integrity: sha512-BDN/yv8MN3fjh504wa7/niZojPtf/brWBsLKlw7Fv+Xh8Df+6ZEAFpp3zaal4etgDxxav1CuzKX5H0YVM9urEQ==} + fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} @@ -1826,6 +1844,9 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} + package-manager-detector@1.2.0: + resolution: {integrity: sha512-PutJepsOtsqVfUsxCzgTTpyXmiAgvKptIgY4th5eq5UXXFhj5PxfQ9hnGkypMeovpAvVshFRItoFHYO18TCOqA==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -2757,6 +2778,8 @@ snapshots: '@types/prop-types': 15.7.14 csstype: 3.1.3 + '@types/semver@7.7.0': {} + '@types/shell-quote@1.7.5': {} '@types/which@3.0.4': {} @@ -3558,6 +3581,8 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-npm-meta@0.4.2: {} + fastq@1.19.1: dependencies: reusify: 1.1.0 @@ -4243,6 +4268,8 @@ snapshots: dependencies: p-limit: 3.1.0 + package-manager-detector@1.2.0: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 From 8f1ea7fa855cc2a130824a272a35dcd031ff519b Mon Sep 17 00:00:00 2001 From: Aaron Casanova <32409546+aaronccasanova@users.noreply.github.com> Date: Mon, 21 Apr 2025 00:00:56 -0700 Subject: [PATCH 45/82] fix: remove extraneous type casts (#462) --- codex-cli/src/cli.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/codex-cli/src/cli.tsx b/codex-cli/src/cli.tsx index 722b9319e5..948c0d3d90 100644 --- a/codex-cli/src/cli.tsx +++ b/codex-cli/src/cli.tsx @@ -230,13 +230,13 @@ const fullContextMode = Boolean(cli.flags.fullContext); let config = loadConfig(undefined, undefined, { cwd: process.cwd(), disableProjectDoc: Boolean(cli.flags.noProjectDoc), - projectDocPath: cli.flags.projectDoc as string | undefined, + projectDocPath: cli.flags.projectDoc, isFullContext: fullContextMode, }); const prompt = cli.input[0]; const model = cli.flags.model ?? config.model; -const imagePaths = cli.flags.image as Array | undefined; +const imagePaths = cli.flags.image; const provider = cli.flags.provider ?? config.provider; const apiKey = getApiKey(provider); @@ -351,7 +351,7 @@ if (quietMode) { : config.approvalMode || AutoApprovalMode.SUGGEST; await runQuietMode({ - prompt: prompt as string, + prompt, imagePaths: imagePaths || [], approvalPolicy: quietApprovalPolicy, additionalWritableRoots, From dc276999a9d63feb305d4de7f176c411cef71ebf Mon Sep 17 00:00:00 2001 From: Thibault Sottiaux Date: Mon, 21 Apr 2025 09:51:34 -0400 Subject: [PATCH 46/82] chore: improve storage/ implementation; use log(...) consistently (#473) This PR tidies up primitives under storage/. **Noop changes:** * Promote logger implementation to top-level utility outside of agent/ * Use logger within storage primitives * Cleanup doc strings and comments **Functional changes:** * Increase command history size to 10_000 * Remove unnecessary debounce implementation and ensure a session ID is created only once per agent loop --------- Signed-off-by: Thibault Sottiaux --- codex-cli/src/cli.tsx | 2 +- .../chat/terminal-chat-input-thinking.tsx | 2 +- .../components/chat/terminal-chat-input.tsx | 2 +- .../chat/terminal-chat-new-input.tsx | 2 +- .../src/components/chat/terminal-chat.tsx | 5 +- codex-cli/src/utils/agent/agent-loop.ts | 2 +- .../src/utils/agent/handle-exec-command.ts | 2 +- .../src/utils/agent/platform-commands.ts | 2 +- .../src/utils/agent/sandbox/macos-seatbelt.ts | 2 +- codex-cli/src/utils/agent/sandbox/raw-exec.ts | 2 +- codex-cli/src/utils/config.ts | 2 +- codex-cli/src/utils/{agent => logger}/log.ts | 0 .../src/utils/storage/command-history.ts | 68 +++++++------------ codex-cli/src/utils/storage/save-rollout.ts | 31 +++------ package.json | 1 + 15 files changed, 49 insertions(+), 76 deletions(-) rename codex-cli/src/utils/{agent => logger}/log.ts (100%) diff --git a/codex-cli/src/cli.tsx b/codex-cli/src/cli.tsx index 948c0d3d90..e6bed1347e 100644 --- a/codex-cli/src/cli.tsx +++ b/codex-cli/src/cli.tsx @@ -14,7 +14,6 @@ import type { ResponseItem } from "openai/resources/responses/responses"; import App from "./app"; import { runSinglePass } from "./cli-singlepass"; import { AgentLoop } from "./utils/agent/agent-loop"; -import { initLogger } from "./utils/agent/log"; import { ReviewDecision } from "./utils/agent/review"; import { AutoApprovalMode } from "./utils/auto-approval-mode"; import { checkForUpdates } from "./utils/check-updates"; @@ -25,6 +24,7 @@ import { INSTRUCTIONS_FILEPATH, } from "./utils/config"; import { createInputItem } from "./utils/input-utils"; +import { initLogger } from "./utils/logger/log"; import { isModelSupportedForResponses } from "./utils/model-utils.js"; import { parseToolCall } from "./utils/parsers"; import { onExit, setInkRenderer } from "./utils/terminal"; diff --git a/codex-cli/src/components/chat/terminal-chat-input-thinking.tsx b/codex-cli/src/components/chat/terminal-chat-input-thinking.tsx index fdc8bd218c..dd938b0640 100644 --- a/codex-cli/src/components/chat/terminal-chat-input-thinking.tsx +++ b/codex-cli/src/components/chat/terminal-chat-input-thinking.tsx @@ -1,4 +1,4 @@ -import { log } from "../../utils/agent/log.js"; +import { log } from "../../utils/logger/log.js"; import { Box, Text, useInput, useStdin } from "ink"; import React, { useState } from "react"; import { useInterval } from "use-interval"; diff --git a/codex-cli/src/components/chat/terminal-chat-input.tsx b/codex-cli/src/components/chat/terminal-chat-input.tsx index ddb9c7a1c9..8768dd8d61 100644 --- a/codex-cli/src/components/chat/terminal-chat-input.tsx +++ b/codex-cli/src/components/chat/terminal-chat-input.tsx @@ -9,10 +9,10 @@ import type { import MultilineTextEditor from "./multiline-editor"; import { TerminalChatCommandReview } from "./terminal-chat-command-review.js"; import TextCompletions from "./terminal-chat-completions.js"; -import { log } from "../../utils/agent/log.js"; import { loadConfig } from "../../utils/config.js"; import { getFileSystemSuggestions } from "../../utils/file-system-suggestions.js"; import { createInputItem } from "../../utils/input-utils.js"; +import { log } from "../../utils/logger/log.js"; import { setSessionId } from "../../utils/session.js"; import { SLASH_COMMANDS, type SlashCommand } from "../../utils/slash-commands"; import { diff --git a/codex-cli/src/components/chat/terminal-chat-new-input.tsx b/codex-cli/src/components/chat/terminal-chat-new-input.tsx index 7dbe130e2c..95915934db 100644 --- a/codex-cli/src/components/chat/terminal-chat-new-input.tsx +++ b/codex-cli/src/components/chat/terminal-chat-new-input.tsx @@ -8,9 +8,9 @@ import type { import MultilineTextEditor from "./multiline-editor"; import { TerminalChatCommandReview } from "./terminal-chat-command-review.js"; -import { log } from "../../utils/agent/log.js"; import { loadConfig } from "../../utils/config.js"; import { createInputItem } from "../../utils/input-utils.js"; +import { log } from "../../utils/logger/log.js"; import { setSessionId } from "../../utils/session.js"; import { loadCommandHistory, diff --git a/codex-cli/src/components/chat/terminal-chat.tsx b/codex-cli/src/components/chat/terminal-chat.tsx index 9ebcfbc75e..e20097ebfc 100644 --- a/codex-cli/src/components/chat/terminal-chat.tsx +++ b/codex-cli/src/components/chat/terminal-chat.tsx @@ -15,13 +15,13 @@ import { formatCommandForDisplay } from "../../format-command.js"; import { useConfirmation } from "../../hooks/use-confirmation.js"; import { useTerminalSize } from "../../hooks/use-terminal-size.js"; import { AgentLoop } from "../../utils/agent/agent-loop.js"; -import { log } from "../../utils/agent/log.js"; import { ReviewDecision } from "../../utils/agent/review.js"; import { generateCompactSummary } from "../../utils/compact-summary.js"; import { OPENAI_BASE_URL, saveConfig } from "../../utils/config.js"; import { extractAppliedPatches as _extractAppliedPatches } from "../../utils/extract-applied-patches.js"; import { getGitDiff } from "../../utils/get-diff.js"; import { createInputItem } from "../../utils/input-utils.js"; +import { log } from "../../utils/logger/log.js"; import { getAvailableModels } from "../../utils/model-utils.js"; import { CLI_VERSION } from "../../utils/session.js"; import { shortCwd } from "../../utils/short-path.js"; @@ -237,6 +237,7 @@ export default function TerminalChat({ // Tear down any existing loop before creating a new one agentRef.current?.terminate(); + const sessionId = crypto.randomUUID(); agentRef.current = new AgentLoop({ model, provider, @@ -249,7 +250,7 @@ export default function TerminalChat({ log(`onItem: ${JSON.stringify(item)}`); setItems((prev) => { const updated = uniqueById([...prev, item as ResponseItem]); - saveRollout(updated); + saveRollout(sessionId, updated); return updated; }); }, diff --git a/codex-cli/src/utils/agent/agent-loop.ts b/codex-cli/src/utils/agent/agent-loop.ts index 311cad96d4..09bb26f826 100644 --- a/codex-cli/src/utils/agent/agent-loop.ts +++ b/codex-cli/src/utils/agent/agent-loop.ts @@ -10,8 +10,8 @@ import type { } from "openai/resources/responses/responses.mjs"; import type { Reasoning } from "openai/resources.mjs"; -import { log } from "./log.js"; import { OPENAI_TIMEOUT_MS, getApiKey, getBaseUrl } from "../config.js"; +import { log } from "../logger/log.js"; import { parseToolCallArguments } from "../parsers.js"; import { responsesCreateViaChatCompletions } from "../responses.js"; import { diff --git a/codex-cli/src/utils/agent/handle-exec-command.ts b/codex-cli/src/utils/agent/handle-exec-command.ts index 97b78c0817..ea87feca6d 100644 --- a/codex-cli/src/utils/agent/handle-exec-command.ts +++ b/codex-cli/src/utils/agent/handle-exec-command.ts @@ -5,12 +5,12 @@ import type { ApplyPatchCommand, ApprovalPolicy } from "../../approvals.js"; import type { ResponseInputItem } from "openai/resources/responses/responses.mjs"; import { exec, execApplyPatch } from "./exec.js"; -import { isLoggingEnabled, log } from "./log.js"; import { ReviewDecision } from "./review.js"; import { FullAutoErrorMode } from "../auto-approval-mode.js"; import { SandboxType } from "./sandbox/interface.js"; import { canAutoApprove } from "../../approvals.js"; import { formatCommandForDisplay } from "../../format-command.js"; +import { isLoggingEnabled, log } from "../logger/log.js"; import { access } from "fs/promises"; // --------------------------------------------------------------------------- diff --git a/codex-cli/src/utils/agent/platform-commands.ts b/codex-cli/src/utils/agent/platform-commands.ts index 085c575d44..161388f942 100644 --- a/codex-cli/src/utils/agent/platform-commands.ts +++ b/codex-cli/src/utils/agent/platform-commands.ts @@ -2,7 +2,7 @@ * Utility functions for handling platform-specific commands */ -import { log } from "./log.js"; +import { log } from "../logger/log.js"; /** * Map of Unix commands to their Windows equivalents diff --git a/codex-cli/src/utils/agent/sandbox/macos-seatbelt.ts b/codex-cli/src/utils/agent/sandbox/macos-seatbelt.ts index 760ba63d8c..934056d9af 100644 --- a/codex-cli/src/utils/agent/sandbox/macos-seatbelt.ts +++ b/codex-cli/src/utils/agent/sandbox/macos-seatbelt.ts @@ -2,7 +2,7 @@ import type { ExecResult } from "./interface.js"; import type { SpawnOptions } from "child_process"; import { exec } from "./raw-exec.js"; -import { log } from "../log.js"; +import { log } from "../../logger/log.js"; function getCommonRoots() { return [ diff --git a/codex-cli/src/utils/agent/sandbox/raw-exec.ts b/codex-cli/src/utils/agent/sandbox/raw-exec.ts index 22add83ed8..b3d1d8ec25 100644 --- a/codex-cli/src/utils/agent/sandbox/raw-exec.ts +++ b/codex-cli/src/utils/agent/sandbox/raw-exec.ts @@ -7,7 +7,7 @@ import type { StdioPipe, } from "child_process"; -import { log } from "../log.js"; +import { log } from "../../logger/log.js"; import { adaptCommandForPlatform } from "../platform-commands.js"; import { spawn } from "child_process"; import * as os from "os"; diff --git a/codex-cli/src/utils/config.ts b/codex-cli/src/utils/config.ts index ada9502520..91e0c3678a 100644 --- a/codex-cli/src/utils/config.ts +++ b/codex-cli/src/utils/config.ts @@ -8,8 +8,8 @@ import type { FullAutoErrorMode } from "./auto-approval-mode.js"; -import { log } from "./agent/log.js"; import { AutoApprovalMode } from "./auto-approval-mode.js"; +import { log } from "./logger/log.js"; import { providers } from "./providers.js"; import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"; import { load as loadYaml, dump as dumpYaml } from "js-yaml"; diff --git a/codex-cli/src/utils/agent/log.ts b/codex-cli/src/utils/logger/log.ts similarity index 100% rename from codex-cli/src/utils/agent/log.ts rename to codex-cli/src/utils/logger/log.ts diff --git a/codex-cli/src/utils/storage/command-history.ts b/codex-cli/src/utils/storage/command-history.ts index 997c4872ad..d774b53340 100644 --- a/codex-cli/src/utils/storage/command-history.ts +++ b/codex-cli/src/utils/storage/command-history.ts @@ -1,12 +1,13 @@ +import { log } from "../logger/log.js"; import { existsSync } from "fs"; import fs from "fs/promises"; import os from "os"; import path from "path"; const HISTORY_FILE = path.join(os.homedir(), ".codex", "history.json"); -const DEFAULT_HISTORY_SIZE = 1000; +const DEFAULT_HISTORY_SIZE = 10_000; -// Regex patterns for sensitive commands that should not be saved +// Regex patterns for sensitive commands that should not be saved. const SENSITIVE_PATTERNS = [ /\b[A-Za-z0-9-_]{20,}\b/, // API keys and tokens /\bpassword\b/i, @@ -18,7 +19,7 @@ const SENSITIVE_PATTERNS = [ export interface HistoryConfig { maxSize: number; saveHistory: boolean; - sensitivePatterns: Array; // Array of regex patterns as strings + sensitivePatterns: Array; // Regex patterns. } export interface HistoryEntry { @@ -32,9 +33,6 @@ export const DEFAULT_HISTORY_CONFIG: HistoryConfig = { sensitivePatterns: [], }; -/** - * Loads command history from the history file - */ export async function loadCommandHistory(): Promise> { try { if (!existsSync(HISTORY_FILE)) { @@ -45,26 +43,21 @@ export async function loadCommandHistory(): Promise> { const history = JSON.parse(data) as Array; return Array.isArray(history) ? history : []; } catch (error) { - // Use error logger but for production would use a proper logging system - // eslint-disable-next-line no-console - console.error("Failed to load command history:", error); + log(`error: failed to load command history: ${error}`); return []; } } -/** - * Saves command history to the history file - */ export async function saveCommandHistory( history: Array, config: HistoryConfig = DEFAULT_HISTORY_CONFIG, ): Promise { try { - // Create directory if it doesn't exist + // Create directory if it doesn't exist. const dir = path.dirname(HISTORY_FILE); await fs.mkdir(dir, { recursive: true }); - // Trim history to max size + // Trim history to max size. const trimmedHistory = history.slice(-config.maxSize); await fs.writeFile( @@ -73,14 +66,10 @@ export async function saveCommandHistory( "utf-8", ); } catch (error) { - // eslint-disable-next-line no-console - console.error("Failed to save command history:", error); + log(`error: failed to save command history: ${error}`); } } -/** - * Adds a command to history if it's not sensitive - */ export async function addToHistory( command: string, history: Array, @@ -90,46 +79,41 @@ export async function addToHistory( return history; } - // Check if command contains sensitive information - if (isSensitiveCommand(command, config.sensitivePatterns)) { + // Skip commands with sensitive information. + if (commandHasSensitiveInfo(command, config.sensitivePatterns)) { return history; } - // Check for duplicate (don't add if it's the same as the last command) + // Check for duplicate (don't add if it's the same as the last command). const lastEntry = history[history.length - 1]; if (lastEntry && lastEntry.command === command) { return history; } - // Add new entry - const newEntry: HistoryEntry = { - command, - timestamp: Date.now(), - }; - - const newHistory = [...history, newEntry]; - - // Save to file + // Add new entry. + const newHistory: Array = [ + ...history, + { + command, + timestamp: Date.now(), + }, + ]; await saveCommandHistory(newHistory, config); - return newHistory; } -/** - * Checks if a command contains sensitive information - */ -function isSensitiveCommand( +function commandHasSensitiveInfo( command: string, additionalPatterns: Array = [], ): boolean { - // Check built-in patterns + // Check built-in patterns. for (const pattern of SENSITIVE_PATTERNS) { if (pattern.test(command)) { return true; } } - // Check additional patterns from config + // Check additional patterns from config. for (const patternStr of additionalPatterns) { try { const pattern = new RegExp(patternStr); @@ -137,23 +121,19 @@ function isSensitiveCommand( return true; } } catch (error) { - // Invalid regex pattern, skip it + // Invalid regex pattern, skip it. } } return false; } -/** - * Clears the command history - */ export async function clearCommandHistory(): Promise { try { if (existsSync(HISTORY_FILE)) { await fs.writeFile(HISTORY_FILE, JSON.stringify([]), "utf-8"); } } catch (error) { - // eslint-disable-next-line no-console - console.error("Failed to clear command history:", error); + log(`error: failed to clear command history: ${error}`); } } diff --git a/codex-cli/src/utils/storage/save-rollout.ts b/codex-cli/src/utils/storage/save-rollout.ts index 76d931fd9f..d6b5a49190 100644 --- a/codex-cli/src/utils/storage/save-rollout.ts +++ b/codex-cli/src/utils/storage/save-rollout.ts @@ -1,20 +1,19 @@ -/* eslint-disable no-console */ - import type { ResponseItem } from "openai/resources/responses/responses"; import { loadConfig } from "../config"; +import { log } from "../logger/log.js"; import fs from "fs/promises"; import os from "os"; import path from "path"; const SESSIONS_ROOT = path.join(os.homedir(), ".codex", "sessions"); -async function saveRolloutToHomeSessions( +async function saveRolloutAsync( + sessionId: string, items: Array, ): Promise { await fs.mkdir(SESSIONS_ROOT, { recursive: true }); - const sessionId = crypto.randomUUID(); const timestamp = new Date().toISOString(); const ts = timestamp.replace(/[:.]/g, "-").slice(0, 10); const filename = `rollout-${ts}-${sessionId}.json`; @@ -39,23 +38,15 @@ async function saveRolloutToHomeSessions( "utf8", ); } catch (error) { - console.error(`Failed to save rollout to ${filePath}: `, error); + log(`error: failed to save rollout to ${filePath}: ${error}`); } } -let debounceTimer: NodeJS.Timeout | null = null; -let pendingItems: Array | null = null; - -export function saveRollout(items: Array): void { - pendingItems = items; - if (debounceTimer) { - clearTimeout(debounceTimer); - } - debounceTimer = setTimeout(() => { - if (pendingItems) { - saveRolloutToHomeSessions(pendingItems).catch(() => {}); - pendingItems = null; - } - debounceTimer = null; - }, 2000); +export function saveRollout( + sessionId: string, + items: Array, +): void { + // Best-effort. We also do not log here in case of failure as that should be taken care of + // by `saveRolloutAsync` already. + saveRolloutAsync(sessionId, items).catch(() => {}); } diff --git a/package.json b/package.json index 215616bec1..b516ce40fa 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "build": "pnpm --filter @openai/codex run build", "test": "pnpm --filter @openai/codex run test", "lint": "pnpm --filter @openai/codex run lint", + "lint:fix": "pnpm --filter @openai/codex run lint:fix", "typecheck": "pnpm --filter @openai/codex run typecheck", "changelog": "git-cliff --config cliff.toml --output CHANGELOG.ignore.md $LAST_RELEASE_TAG..HEAD", "prepare": "husky", From 3c4f1fea9b0c1184c13ad10e66dbe41f7077c0da Mon Sep 17 00:00:00 2001 From: Thibault Sottiaux Date: Mon, 21 Apr 2025 12:33:57 -0400 Subject: [PATCH 47/82] chore: consolidate model utils and drive-by cleanups (#476) Signed-off-by: Thibault Sottiaux --- codex-cli/src/approvals.ts | 14 +- codex-cli/src/cli.tsx | 47 ++++--- .../src/components/chat/multiline-editor.tsx | 2 +- .../components/chat/terminal-chat-utils.ts | 113 ---------------- .../src/components/chat/terminal-chat.tsx | 57 ++++---- .../src/components/chat/terminal-header.tsx | 4 +- .../src/components/singlepass-cli-app.tsx | 4 +- codex-cli/src/hooks/use-confirmation.ts | 1 - codex-cli/src/text-buffer.ts | 4 +- codex-cli/src/utils/agent/agent-loop.ts | 4 +- .../src/utils/agent/handle-exec-command.ts | 2 +- codex-cli/src/utils/config.ts | 4 +- codex-cli/src/utils/model-utils.ts | 124 +++++++++++++++++- codex-cli/src/utils/terminal-chat-utils.ts | 0 codex-cli/tests/agent-project-doc.test.ts | 2 +- codex-cli/tests/approvals.test.ts | 2 +- codex-cli/tests/config.test.tsx | 2 +- .../tests/model-utils-network-error.test.ts | 9 +- .../tests/multiline-history-behavior.test.tsx | 2 +- codex-cli/tests/text-buffer.test.ts | 2 +- codex-cli/tests/typeahead-scroll.test.tsx | 2 +- 21 files changed, 196 insertions(+), 205 deletions(-) delete mode 100644 codex-cli/src/components/chat/terminal-chat-utils.ts create mode 100644 codex-cli/src/utils/terminal-chat-utils.ts diff --git a/codex-cli/src/approvals.ts b/codex-cli/src/approvals.ts index 3b3162af7f..b9eb50d1d7 100644 --- a/codex-cli/src/approvals.ts +++ b/codex-cli/src/approvals.ts @@ -136,8 +136,8 @@ export function canAutoApprove( // bashCmd could be a mix of strings and operators, e.g.: // "ls || (true && pwd)" => [ 'ls', { op: '||' }, '(', 'true', { op: '&&' }, 'pwd', ')' ] // We try to ensure that *every* command segment is deemed safe and that - // all operators belong to an allow‑list. If so, the entire expression is - // considered auto‑approvable. + // all operators belong to an allow-list. If so, the entire expression is + // considered auto-approvable. const shellSafe = isEntireShellExpressionSafe(bashCmd); if (shellSafe != null) { @@ -333,7 +333,7 @@ export function isSafeCommand( }; case "true": return { - reason: "No‑op (true)", + reason: "No-op (true)", group: "Utility", }; case "echo": @@ -442,10 +442,10 @@ function isValidSedNArg(arg: string | undefined): boolean { // ---------------- Helper utilities for complex shell expressions ----------------- -// A conservative allow‑list of bash operators that do not, on their own, cause +// A conservative allow-list of bash operators that do not, on their own, cause // side effects. Redirections (>, >>, <, etc.) and command substitution `$()` // are intentionally excluded. Parentheses used for grouping are treated as -// strings by `shell‑quote`, so we do not add them here. Reference: +// strings by `shell-quote`, so we do not add them here. Reference: // https://github.com/substack/node-shell-quote#parsecmd-opts const SAFE_SHELL_OPERATORS: ReadonlySet = new Set([ "&&", // logical AND @@ -471,7 +471,7 @@ function isEntireShellExpressionSafe( } try { - // Collect command segments delimited by operators. `shell‑quote` represents + // Collect command segments delimited by operators. `shell-quote` represents // subshell grouping parentheses as literal strings "(" and ")"; treat them // as unsafe to keep the logic simple (since subshells could introduce // unexpected scope changes). @@ -539,7 +539,7 @@ function isParseEntryWithOp( return ( typeof entry === "object" && entry != null && - // Using the safe `in` operator keeps the check property‑safe even when + // Using the safe `in` operator keeps the check property-safe even when // `entry` is a `string`. "op" in entry && typeof (entry as { op?: unknown }).op === "string" diff --git a/codex-cli/src/cli.tsx b/codex-cli/src/cli.tsx index e6bed1347e..2ec883d448 100644 --- a/codex-cli/src/cli.tsx +++ b/codex-cli/src/cli.tsx @@ -136,7 +136,7 @@ const cli = meow( }, noProjectDoc: { type: "boolean", - description: "Disable automatic inclusion of project‑level codex.md", + description: "Disable automatic inclusion of project-level codex.md", }, projectDoc: { type: "string", @@ -202,19 +202,20 @@ complete -c codex -a '(_fish_complete_path)' -d 'file path'`, console.log(script); process.exit(0); } -// Show help if requested + +// For --help, show help and exit. if (cli.flags.help) { cli.showHelp(); } -// Handle config flag: open instructions file in editor and exit +// For --config, open custom instructions file in editor and exit. if (cli.flags.config) { - // Ensure configuration and instructions file exist try { - loadConfig(); + loadConfig(); // Ensures the file is created if it doesn't already exit. } catch { // ignore errors } + const filePath = INSTRUCTIONS_FILEPATH; const editor = process.env["EDITOR"] || (process.platform === "win32" ? "notepad" : "vi"); @@ -237,13 +238,13 @@ let config = loadConfig(undefined, undefined, { const prompt = cli.input[0]; const model = cli.flags.model ?? config.model; const imagePaths = cli.flags.image; -const provider = cli.flags.provider ?? config.provider; +const provider = cli.flags.provider ?? config.provider ?? "openai"; const apiKey = getApiKey(provider); if (!apiKey) { // eslint-disable-next-line no-console console.error( - `\n${chalk.red("Missing OpenAI API key.")}\n\n` + + `\n${chalk.red(`Missing ${provider} API key.`)}\n\n` + `Set the environment variable ${chalk.bold("OPENAI_API_KEY")} ` + `and re-run this command.\n` + `You can create a key here: ${chalk.bold( @@ -262,13 +263,11 @@ config = { provider, }; -// Check for updates after loading config -// This is important because we write state file in the config dir +// Check for updates after loading config. This is important because we write state file in +// the config dir. await checkForUpdates().catch(); -// --------------------------------------------------------------------------- -// --flex-mode validation (only allowed for o3 and o4-mini) -// --------------------------------------------------------------------------- +// For --flex-mode, validate and exit if incorrect. if (cli.flags.flexMode) { const allowedFlexModels = new Set(["o3", "o4-mini"]); if (!allowedFlexModels.has(config.model)) { @@ -282,13 +281,13 @@ if (cli.flags.flexMode) { } if ( - !(await isModelSupportedForResponses(config.model)) && + !(await isModelSupportedForResponses(provider, config.model)) && (!provider || provider.toLowerCase() === "openai") ) { // eslint-disable-next-line no-console console.error( `The model "${config.model}" does not appear in the list of models ` + - `available to your account. Double‑check the spelling (use\n` + + `available to your account. Double-check the spelling (use\n` + ` openai models list\n` + `to see the full list) or choose another model with the --model flag.`, ); @@ -297,6 +296,7 @@ if ( let rollout: AppRollout | undefined; +// For --view, optionally load an existing rollout from disk, display it and exit. if (cli.flags.view) { const viewPath = cli.flags.view; const absolutePath = path.isAbsolute(viewPath) @@ -312,7 +312,7 @@ if (cli.flags.view) { } } -// If we are running in --fullcontext mode, do that and exit. +// For --fullcontext, run the separate cli entrypoint and exit. if (fullContextMode) { await runSinglePass({ originalPrompt: prompt, @@ -328,11 +328,8 @@ const additionalWritableRoots: ReadonlyArray = ( cli.flags.writableRoot ?? [] ).map((p) => path.resolve(p)); -// If we are running in --quiet mode, do that and exit. -const quietMode = Boolean(cli.flags.quiet); -const fullStdout = Boolean(cli.flags.fullStdout); - -if (quietMode) { +// For --quiet, run the cli without user interactions and exit. +if (cli.flags.quiet) { process.env["CODEX_QUIET_MODE"] = "1"; if (!prompt || prompt.trim() === "") { // eslint-disable-next-line no-console @@ -389,7 +386,7 @@ const instance = render( imagePaths={imagePaths} approvalPolicy={approvalPolicy} additionalWritableRoots={additionalWritableRoots} - fullStdout={fullStdout} + fullStdout={Boolean(cli.flags.fullStdout)} />, { patchConsole: process.env["DEBUG"] ? false : true, @@ -501,13 +498,13 @@ process.on("SIGQUIT", exit); process.on("SIGTERM", exit); // --------------------------------------------------------------------------- -// Fallback for Ctrl‑C when stdin is in raw‑mode +// Fallback for Ctrl-C when stdin is in raw-mode // --------------------------------------------------------------------------- if (process.stdin.isTTY) { // Ensure we do not leave the terminal in raw mode if the user presses - // Ctrl‑C while some other component has focus and Ink is intercepting - // input. Node does *not* emit a SIGINT in raw‑mode, so we listen for the + // Ctrl-C while some other component has focus and Ink is intercepting + // input. Node does *not* emit a SIGINT in raw-mode, so we listen for the // corresponding byte (0x03) ourselves and trigger a graceful shutdown. const onRawData = (data: Buffer | string): void => { const str = Buffer.isBuffer(data) ? data.toString("utf8") : data; @@ -518,6 +515,6 @@ if (process.stdin.isTTY) { process.stdin.on("data", onRawData); } -// Ensure terminal clean‑up always runs, even when other code calls +// Ensure terminal clean-up always runs, even when other code calls // `process.exit()` directly. process.once("exit", onExit); diff --git a/codex-cli/src/components/chat/multiline-editor.tsx b/codex-cli/src/components/chat/multiline-editor.tsx index a91eceea34..eea5ec4852 100644 --- a/codex-cli/src/components/chat/multiline-editor.tsx +++ b/codex-cli/src/components/chat/multiline-editor.tsx @@ -14,7 +14,7 @@ import React, { useRef, useState } from "react"; * The real `process.stdin` object exposed by Node.js inherits these methods * from `Socket`, but the lightweight stub used in tests only extends * `EventEmitter`. Ink calls the two methods when enabling/disabling raw - * mode, so make them harmless no‑ops when they're absent to avoid runtime + * mode, so make them harmless no-ops when they're absent to avoid runtime * failures during unit tests. * ----------------------------------------------------------------------- */ diff --git a/codex-cli/src/components/chat/terminal-chat-utils.ts b/codex-cli/src/components/chat/terminal-chat-utils.ts deleted file mode 100644 index 73ab3c971a..0000000000 --- a/codex-cli/src/components/chat/terminal-chat-utils.ts +++ /dev/null @@ -1,113 +0,0 @@ -import type { ResponseItem } from "openai/resources/responses/responses.mjs"; - -import { approximateTokensUsed } from "../../utils/approximate-tokens-used.js"; - -/** - * Type‑guard that narrows a {@link ResponseItem} to one that represents a - * user‑authored message. The OpenAI SDK represents both input *and* output - * messages with a discriminated union where: - * • `type` is the string literal "message" and - * • `role` is one of "user" | "assistant" | "system" | "developer". - * - * For the purposes of de‑duplication we only care about *user* messages so we - * detect those here in a single, reusable helper. - */ -function isUserMessage( - item: ResponseItem, -): item is ResponseItem & { type: "message"; role: "user"; content: unknown } { - return item.type === "message" && (item as { role?: string }).role === "user"; -} - -/** - * Returns the maximum context length (in tokens) for a given model. - * These numbers are best‑effort guesses and provide a basis for UI percentages. - */ -export function maxTokensForModel(model: string): number { - const lower = model.toLowerCase(); - if (lower.includes("32k")) { - return 32000; - } - if (lower.includes("16k")) { - return 16000; - } - if (lower.includes("8k")) { - return 8000; - } - if (lower.includes("4k")) { - return 4000; - } - // Default to 128k for newer long‑context models - return 128000; -} - -/** - * Calculates the percentage of tokens remaining in context for a model. - */ -export function calculateContextPercentRemaining( - items: Array, - model: string, -): number { - const used = approximateTokensUsed(items); - const max = maxTokensForModel(model); - const remaining = Math.max(0, max - used); - return (remaining / max) * 100; -} - -/** - * Deduplicate the stream of {@link ResponseItem}s before they are persisted in - * component state. - * - * Historically we used the (optional) {@code id} field returned by the - * OpenAI streaming API as the primary key: the first occurrence of any given - * {@code id} “won” and subsequent duplicates were dropped. In practice this - * proved brittle because locally‑generated user messages don’t include an - * {@code id}. The result was that if a user quickly pressed twice the - * exact same message would appear twice in the transcript. - * - * The new rules are therefore: - * 1. If a {@link ResponseItem} has an {@code id} keep only the *first* - * occurrence of that {@code id} (this retains the previous behaviour for - * assistant / tool messages). - * 2. Additionally, collapse *consecutive* user messages with identical - * content. Two messages are considered identical when their serialized - * {@code content} array matches exactly. We purposefully restrict this - * to **adjacent** duplicates so that legitimately repeated questions at - * a later point in the conversation are still shown. - */ -export function uniqueById(items: Array): Array { - const seenIds = new Set(); - const deduped: Array = []; - - for (const item of items) { - // ────────────────────────────────────────────────────────────────── - // Rule #1 – de‑duplicate by id when present - // ────────────────────────────────────────────────────────────────── - if (typeof item.id === "string" && item.id.length > 0) { - if (seenIds.has(item.id)) { - continue; // skip duplicates - } - seenIds.add(item.id); - } - - // ────────────────────────────────────────────────────────────────── - // Rule #2 – collapse consecutive identical user messages - // ────────────────────────────────────────────────────────────────── - if (isUserMessage(item) && deduped.length > 0) { - const prev = deduped[deduped.length - 1]!; - - if ( - isUserMessage(prev) && - // Note: the `content` field is an array of message parts. Performing - // a deep compare is over‑kill here; serialising to JSON is sufficient - // (and fast for the tiny payloads involved). - JSON.stringify(prev.content) === JSON.stringify(item.content) - ) { - continue; // skip duplicate user message - } - } - - deduped.push(item); - } - - return deduped; -} diff --git a/codex-cli/src/components/chat/terminal-chat.tsx b/codex-cli/src/components/chat/terminal-chat.tsx index e20097ebfc..8558208005 100644 --- a/codex-cli/src/components/chat/terminal-chat.tsx +++ b/codex-cli/src/components/chat/terminal-chat.tsx @@ -6,10 +6,6 @@ import type { ResponseItem } from "openai/resources/responses/responses.mjs"; import TerminalChatInput from "./terminal-chat-input.js"; import { TerminalChatToolCallCommand } from "./terminal-chat-tool-call-command.js"; -import { - calculateContextPercentRemaining, - uniqueById, -} from "./terminal-chat-utils.js"; import TerminalMessageHistory from "./terminal-message-history.js"; import { formatCommandForDisplay } from "../../format-command.js"; import { useConfirmation } from "../../hooks/use-confirmation.js"; @@ -22,7 +18,11 @@ import { extractAppliedPatches as _extractAppliedPatches } from "../../utils/ext import { getGitDiff } from "../../utils/get-diff.js"; import { createInputItem } from "../../utils/input-utils.js"; import { log } from "../../utils/logger/log.js"; -import { getAvailableModels } from "../../utils/model-utils.js"; +import { + getAvailableModels, + calculateContextPercentRemaining, + uniqueById, +} from "../../utils/model-utils.js"; import { CLI_VERSION } from "../../utils/session.js"; import { shortCwd } from "../../utils/short-path.js"; import { saveRollout } from "../../utils/storage/save-rollout.js"; @@ -106,11 +106,8 @@ async function generateCommandExplanation( } catch (error) { log(`Error generating command explanation: ${error}`); - // Improved error handling with more specific error information let errorMessage = "Unable to generate explanation due to an error."; - if (error instanceof Error) { - // Include specific error message for better debugging errorMessage = `Unable to generate explanation: ${error.message}`; // If it's an API error, check for more specific information @@ -141,18 +138,17 @@ export default function TerminalChat({ additionalWritableRoots, fullStdout, }: Props): React.ReactElement { - // Desktop notification setting const notify = config.notify; const [model, setModel] = useState(config.model); const [provider, setProvider] = useState(config.provider || "openai"); const [lastResponseId, setLastResponseId] = useState(null); const [items, setItems] = useState>([]); const [loading, setLoading] = useState(false); - // Allow switching approval modes at runtime via an overlay. const [approvalPolicy, setApprovalPolicy] = useState( initialApprovalPolicy, ); const [thinkingSeconds, setThinkingSeconds] = useState(0); + const handleCompact = async () => { setLoading(true); try { @@ -185,6 +181,7 @@ export default function TerminalChat({ setLoading(false); } }; + const { requestConfirmation, confirmationPrompt, @@ -215,13 +212,13 @@ export default function TerminalChat({ // DEBUG: log every render w/ key bits of state // ──────────────────────────────────────────────────────────────── log( - `render – agent? ${Boolean(agentRef.current)} loading=${loading} items=${ + `render - agent? ${Boolean(agentRef.current)} loading=${loading} items=${ items.length }`, ); useEffect(() => { - // Skip recreating the agent if awaiting a decision on a pending confirmation + // Skip recreating the agent if awaiting a decision on a pending confirmation. if (confirmationPrompt != null) { log("skip AgentLoop recreation due to pending confirmationPrompt"); return; @@ -234,7 +231,7 @@ export default function TerminalChat({ )} approvalPolicy=${approvalPolicy}`, ); - // Tear down any existing loop before creating a new one + // Tear down any existing loop before creating a new one. agentRef.current?.terminate(); const sessionId = crypto.randomUUID(); @@ -267,11 +264,9 @@ export default function TerminalChat({ , ); - // If the user wants an explanation, generate one and ask again + // If the user wants an explanation, generate one and ask again. if (review === ReviewDecision.EXPLAIN) { log(`Generating explanation for command: ${commandForDisplay}`); - - // Generate an explanation using the same model const explanation = await generateCommandExplanation( command, model, @@ -279,7 +274,7 @@ export default function TerminalChat({ ); log(`Generated explanation: ${explanation}`); - // Ask for confirmation again, but with the explanation + // Ask for confirmation again, but with the explanation. const confirmResult = await requestConfirmation( , ); - // Update the decision based on the second confirmation + // Update the decision based on the second confirmation. review = confirmResult.decision; customDenyMessage = confirmResult.customDenyMessage; - // Return the final decision with the explanation + // Return the final decision with the explanation. return { review, customDenyMessage, applyPatch, explanation }; } @@ -299,7 +294,7 @@ export default function TerminalChat({ }, }); - // force a render so JSX below can "see" the freshly created agent + // Force a render so JSX below can "see" the freshly created agent. forceUpdate(); log(`AgentLoop created: ${inspect(agentRef.current, { depth: 1 })}`); @@ -320,7 +315,7 @@ export default function TerminalChat({ additionalWritableRoots, ]); - // whenever loading starts/stops, reset or start a timer — but pause the + // Whenever loading starts/stops, reset or start a timer — but pause the // timer while a confirmation overlay is displayed so we don't trigger a // re‑render every second during apply_patch reviews. useEffect(() => { @@ -345,14 +340,15 @@ export default function TerminalChat({ }; }, [loading, confirmationPrompt]); - // Notify desktop with a preview when an assistant response arrives + // Notify desktop with a preview when an assistant response arrives. const prevLoadingRef = useRef(false); useEffect(() => { - // Only notify when notifications are enabled + // Only notify when notifications are enabled. if (!notify) { prevLoadingRef.current = loading; return; } + if ( prevLoadingRef.current && !loading && @@ -389,7 +385,7 @@ export default function TerminalChat({ prevLoadingRef.current = loading; }, [notify, loading, confirmationPrompt, items, PWD]); - // Let's also track whenever the ref becomes available + // Let's also track whenever the ref becomes available. const agent = agentRef.current; useEffect(() => { log(`agentRef.current is now ${Boolean(agent)}`); @@ -412,7 +408,7 @@ export default function TerminalChat({ const inputItems = [ await createInputItem(initialPrompt || "", initialImagePaths || []), ]; - // Clear them to prevent subsequent runs + // Clear them to prevent subsequent runs. setInitialPrompt(""); setInitialImagePaths([]); agent?.run(inputItems); @@ -447,7 +443,7 @@ export default function TerminalChat({ // eslint-disable-next-line react-hooks/exhaustive-deps }, []); - // Just render every item in order, no grouping/collapse + // Just render every item in order, no grouping/collapse. const lastMessageBatch = items.map((item) => ({ item })); const groupCounts: Record = {}; const userMsgCount = items.filter( @@ -626,10 +622,10 @@ export default function TerminalChat({ agent?.cancel(); setLoading(false); - // Select default model for the new provider + // Select default model for the new provider. const defaultModel = model; - // Save provider to config + // Save provider to config. const updatedConfig = { ...config, provider: newProvider, @@ -669,13 +665,12 @@ export default function TerminalChat({ { - // update approval policy without cancelling an in-progress session + // Update approval policy without cancelling an in-progress session. if (newMode === approvalPolicy) { return; } - // update state + setApprovalPolicy(newMode as ApprovalPolicy); - // update existing AgentLoop instance if (agentRef.current) { ( agentRef.current as unknown as { diff --git a/codex-cli/src/components/chat/terminal-header.tsx b/codex-cli/src/components/chat/terminal-header.tsx index bdc49946a5..1bd08aef2a 100644 --- a/codex-cli/src/components/chat/terminal-header.tsx +++ b/codex-cli/src/components/chat/terminal-header.tsx @@ -34,9 +34,9 @@ const TerminalHeader: React.FC = ({ {terminalRows < 10 ? ( // Compact header for small terminal windows - ● Codex v{version} – {PWD} – {model} ({provider}) –{" "} + ● Codex v{version} - {PWD} - {model} ({provider}) -{" "} {approvalPolicy} - {flexModeEnabled ? " – flex-mode" : ""} + {flexModeEnabled ? " - flex-mode" : ""} ) : ( <> diff --git a/codex-cli/src/components/singlepass-cli-app.tsx b/codex-cli/src/components/singlepass-cli-app.tsx index c52ae1be3d..0c5eeb4e11 100644 --- a/codex-cli/src/components/singlepass-cli-app.tsx +++ b/codex-cli/src/components/singlepass-cli-app.tsx @@ -399,8 +399,8 @@ export function SinglePassApp({ }); const openai = new OpenAI({ - apiKey: getApiKey(config.provider), - baseURL: getBaseUrl(config.provider), + apiKey: getApiKey(config.provider ?? "openai"), + baseURL: getBaseUrl(config.provider ?? "openai"), timeout: OPENAI_TIMEOUT_MS, }); const chatResp = await openai.beta.chat.completions.parse({ diff --git a/codex-cli/src/hooks/use-confirmation.ts b/codex-cli/src/hooks/use-confirmation.ts index b10a309dc5..07c9e75cd0 100644 --- a/codex-cli/src/hooks/use-confirmation.ts +++ b/codex-cli/src/hooks/use-confirmation.ts @@ -1,4 +1,3 @@ -// use-confirmation.ts import type { ReviewDecision } from "../utils/agent/review"; import type React from "react"; diff --git a/codex-cli/src/text-buffer.ts b/codex-cli/src/text-buffer.ts index d7ce153060..fe4e2a4759 100644 --- a/codex-cli/src/text-buffer.ts +++ b/codex-cli/src/text-buffer.ts @@ -423,7 +423,7 @@ export default class TextBuffer { /** Delete the word to the *left* of the caret, mirroring common * Ctrl/Alt+Backspace behaviour in editors & terminals. Both the adjacent * whitespace *and* the word characters immediately preceding the caret are - * removed. If the caret is already at column‑0 this becomes a no‑op. */ + * removed. If the caret is already at column‑0 this becomes a no-op. */ deleteWordLeft(): void { dbg("deleteWordLeft", { beforeCursor: this.getCursor() }); @@ -710,7 +710,7 @@ export default class TextBuffer { } endSelection(): void { - // no‑op for now, kept for API symmetry + // no-op for now, kept for API symmetry // we rely on anchor + current cursor to compute selection } diff --git a/codex-cli/src/utils/agent/agent-loop.ts b/codex-cli/src/utils/agent/agent-loop.ts index 09bb26f826..9cf5d30ff7 100644 --- a/codex-cli/src/utils/agent/agent-loop.ts +++ b/codex-cli/src/utils/agent/agent-loop.ts @@ -744,7 +744,7 @@ export class AgentLoop { for await (const event of stream as AsyncIterable) { log(`AgentLoop.run(): response event ${event.type}`); - // process and surface each item (no‑op until we can depend on streaming events) + // process and surface each item (no-op until we can depend on streaming events) if (event.type === "response.output_item.done") { const item = event.item; // 1) if it's a reasoning item, annotate it @@ -936,7 +936,7 @@ export class AgentLoop { ], }); } catch { - /* no‑op – emitting the error message is best‑effort */ + /* no-op – emitting the error message is best‑effort */ } this.onLoading(false); return; diff --git a/codex-cli/src/utils/agent/handle-exec-command.ts b/codex-cli/src/utils/agent/handle-exec-command.ts index ea87feca6d..aea2c3a707 100644 --- a/codex-cli/src/utils/agent/handle-exec-command.ts +++ b/codex-cli/src/utils/agent/handle-exec-command.ts @@ -144,7 +144,7 @@ export async function handleExecCommand( abortSignal, ); // If the operation was aborted in the meantime, propagate the cancellation - // upward by returning an empty (no‑op) result so that the agent loop will + // upward by returning an empty (no-op) result so that the agent loop will // exit cleanly without emitting spurious output. if (abortSignal?.aborted) { return { diff --git a/codex-cli/src/utils/config.ts b/codex-cli/src/utils/config.ts index 91e0c3678a..a4a9c0cb0e 100644 --- a/codex-cli/src/utils/config.ts +++ b/codex-cli/src/utils/config.ts @@ -41,7 +41,7 @@ export function setApiKey(apiKey: string): void { OPENAI_API_KEY = apiKey; } -export function getBaseUrl(provider: string = "openai"): string | undefined { +export function getBaseUrl(provider: string): string | undefined { const providerInfo = providers[provider.toLowerCase()]; if (providerInfo) { return providerInfo.baseURL; @@ -49,7 +49,7 @@ export function getBaseUrl(provider: string = "openai"): string | undefined { return undefined; } -export function getApiKey(provider: string = "openai"): string | undefined { +export function getApiKey(provider: string): string | undefined { const providerInfo = providers[provider.toLowerCase()]; if (providerInfo) { if (providerInfo.name === "Ollama") { diff --git a/codex-cli/src/utils/model-utils.ts b/codex-cli/src/utils/model-utils.ts index 946756cb24..31f1afe6c4 100644 --- a/codex-cli/src/utils/model-utils.ts +++ b/codex-cli/src/utils/model-utils.ts @@ -1,3 +1,6 @@ +import type { ResponseItem } from "openai/resources/responses/responses.mjs"; + +import { approximateTokensUsed } from "./approximate-tokens-used.js"; import { getBaseUrl, getApiKey } from "./config"; import OpenAI from "openai"; @@ -11,9 +14,8 @@ export const RECOMMENDED_MODELS: Array = ["o4-mini", "o3"]; * enters interactive mode. The request is made exactly once during the * lifetime of the process and the results are cached for subsequent calls. */ - async function fetchModels(provider: string): Promise> { - // If the user has not configured an API key we cannot hit the network. + // If the user has not configured an API key we cannot retrieve the models. if (!getApiKey(provider)) { throw new Error("No API key configured for provider: " + provider); } @@ -26,7 +28,7 @@ async function fetchModels(provider: string): Promise> { for await (const model of list as AsyncIterable<{ id?: string }>) { if (model && typeof model.id === "string") { let modelStr = model.id; - // fix for gemini + // Fix for gemini. if (modelStr.startsWith("models/")) { modelStr = modelStr.replace("models/", ""); } @@ -40,6 +42,7 @@ async function fetchModels(provider: string): Promise> { } } +/** Returns the list of models available for the provided key / credentials. */ export async function getAvailableModels( provider: string, ): Promise> { @@ -47,11 +50,11 @@ export async function getAvailableModels( } /** - * Verify that the provided model identifier is present in the set returned by - * {@link getAvailableModels}. The list of models is fetched from the OpenAI - * `/models` endpoint the first time it is required and then cached in‑process. + * Verifies that the provided model identifier is present in the set returned by + * {@link getAvailableModels}. */ export async function isModelSupportedForResponses( + provider: string, model: string | undefined | null, ): Promise { if ( @@ -64,7 +67,7 @@ export async function isModelSupportedForResponses( try { const models = await Promise.race>([ - getAvailableModels("openai"), + getAvailableModels(provider), new Promise>((resolve) => setTimeout(() => resolve([]), MODEL_LIST_TIMEOUT_MS), ), @@ -82,3 +85,110 @@ export async function isModelSupportedForResponses( return true; } } + +/** Returns the maximum context length (in tokens) for a given model. */ +function maxTokensForModel(model: string): number { + // TODO: These numbers are best‑effort guesses and provide a basis for UI percentages. They + // should be provider & model specific instead of being wild guesses. + + const lower = model.toLowerCase(); + if (lower.includes("32k")) { + return 32000; + } + if (lower.includes("16k")) { + return 16000; + } + if (lower.includes("8k")) { + return 8000; + } + if (lower.includes("4k")) { + return 4000; + } + return 128000; // Default to 128k for any other model. +} + +/** Calculates the percentage of tokens remaining in context for a model. */ +export function calculateContextPercentRemaining( + items: Array, + model: string, +): number { + const used = approximateTokensUsed(items); + const max = maxTokensForModel(model); + const remaining = Math.max(0, max - used); + return (remaining / max) * 100; +} + +/** + * Type‑guard that narrows a {@link ResponseItem} to one that represents a + * user‑authored message. The OpenAI SDK represents both input *and* output + * messages with a discriminated union where: + * • `type` is the string literal "message" and + * • `role` is one of "user" | "assistant" | "system" | "developer". + * + * For the purposes of de‑duplication we only care about *user* messages so we + * detect those here in a single, reusable helper. + */ +function isUserMessage( + item: ResponseItem, +): item is ResponseItem & { type: "message"; role: "user"; content: unknown } { + return item.type === "message" && (item as { role?: string }).role === "user"; +} + +/** + * Deduplicate the stream of {@link ResponseItem}s before they are persisted in + * component state. + * + * Historically we used the (optional) {@code id} field returned by the + * OpenAI streaming API as the primary key: the first occurrence of any given + * {@code id} “won” and subsequent duplicates were dropped. In practice this + * proved brittle because locally‑generated user messages don’t include an + * {@code id}. The result was that if a user quickly pressed twice the + * exact same message would appear twice in the transcript. + * + * The new rules are therefore: + * 1. If a {@link ResponseItem} has an {@code id} keep only the *first* + * occurrence of that {@code id} (this retains the previous behaviour for + * assistant / tool messages). + * 2. Additionally, collapse *consecutive* user messages with identical + * content. Two messages are considered identical when their serialized + * {@code content} array matches exactly. We purposefully restrict this + * to **adjacent** duplicates so that legitimately repeated questions at + * a later point in the conversation are still shown. + */ +export function uniqueById(items: Array): Array { + const seenIds = new Set(); + const deduped: Array = []; + + for (const item of items) { + // ────────────────────────────────────────────────────────────────── + // Rule #1 – de‑duplicate by id when present + // ────────────────────────────────────────────────────────────────── + if (typeof item.id === "string" && item.id.length > 0) { + if (seenIds.has(item.id)) { + continue; // skip duplicates + } + seenIds.add(item.id); + } + + // ────────────────────────────────────────────────────────────────── + // Rule #2 – collapse consecutive identical user messages + // ────────────────────────────────────────────────────────────────── + if (isUserMessage(item) && deduped.length > 0) { + const prev = deduped[deduped.length - 1]!; + + if ( + isUserMessage(prev) && + // Note: the `content` field is an array of message parts. Performing + // a deep compare is over‑kill here; serialising to JSON is sufficient + // (and fast for the tiny payloads involved). + JSON.stringify(prev.content) === JSON.stringify(item.content) + ) { + continue; // skip duplicate user message + } + } + + deduped.push(item); + } + + return deduped; +} diff --git a/codex-cli/src/utils/terminal-chat-utils.ts b/codex-cli/src/utils/terminal-chat-utils.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/codex-cli/tests/agent-project-doc.test.ts b/codex-cli/tests/agent-project-doc.test.ts index 4b8951e7a0..d421c268c0 100644 --- a/codex-cli/tests/agent-project-doc.test.ts +++ b/codex-cli/tests/agent-project-doc.test.ts @@ -50,7 +50,7 @@ vi.mock("openai", () => { // The AgentLoop pulls these helpers in order to decide whether a command can // be auto‑approved. None of that matters for this test, so we stub the module -// with minimal no‑op implementations. +// with minimal no-op implementations. vi.mock("../src/approvals.js", () => { return { __esModule: true, diff --git a/codex-cli/tests/approvals.test.ts b/codex-cli/tests/approvals.test.ts index a4c08b04a6..43490cf84e 100644 --- a/codex-cli/tests/approvals.test.ts +++ b/codex-cli/tests/approvals.test.ts @@ -79,7 +79,7 @@ describe("canAutoApprove()", () => { test("true command is considered safe", () => { expect(check(["true"])).toEqual({ type: "auto-approve", - reason: "No‑op (true)", + reason: "No-op (true)", group: "Utility", runInSandbox: false, }); diff --git a/codex-cli/tests/config.test.tsx b/codex-cli/tests/config.test.tsx index 49b3229b73..867b957dfc 100644 --- a/codex-cli/tests/config.test.tsx +++ b/codex-cli/tests/config.test.tsx @@ -26,7 +26,7 @@ vi.mock("fs", async () => { memfs[path] = data; }, mkdirSync: () => { - // no‑op in in‑memory store + // no-op in in‑memory store }, rmSync: (path: string) => { // recursively delete any key under this prefix diff --git a/codex-cli/tests/model-utils-network-error.test.ts b/codex-cli/tests/model-utils-network-error.test.ts index f67b4fc4db..537e7fdb35 100644 --- a/codex-cli/tests/model-utils-network-error.test.ts +++ b/codex-cli/tests/model-utils-network-error.test.ts @@ -44,7 +44,7 @@ describe("model-utils – offline resilience", () => { "../src/utils/model-utils.js" ); - const supported = await isModelSupportedForResponses("o4-mini"); + const supported = await isModelSupportedForResponses("openai", "o4-mini"); expect(supported).toBe(true); }); @@ -63,8 +63,11 @@ describe("model-utils – offline resilience", () => { "../src/utils/model-utils.js" ); - // Should resolve true despite the network failure - const supported = await isModelSupportedForResponses("some-model"); + // Should resolve true despite the network failure. + const supported = await isModelSupportedForResponses( + "openai", + "some-model", + ); expect(supported).toBe(true); }); }); diff --git a/codex-cli/tests/multiline-history-behavior.test.tsx b/codex-cli/tests/multiline-history-behavior.test.tsx index cada52ddab..ee46d3d5c1 100644 --- a/codex-cli/tests/multiline-history-behavior.test.tsx +++ b/codex-cli/tests/multiline-history-behavior.test.tsx @@ -57,7 +57,7 @@ async function type( await flush(); } -/** Build a set of no‑op callbacks so renders with minimal +/** Build a set of no-op callbacks so renders with minimal * scaffolding. */ function stubProps(): any { diff --git a/codex-cli/tests/text-buffer.test.ts b/codex-cli/tests/text-buffer.test.ts index c3f33d0fa1..e5c532f7cb 100644 --- a/codex-cli/tests/text-buffer.test.ts +++ b/codex-cli/tests/text-buffer.test.ts @@ -127,7 +127,7 @@ describe("TextBuffer – basic editing parity with Rust suite", () => { expect(buf.getCursor()).toEqual([0, 2]); // after 'b' }); - it("is a no‑op at the very beginning of the buffer", () => { + it("is a no-op at the very beginning of the buffer", () => { const buf = new TextBuffer("ab"); buf.backspace(); // caret starts at (0,0) diff --git a/codex-cli/tests/typeahead-scroll.test.tsx b/codex-cli/tests/typeahead-scroll.test.tsx index fab7c753b8..e0e496adac 100644 --- a/codex-cli/tests/typeahead-scroll.test.tsx +++ b/codex-cli/tests/typeahead-scroll.test.tsx @@ -26,7 +26,7 @@ vi.mock("../src/components/select-input/select-input.js", () => { // Ink's toggles raw‑mode which calls .ref() / .unref() on stdin. // The test environment's mock streams don't implement those methods, so we -// polyfill them to no‑ops on the prototype *before* the component tree mounts. +// polyfill them to no-ops on the prototype *before* the component tree mounts. import { EventEmitter } from "node:events"; if (!(EventEmitter.prototype as any).ref) { (EventEmitter.prototype as any).ref = () => {}; From 5b19451770f8e218c6f64c0d5be96313012ed968 Mon Sep 17 00:00:00 2001 From: Jon Church Date: Mon, 21 Apr 2025 12:35:25 -0400 Subject: [PATCH 48/82] chore(build): cleanup dist before build (#477) Another one that I noticed. The dist structure is very simple rn, so unlikely to run into orphaned files as you're emitting a single built artifact which wil be overwritten on build, but I always prefer to do clean builds as "hygiene". I had a dirty dist personally after local development and testing some things, as an example. Alternatives could be to create a `clean` script with cross platform `rimraf dist` --- codex-cli/build.mjs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/codex-cli/build.mjs b/codex-cli/build.mjs index 0fcd5c6ece..465e8b9244 100644 --- a/codex-cli/build.mjs +++ b/codex-cli/build.mjs @@ -1,6 +1,8 @@ import * as esbuild from "esbuild"; import * as fs from "fs"; import * as path from "path"; + +const OUT_DIR = 'dist' /** * ink attempts to import react-devtools-core in an ESM-unfriendly way: * @@ -39,6 +41,11 @@ const isDevBuild = const plugins = [ignoreReactDevToolsPlugin]; +// Build Hygiene, ensure we drop previous dist dir and any leftover files +const outPath = path.resolve(OUT_DIR); +if (fs.existsSync(outPath)) { + fs.rmSync(outPath, { recursive: true, force: true }); +} // Add a shebang that enables source‑map support for dev builds so that stack // traces point to the original TypeScript lines without requiring callers to @@ -50,7 +57,7 @@ if (isDevBuild) { name: "dev-shebang", setup(build) { build.onEnd(async () => { - const outFile = path.resolve(isDevBuild ? "dist/cli-dev.js" : "dist/cli.js"); + const outFile = path.resolve(isDevBuild ? `${OUT_DIR}/cli-dev.js` : `${OUT_DIR}/cli.js`); let code = await fs.promises.readFile(outFile, "utf8"); if (code.startsWith("#!")) { code = code.replace(/^#!.*\n/, devShebangLine); @@ -69,7 +76,7 @@ esbuild format: "esm", platform: "node", tsconfig: "tsconfig.json", - outfile: isDevBuild ? "dist/cli-dev.js" : "dist/cli.js", + outfile: isDevBuild ? `${OUT_DIR}/cli-dev.js` : `${OUT_DIR}/cli.js`, minify: !isDevBuild, sourcemap: isDevBuild ? "inline" : true, plugins, From 797eba4930fc655d9818a10e74b74977f8d5f54d Mon Sep 17 00:00:00 2001 From: nerdielol <60550503+nerdielol@users.noreply.github.com> Date: Mon, 21 Apr 2025 12:39:46 -0400 Subject: [PATCH 49/82] fix: `/clear` now clears terminal screen and resets context left indicator (#425) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What does this PR do? * Implements the full `/clear` command in **codex‑cli**: * Resets chat history **and** wipes the terminal screen. * Shows a single system message: `Context cleared`. * Adds comprehensive unit tests for the new behaviour. ## Why is it needed? * Fixes user‑reported bugs: * **#395** * **#405** ## How is it implemented? * **Code** – Adds `process.stdout.write('\x1b[3J\x1b[H\x1b[2J')` in `terminal.tsx`. Removed reference to `prev` in ` setItems((prev) => [ ...prev, ` in `terminal-chat-new-input.tsx` & `terminal-chat-input.tsx`. ## CI / QA All commands pass locally: ```bash pnpm test # green pnpm run lint # green pnpm run typecheck # zero TS errors ``` ## Results https://github.com/user-attachments/assets/11dcf05c-e054-495a-8ecb-ac6ef21a9da4 --------- Co-authored-by: Thibault Sottiaux --- .../components/chat/terminal-chat-input.tsx | 18 +- .../chat/terminal-chat-new-input.tsx | 9 +- codex-cli/src/utils/terminal.ts | 2 + codex-cli/tests/clear-command.test.tsx | 174 ++++++++++++++++++ 4 files changed, 197 insertions(+), 6 deletions(-) create mode 100644 codex-cli/tests/clear-command.test.tsx diff --git a/codex-cli/src/components/chat/terminal-chat-input.tsx b/codex-cli/src/components/chat/terminal-chat-input.tsx index 8768dd8d61..aad2e6b3c3 100644 --- a/codex-cli/src/components/chat/terminal-chat-input.tsx +++ b/codex-cli/src/components/chat/terminal-chat-input.tsx @@ -191,6 +191,12 @@ export default function TerminalChatInput({ case "/bug": onSubmit(cmd); break; + case "/clear": + onSubmit(cmd); + break; + case "/clearhistory": + onSubmit(cmd); + break; default: break; } @@ -396,19 +402,29 @@ export default function TerminalChatInput({ setInput(""); setSessionId(""); setLastResponseId(""); + // Clear the terminal screen (including scrollback) before resetting context clearTerminal(); + // Emit a system notice in the chat; no raw console writes so Ink keeps control. + // Emit a system message to confirm the clear action. We *append* // it so Ink's treats it as new output and actually renders it. setItems((prev) => { const filteredOldItems = prev.filter((item) => { + // Remove any token‑heavy entries (user/assistant turns and function calls) if ( item.type === "message" && (item.role === "user" || item.role === "assistant") ) { return false; } - return true; + if ( + item.type === "function_call" || + item.type === "function_call_output" + ) { + return false; + } + return true; // keep developer/system and other meta entries }); return [ diff --git a/codex-cli/src/components/chat/terminal-chat-new-input.tsx b/codex-cli/src/components/chat/terminal-chat-new-input.tsx index 95915934db..b03cc9636d 100644 --- a/codex-cli/src/components/chat/terminal-chat-new-input.tsx +++ b/codex-cli/src/components/chat/terminal-chat-new-input.tsx @@ -263,17 +263,16 @@ export default function TerminalChatInput({ setInput(""); setSessionId(""); setLastResponseId(""); + // Clear the terminal screen (including scrollback) before resetting context clearTerminal(); - // Emit a system message to confirm the clear action. We *append* - // it so Ink's treats it as new output and actually renders it. - setItems((prev) => [ - ...prev, + // Print a clear confirmation and reset conversation items. + setItems([ { id: `clear-${Date.now()}`, type: "message", role: "system", - content: [{ type: "input_text", text: "Context cleared" }], + content: [{ type: "input_text", text: "Terminal cleared" }], }, ]); diff --git a/codex-cli/src/utils/terminal.ts b/codex-cli/src/utils/terminal.ts index e8187eba6b..296cbb39b2 100644 --- a/codex-cli/src/utils/terminal.ts +++ b/codex-cli/src/utils/terminal.ts @@ -50,6 +50,8 @@ export function clearTerminal(): void { if (inkRenderer) { inkRenderer.clear(); } + // Also clear scrollback and primary buffer to ensure a truly blank slate + process.stdout.write("\x1b[3J\x1b[H\x1b[2J"); } export function onExit(): void { diff --git a/codex-cli/tests/clear-command.test.tsx b/codex-cli/tests/clear-command.test.tsx new file mode 100644 index 0000000000..bab9b84c9b --- /dev/null +++ b/codex-cli/tests/clear-command.test.tsx @@ -0,0 +1,174 @@ +import React from "react"; +import type { ComponentProps } from "react"; +import { describe, it, expect, vi } from "vitest"; +import { renderTui } from "./ui-test-helpers.js"; +import TerminalChatInput from "../src/components/chat/terminal-chat-input.js"; +import TerminalChatNewInput from "../src/components/chat/terminal-chat-new-input.js"; +import * as TermUtils from "../src/utils/terminal.js"; + +// ------------------------------------------------------------------------------------------------- +// Helpers +// ------------------------------------------------------------------------------------------------- + +async function type( + stdin: NodeJS.WritableStream, + text: string, + flush: () => Promise, +): Promise { + stdin.write(text); + await flush(); +} + +// ------------------------------------------------------------------------------------------------- +// Tests +// ------------------------------------------------------------------------------------------------- + +describe("/clear command", () => { + it("invokes clearTerminal and resets context in TerminalChatInput", async () => { + const clearSpy = vi + .spyOn(TermUtils, "clearTerminal") + .mockImplementation(() => {}); + + const setItems = vi.fn(); + + // Minimal stub of a ResponseItem – cast to bypass exhaustive type checks in this test context + const existingItems = [ + { + id: "dummy-1", + type: "message", + role: "system", + content: [{ type: "input_text", text: "Old item" }], + }, + ] as Array; + + const props: ComponentProps = { + isNew: false, + loading: false, + submitInput: () => {}, + confirmationPrompt: null, + explanation: undefined, + submitConfirmation: () => {}, + setLastResponseId: () => {}, + setItems, + contextLeftPercent: 100, + openOverlay: () => {}, + openModelOverlay: () => {}, + openApprovalOverlay: () => {}, + openHelpOverlay: () => {}, + openDiffOverlay: () => {}, + onCompact: () => {}, + interruptAgent: () => {}, + active: true, + thinkingSeconds: 0, + items: existingItems, + }; + + const { stdin, flush, cleanup } = renderTui( + , + ); + + await flush(); + + await type(stdin, "/clear", flush); + await type(stdin, "\r", flush); // press Enter + + // Allow any asynchronous state updates to propagate + await flush(); + + expect(clearSpy).toHaveBeenCalledTimes(2); + expect(setItems).toHaveBeenCalledTimes(2); + + const stateUpdater = setItems.mock.calls[0]![0]; + expect(typeof stateUpdater).toBe("function"); + const newItems = stateUpdater(existingItems); + expect(Array.isArray(newItems)).toBe(true); + expect(newItems).toHaveLength(2); + expect(newItems.at(-1)).toMatchObject({ + role: "system", + type: "message", + content: [{ type: "input_text", text: "Terminal cleared" }], + }); + + cleanup(); + clearSpy.mockRestore(); + }); + + it("invokes clearTerminal and resets context in TerminalChatNewInput", async () => { + const clearSpy = vi + .spyOn(TermUtils, "clearTerminal") + .mockImplementation(() => {}); + + const setItems = vi.fn(); + + const props: ComponentProps = { + isNew: false, + loading: false, + submitInput: () => {}, + confirmationPrompt: null, + explanation: undefined, + submitConfirmation: () => {}, + setLastResponseId: () => {}, + setItems, + contextLeftPercent: 100, + openOverlay: () => {}, + openModelOverlay: () => {}, + openApprovalOverlay: () => {}, + openHelpOverlay: () => {}, + openDiffOverlay: () => {}, + interruptAgent: () => {}, + active: true, + thinkingSeconds: 0, + }; + + const { stdin, flush, cleanup } = renderTui( + , + ); + + await flush(); + + await type(stdin, "/clear", flush); + await type(stdin, "\r", flush); // press Enter + + await flush(); + + expect(clearSpy).toHaveBeenCalledTimes(1); + expect(setItems).toHaveBeenCalledTimes(1); + + const firstArg = setItems.mock.calls[0]![0]; + expect(Array.isArray(firstArg)).toBe(true); + expect(firstArg).toHaveLength(1); + expect(firstArg[0]).toMatchObject({ + role: "system", + type: "message", + content: [{ type: "input_text", text: "Terminal cleared" }], + }); + + cleanup(); + clearSpy.mockRestore(); + }); +}); + +describe("clearTerminal", () => { + it("writes escape sequence to stdout", () => { + const originalQuiet = process.env["CODEX_QUIET_MODE"]; + delete process.env["CODEX_QUIET_MODE"]; + + process.env["CODEX_QUIET_MODE"] = "0"; + + const writeSpy = vi + .spyOn(process.stdout, "write") + .mockImplementation(() => true); + + TermUtils.clearTerminal(); + + expect(writeSpy).toHaveBeenCalledWith("\x1b[3J\x1b[H\x1b[2J"); + + writeSpy.mockRestore(); + + if (originalQuiet !== undefined) { + process.env["CODEX_QUIET_MODE"] = originalQuiet; + } else { + delete process.env["CODEX_QUIET_MODE"]; + } + }); +}); From d36d295a1a8d047fa5d64cc4d67f92f113660f24 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Mon, 21 Apr 2025 09:52:11 -0700 Subject: [PATCH 50/82] revert #386 due to unsafe shell command parsing (#478) Reverts https://github.com/openai/codex/pull/386 because: * The parsing logic for shell commands was unsafe (`split(/\s+/)` instead of something like `shell-quote`) * We have a different plan for supporting auto-approved commands. --- README.md | 3 --- codex-cli/src/approvals.ts | 19 ------------------- codex-cli/src/utils/config.ts | 16 ---------------- codex-cli/tests/approvals.test.ts | 31 +------------------------------ 4 files changed, 1 insertion(+), 68 deletions(-) diff --git a/README.md b/README.md index 2094b9398c..e5690d7d48 100644 --- a/README.md +++ b/README.md @@ -289,9 +289,6 @@ model: o4-mini # Default model approvalMode: suggest # or auto-edit, full-auto fullAutoErrorMode: ask-user # or ignore-and-continue notify: true # Enable desktop notifications for responses -safeCommands: - - npm test # Automatically approve npm test - - yarn lint # Automatically approve yarn lint ``` ```json diff --git a/codex-cli/src/approvals.ts b/codex-cli/src/approvals.ts index b9eb50d1d7..f4a35402f9 100644 --- a/codex-cli/src/approvals.ts +++ b/codex-cli/src/approvals.ts @@ -4,7 +4,6 @@ import { identify_files_added, identify_files_needed, } from "./utils/agent/apply-patch"; -import { loadConfig } from "./utils/config"; import * as path from "path"; import { parse } from "shell-quote"; @@ -297,24 +296,6 @@ export function isSafeCommand( ): SafeCommandReason | null { const [cmd0, cmd1, cmd2, cmd3] = command; - const config = loadConfig(); - if (config.safeCommands && Array.isArray(config.safeCommands)) { - for (const safe of config.safeCommands) { - // safe: "npm test" → ["npm", "test"] - const safeArr = typeof safe === "string" ? safe.trim().split(/\s+/) : []; - if ( - safeArr.length > 0 && - safeArr.length <= command.length && - safeArr.every((v, i) => v === command[i]) - ) { - return { - reason: "User-defined safe command", - group: "User config", - }; - } - } - } - switch (cmd0) { case "cd": return { diff --git a/codex-cli/src/utils/config.ts b/codex-cli/src/utils/config.ts index a4a9c0cb0e..190d111762 100644 --- a/codex-cli/src/utils/config.ts +++ b/codex-cli/src/utils/config.ts @@ -78,8 +78,6 @@ export type StoredConfig = { saveHistory?: boolean; sensitivePatterns?: Array; }; - /** User-defined safe commands */ - safeCommands?: Array; }; // Minimal config written on first run. An *empty* model string ensures that @@ -113,8 +111,6 @@ export type AppConfig = { saveHistory: boolean; sensitivePatterns: Array; }; - /** User-defined safe commands */ - safeCommands?: Array; }; // --------------------------------------------------------------------------- @@ -297,7 +293,6 @@ export const loadConfig = ( instructions: combinedInstructions, notify: storedConfig.notify === true, approvalMode: storedConfig.approvalMode, - safeCommands: storedConfig.safeCommands ?? [], }; // ----------------------------------------------------------------------- @@ -375,13 +370,6 @@ export const loadConfig = ( }; } - // Load user-defined safe commands - if (Array.isArray(storedConfig.safeCommands)) { - config.safeCommands = storedConfig.safeCommands.map(String); - } else { - config.safeCommands = []; - } - return config; }; @@ -425,10 +413,6 @@ export const saveConfig = ( sensitivePatterns: config.history.sensitivePatterns, }; } - // Save: User-defined safe commands - if (config.safeCommands && config.safeCommands.length > 0) { - configToSave.safeCommands = config.safeCommands; - } if (ext === ".yaml" || ext === ".yml") { writeFileSync(targetPath, dumpYaml(configToSave), "utf-8"); diff --git a/codex-cli/tests/approvals.test.ts b/codex-cli/tests/approvals.test.ts index 43490cf84e..a39adff460 100644 --- a/codex-cli/tests/approvals.test.ts +++ b/codex-cli/tests/approvals.test.ts @@ -1,13 +1,7 @@ import type { SafetyAssessment } from "../src/approvals"; import { canAutoApprove } from "../src/approvals"; -import { describe, test, expect, vi } from "vitest"; - -vi.mock("../src/utils/config", () => ({ - loadConfig: () => ({ - safeCommands: ["npm test", "sl"], - }), -})); +import { describe, test, expect } from "vitest"; describe("canAutoApprove()", () => { const env = { @@ -95,27 +89,4 @@ describe("canAutoApprove()", () => { expect(check(["cargo", "build"])).toEqual({ type: "ask-user" }); }); - - test("commands in safeCommands config should be safe", async () => { - expect(check(["npm", "test"])).toEqual({ - type: "auto-approve", - reason: "User-defined safe command", - group: "User config", - runInSandbox: false, - }); - - expect(check(["sl"])).toEqual({ - type: "auto-approve", - reason: "User-defined safe command", - group: "User config", - runInSandbox: false, - }); - - expect(check(["npm", "test", "--watch"])).toEqual({ - type: "auto-approve", - reason: "User-defined safe command", - group: "User config", - runInSandbox: false, - }); - }); }); From 7346f4388ed90506b6093a6f6536f4789a9ae157 Mon Sep 17 00:00:00 2001 From: Jon Church Date: Mon, 21 Apr 2025 12:56:00 -0400 Subject: [PATCH 51/82] chore: drop src from publish (#474) Publish shouldn't need the source files published along with the distributable bin. `src` is being shipped to the registry rn: https://www.npmjs.com/package/@openai/codex?activeTab=code You can verify that the src is not needed by packing the project manually after removing src from the files: ```sh # from the codex-cli dir rm -rf dist # just for hygiene pnpm run build pnpm pack mkdir /tmp/codex-tar-test mv openai-codex-0.1.2504181820.tgz /tmp/codex-tar-test cd /tmp/codex-tar-test pnpm init pnpm add ./openai-codex-0.1.2504181820.tgz /tmp/codex-tar-test pnpm exec codex --full-auto "run a bash -c command to echo hello world" ``` The cli is operational > noticed this when checking the screenshot included in https://github.com/openai/codex/pull/461 --- codex-cli/package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/codex-cli/package.json b/codex-cli/package.json index 2bfe93ce70..fc445342f1 100644 --- a/codex-cli/package.json +++ b/codex-cli/package.json @@ -26,8 +26,7 @@ "release": "pnpm run release:readme && pnpm run release:version && pnpm install && pnpm run release:build-and-publish" }, "files": [ - "dist", - "src" + "dist" ], "dependencies": { "@inkjs/ui": "^2.0.0", From 12ec57b330183480f27d43ef12f73f588298628b Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Mon, 21 Apr 2025 10:29:58 -0700 Subject: [PATCH 52/82] do not auto-approve the find command if it contains options that write files or spawn commands (#482) Updates `isSafeCommand()` so that an invocation of `find` is not auto-approved if it contains any of: `-exec`, `-execdir`, `-ok`, `-okdir`, `-delete`, `-fls`, `-fprint`, `-fprint0`, `-fprintf`. --- codex-cli/src/approvals.ts | 34 +++++++++++++++++--- codex-cli/tests/approvals.test.ts | 52 +++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/codex-cli/src/approvals.ts b/codex-cli/src/approvals.ts index f4a35402f9..ff37a8903f 100644 --- a/codex-cli/src/approvals.ts +++ b/codex-cli/src/approvals.ts @@ -329,11 +329,20 @@ export function isSafeCommand( reason: "Ripgrep search", group: "Searching", }; - case "find": - return { - reason: "Find files or directories", - group: "Searching", - }; + case "find": { + // Certain options to `find` allow executing arbitrary processes, so we + // cannot auto-approve them. + if ( + command.some((arg: string) => UNSAFE_OPTIONS_FOR_FIND_COMMAND.has(arg)) + ) { + break; + } else { + return { + reason: "Find files or directories", + group: "Searching", + }; + } + } case "grep": return { reason: "Text search (grep)", @@ -421,6 +430,21 @@ function isValidSedNArg(arg: string | undefined): boolean { return arg != null && /^(\d+,)?\d+p$/.test(arg); } +const UNSAFE_OPTIONS_FOR_FIND_COMMAND: ReadonlySet = new Set([ + // Options that can execute arbitrary commands. + "-exec", + "-execdir", + "-ok", + "-okdir", + // Option that deletes matching files. + "-delete", + // Options that write pathnames to a file. + "-fls", + "-fprint", + "-fprint0", + "-fprintf", +]); + // ---------------- Helper utilities for complex shell expressions ----------------- // A conservative allow-list of bash operators that do not, on their own, cause diff --git a/codex-cli/tests/approvals.test.ts b/codex-cli/tests/approvals.test.ts index a39adff460..a90abad6eb 100644 --- a/codex-cli/tests/approvals.test.ts +++ b/codex-cli/tests/approvals.test.ts @@ -89,4 +89,56 @@ describe("canAutoApprove()", () => { expect(check(["cargo", "build"])).toEqual({ type: "ask-user" }); }); + + test("find", () => { + expect(check(["find", ".", "-name", "file.txt"])).toEqual({ + type: "auto-approve", + reason: "Find files or directories", + group: "Searching", + runInSandbox: false, + }); + + // Options that can execute arbitrary commands. + expect( + check(["find", ".", "-name", "file.txt", "-exec", "rm", "{}", ";"]), + ).toEqual({ + type: "ask-user", + }); + expect( + check(["find", ".", "-name", "*.py", "-execdir", "python3", "{}", ";"]), + ).toEqual({ + type: "ask-user", + }); + expect( + check(["find", ".", "-name", "file.txt", "-ok", "rm", "{}", ";"]), + ).toEqual({ + type: "ask-user", + }); + expect( + check(["find", ".", "-name", "*.py", "-okdir", "python3", "{}", ";"]), + ).toEqual({ + type: "ask-user", + }); + + // Option that deletes matching files. + expect(check(["find", ".", "-delete", "-name", "file.txt"])).toEqual({ + type: "ask-user", + }); + + // Options that write pathnames to a file. + expect(check(["find", ".", "-fls", "/etc/passwd"])).toEqual({ + type: "ask-user", + }); + expect(check(["find", ".", "-fprint", "/etc/passwd"])).toEqual({ + type: "ask-user", + }); + expect(check(["find", ".", "-fprint0", "/etc/passwd"])).toEqual({ + type: "ask-user", + }); + expect( + check(["find", ".", "-fprintf", "/root/suid.txt", "%#m %u %p\n"]), + ).toEqual({ + type: "ask-user", + }); + }); }); From f72cfd7ef337fcdf382079e41c4624813264e1ee Mon Sep 17 00:00:00 2001 From: Benny Yen Date: Tue, 22 Apr 2025 02:45:49 +0800 Subject: [PATCH 53/82] fix: correct fish completion function name in CLI script (#485) Missing an underscore. fish function: https://github.com/fish-shell/fish-shell/blob/master/share/functions/__fish_complete_path.fish fixes https://github.com/openai/codex/issues/469 --- codex-cli/src/cli.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codex-cli/src/cli.tsx b/codex-cli/src/cli.tsx index 2ec883d448..d716a07b01 100644 --- a/codex-cli/src/cli.tsx +++ b/codex-cli/src/cli.tsx @@ -190,7 +190,7 @@ _codex() { } _codex`, fish: `# fish completion for codex -complete -c codex -a '(_fish_complete_path)' -d 'file path'`, +complete -c codex -a '(__fish_complete_path)' -d 'file path'`, }; const script = scripts[shell]; if (!script) { From 09f0ae3899910995ac9659504739e16363d7176c Mon Sep 17 00:00:00 2001 From: Mitchell Kutchuk Date: Mon, 21 Apr 2025 15:01:09 -0400 Subject: [PATCH 54/82] fix: unintended tear down of agent loop (#483) fixes #465 --- codex-cli/src/components/chat/terminal-chat.tsx | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/codex-cli/src/components/chat/terminal-chat.tsx b/codex-cli/src/components/chat/terminal-chat.tsx index 8558208005..4859513a4f 100644 --- a/codex-cli/src/components/chat/terminal-chat.tsx +++ b/codex-cli/src/components/chat/terminal-chat.tsx @@ -305,15 +305,10 @@ export default function TerminalChat({ agentRef.current = undefined; forceUpdate(); // re‑render after teardown too }; - }, [ - model, - provider, - config, - approvalPolicy, - confirmationPrompt, - requestConfirmation, - additionalWritableRoots, - ]); + // We intentionally omit 'approvalPolicy' and 'confirmationPrompt' from the deps + // so switching modes or showing confirmation dialogs doesn’t tear down the loop. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [model, provider, config, requestConfirmation, additionalWritableRoots]); // Whenever loading starts/stops, reset or start a timer — but pause the // timer while a confirmation overlay is displayed so we don't trigger a From e7a3eec942032908dc3ebdf2f9ddf29670f0743f Mon Sep 17 00:00:00 2001 From: Daniel Nakov Date: Mon, 21 Apr 2025 17:53:09 -0400 Subject: [PATCH 55/82] docs: Add note about non-openai providers; add --provider cli flag to the help (#484) --- README.md | 19 +++++++++++++++++++ codex-cli/src/cli.tsx | 1 + 2 files changed, 20 insertions(+) diff --git a/README.md b/README.md index e5690d7d48..1abae778cf 100644 --- a/README.md +++ b/README.md @@ -78,6 +78,25 @@ export OPENAI_API_KEY="your-api-key-here" > > The CLI will automatically load variables from `.env` (via `dotenv/config`). +> **Note:** Codex also allows you to use other providers that support the OpenAI Chat Completions API. You can set the provider in the config file or use the `--provider` flag. +> +> The possible options for `--provider` are: +> +> - openai (default) +> - openrouter +> - gemini +> - ollama +> - mistral +> - deepseek +> - xai +> - groq +> +> If you use a provider other than OpenAI, you will need to set the API key for the provider in the config file or in the environment variable as: +> +> ```shell +> export _API_KEY="your-api-key-here" +> ``` + Run interactively: ```shell diff --git a/codex-cli/src/cli.tsx b/codex-cli/src/cli.tsx index d716a07b01..65147b9e5b 100644 --- a/codex-cli/src/cli.tsx +++ b/codex-cli/src/cli.tsx @@ -54,6 +54,7 @@ const cli = meow( Options -h, --help Show usage and exit -m, --model Model to use for completions (default: o4-mini) + -p, --provider Provider to use for completions (default: openai) -i, --image Path(s) to image files to include as input -v, --view Inspect a previously saved rollout instead of starting a session -q, --quiet Non-interactive mode that only prints the assistant's final output From 0e9d75657bf6e28130c64f683cbe71fc9175bac6 Mon Sep 17 00:00:00 2001 From: Fouad Matin <169186268+fouad-openai@users.noreply.github.com> Date: Mon, 21 Apr 2025 15:06:03 -0700 Subject: [PATCH 56/82] update: readme (#491) --- README.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1abae778cf..c9fcc93c80 100644 --- a/README.md +++ b/README.md @@ -68,9 +68,7 @@ Next, set your OpenAI API key as an environment variable: export OPENAI_API_KEY="your-api-key-here" ``` -> **Note:** This command sets the key only for your current terminal session. To make it permanent, add the `export` line to your shell's configuration file (e.g., `~/.zshrc`). -> -> **Tip:** You can also place your API key into a `.env` file at the root of your project: +> **Note:** This command sets the key only for your current terminal session. You can add the `export` line to your shell's configuration file (e.g., `~/.zshrc`) but we recommend setting for the session. **Tip:** You can also place your API key into a `.env` file at the root of your project: > > ```env > OPENAI_API_KEY=your-api-key-here @@ -78,9 +76,10 @@ export OPENAI_API_KEY="your-api-key-here" > > The CLI will automatically load variables from `.env` (via `dotenv/config`). -> **Note:** Codex also allows you to use other providers that support the OpenAI Chat Completions API. You can set the provider in the config file or use the `--provider` flag. -> -> The possible options for `--provider` are: +
+Use --provider to use other models + +> Codex also allows you to use other providers that support the OpenAI Chat Completions API. You can set the provider in the config file or use the `--provider` flag. The possible options for `--provider` are: > > - openai (default) > - openrouter @@ -97,6 +96,9 @@ export OPENAI_API_KEY="your-api-key-here" > export _API_KEY="your-api-key-here" > ``` +
+
+ Run interactively: ```shell @@ -386,7 +388,7 @@ OpenAI rejected the request. Error details: Status: 400, Code: unsupported_param **What can I do?** - If you are part of a ZDR organization, Codex CLI will not work until support is added. -- We are tracking this limitation and will update the documentation if support becomes available. +- We are tracking this limitation and will update the documentation once support becomes available. --- From 99ed27ad1b4b270db0b8d804d151e3e6adac127d Mon Sep 17 00:00:00 2001 From: Fouad Matin <169186268+fouad-openai@users.noreply.github.com> Date: Mon, 21 Apr 2025 15:13:33 -0700 Subject: [PATCH 57/82] bump(version): 0.1.2504211509 (#493) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## `0.1.2504211509` ### 🚀 Features - Support multiple providers via Responses-Completion transformation (#247) - Add user-defined safe commands configuration and approval logic #380 (#386) - Allow switching approval modes when prompted to approve an edit/command (#400) - Add support for `/diff` command autocomplete in TerminalChatInput (#431) - Auto-open model selector if user selects deprecated model (#427) - Read approvalMode from config file (#298) - `/diff` command to view git diff (#426) - Tab completions for file paths (#279) - Add /command autocomplete (#317) - Allow multi-line input (#438) ### 🐛 Bug Fixes - `full-auto` support in quiet mode (#374) - Enable shell option for child process execution (#391) - Configure husky and lint-staged for pnpm monorepo (#384) - Command pipe execution by improving shell detection (#437) - Name of the file not matching the name of the component (#354) - Allow proper exit from new Switch approval mode dialog (#453) - Ensure /clear resets context and exclude system messages from approximateTokenUsed count (#443) - `/clear` now clears terminal screen and resets context left indicator (#425) - Correct fish completion function name in CLI script (#485) - Auto-open model-selector when model is not found (#448) - Remove unnecessary isLoggingEnabled() checks (#420) - Improve test reliability for `raw-exec` (#434) - Unintended tear down of agent loop (#483) - Remove extraneous type casts (#462) --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++++++ codex-cli/package.json | 2 +- codex-cli/src/utils/session.ts | 2 +- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 95c916b821..ec32ba3f56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,38 @@ You can install any of these versions: `npm install -g codex@version` +## `0.1.2504211509` + +### 🚀 Features + +- Support multiple providers via Responses-Completion transformation (#247) +- Add user-defined safe commands configuration and approval logic #380 (#386) +- Allow switching approval modes when prompted to approve an edit/command (#400) +- Add support for `/diff` command autocomplete in TerminalChatInput (#431) +- Auto-open model selector if user selects deprecated model (#427) +- Read approvalMode from config file (#298) +- `/diff` command to view git diff (#426) +- Tab completions for file paths (#279) +- Add /command autocomplete (#317) +- Allow multi-line input (#438) + +### 🐛 Bug Fixes + +- `full-auto` support in quiet mode (#374) +- Enable shell option for child process execution (#391) +- Configure husky and lint-staged for pnpm monorepo (#384) +- Command pipe execution by improving shell detection (#437) +- Name of the file not matching the name of the component (#354) +- Allow proper exit from new Switch approval mode dialog (#453) +- Ensure /clear resets context and exclude system messages from approximateTokenUsed count (#443) +- `/clear` now clears terminal screen and resets context left indicator (#425) +- Correct fish completion function name in CLI script (#485) +- Auto-open model-selector when model is not found (#448) +- Remove unnecessary isLoggingEnabled() checks (#420) +- Improve test reliability for `raw-exec` (#434) +- Unintended tear down of agent loop (#483) +- Remove extraneous type casts (#462) + ## `0.1.2504181820` ### 🚀 Features diff --git a/codex-cli/package.json b/codex-cli/package.json index fc445342f1..0e70895f38 100644 --- a/codex-cli/package.json +++ b/codex-cli/package.json @@ -1,6 +1,6 @@ { "name": "@openai/codex", - "version": "0.1.2504181820", + "version": "0.1.2504211509", "license": "Apache-2.0", "bin": { "codex": "bin/codex.js" diff --git a/codex-cli/src/utils/session.ts b/codex-cli/src/utils/session.ts index 06f404f155..923143d72a 100644 --- a/codex-cli/src/utils/session.ts +++ b/codex-cli/src/utils/session.ts @@ -1,4 +1,4 @@ -export const CLI_VERSION = "0.1.2504181820"; // Must be in sync with package.json. +export const CLI_VERSION = "0.1.2504211509"; // Must be in sync with package.json. export const ORIGIN = "codex_cli_ts"; export type TerminalChatSession = { From 3eba86a55353f6b6f0efee19704b8d11e683b4ad Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Mon, 21 Apr 2025 19:06:03 -0700 Subject: [PATCH 58/82] include fractional portion of chunk that exceeds stdout/stderr limit (#497) I saw cases where the first chunk of output from `ls -R` could be large enough to exceed `MAX_OUTPUT_BYTES` or `MAX_OUTPUT_LINES`, in which case the loop would exit early in `createTruncatingCollector()` such that nothing was appended to the `chunks` array. As a result, the reported `stdout` of `ls -R` would be empty. I asked Codex to add logic to handle this edge case and write a unit test. I used this as my test: ``` ./codex-cli/dist/cli.js -q 'what is the output of `ls -R`' ``` now it appears to include a ton of stuff whereas before this change, I saw: ``` {"type":"function_call_output","call_id":"call_a2QhVt7HRJYKjb3dIc8w1aBB","output":"{\"output\":\"\\n\\n[Output truncated: too many lines or bytes]\",\"metadata\":{\"exit_code\":0,\"duration_seconds\":0.5}}"} ``` --- .../sandbox/create-truncating-collector.ts | 78 +++++++++++++++++++ codex-cli/src/utils/agent/sandbox/raw-exec.ts | 49 +----------- .../tests/create-truncating-collector.test.ts | 55 +++++++++++++ 3 files changed, 134 insertions(+), 48 deletions(-) create mode 100644 codex-cli/src/utils/agent/sandbox/create-truncating-collector.ts create mode 100644 codex-cli/tests/create-truncating-collector.test.ts diff --git a/codex-cli/src/utils/agent/sandbox/create-truncating-collector.ts b/codex-cli/src/utils/agent/sandbox/create-truncating-collector.ts new file mode 100644 index 0000000000..518d475c78 --- /dev/null +++ b/codex-cli/src/utils/agent/sandbox/create-truncating-collector.ts @@ -0,0 +1,78 @@ +// Maximum output cap: either MAX_OUTPUT_LINES lines or MAX_OUTPUT_BYTES bytes, +// whichever limit is reached first. +const MAX_OUTPUT_BYTES = 1024 * 10; // 10 KB +const MAX_OUTPUT_LINES = 256; + +/** + * Creates a collector that accumulates data Buffers from a stream up to + * specified byte and line limits. After either limit is exceeded, further + * data is ignored. + */ +export function createTruncatingCollector( + stream: NodeJS.ReadableStream, + byteLimit: number = MAX_OUTPUT_BYTES, + lineLimit: number = MAX_OUTPUT_LINES, +): { + getString: () => string; + hit: boolean; +} { + const chunks: Array = []; + let totalBytes = 0; + let totalLines = 0; + let hitLimit = false; + + stream?.on("data", (data: Buffer) => { + if (hitLimit) { + return; + } + const dataLength = data.length; + let newlineCount = 0; + for (let i = 0; i < dataLength; i++) { + if (data[i] === 0x0a) { + newlineCount++; + } + } + // If entire chunk fits within byte and line limits, take it whole + if ( + totalBytes + dataLength <= byteLimit && + totalLines + newlineCount <= lineLimit + ) { + chunks.push(data); + totalBytes += dataLength; + totalLines += newlineCount; + } else { + // Otherwise, take a partial slice up to the first limit breach + const allowedBytes = byteLimit - totalBytes; + const allowedLines = lineLimit - totalLines; + let bytesTaken = 0; + let linesSeen = 0; + for (let i = 0; i < dataLength; i++) { + // Stop if byte or line limit is reached + if (bytesTaken === allowedBytes || linesSeen === allowedLines) { + break; + } + const byte = data[i]; + if (byte === 0x0a) { + linesSeen++; + } + bytesTaken++; + } + if (bytesTaken > 0) { + chunks.push(data.slice(0, bytesTaken)); + totalBytes += bytesTaken; + totalLines += linesSeen; + } + hitLimit = true; + } + }); + + return { + getString() { + return Buffer.concat(chunks).toString("utf8"); + }, + /** True if either byte or line limit was exceeded */ + get hit(): boolean { + return hitLimit; + }, + }; +} diff --git a/codex-cli/src/utils/agent/sandbox/raw-exec.ts b/codex-cli/src/utils/agent/sandbox/raw-exec.ts index b3d1d8ec25..b33feb8518 100644 --- a/codex-cli/src/utils/agent/sandbox/raw-exec.ts +++ b/codex-cli/src/utils/agent/sandbox/raw-exec.ts @@ -9,14 +9,10 @@ import type { import { log } from "../../logger/log.js"; import { adaptCommandForPlatform } from "../platform-commands.js"; +import { createTruncatingCollector } from "./create-truncating-collector"; import { spawn } from "child_process"; import * as os from "os"; -// Maximum output cap: either MAX_OUTPUT_LINES lines or MAX_OUTPUT_BYTES bytes, -// whichever limit is reached first. -const MAX_OUTPUT_BYTES = 1024 * 10; // 10 KB -const MAX_OUTPUT_LINES = 256; - /** * This function should never return a rejected promise: errors should be * mapped to a non-zero exit code and the error message should be in stderr. @@ -204,49 +200,6 @@ export function exec( }); } -/** - * Creates a collector that accumulates data Buffers from a stream up to - * specified byte and line limits. After either limit is exceeded, further - * data is ignored. - */ -function createTruncatingCollector( - stream: NodeJS.ReadableStream, - byteLimit: number = MAX_OUTPUT_BYTES, - lineLimit: number = MAX_OUTPUT_LINES, -) { - const chunks: Array = []; - let totalBytes = 0; - let totalLines = 0; - let hitLimit = false; - - stream?.on("data", (data: Buffer) => { - if (hitLimit) { - return; - } - totalBytes += data.length; - for (let i = 0; i < data.length; i++) { - if (data[i] === 0x0a) { - totalLines++; - } - } - if (totalBytes <= byteLimit && totalLines <= lineLimit) { - chunks.push(data); - } else { - hitLimit = true; - } - }); - - return { - getString() { - return Buffer.concat(chunks).toString("utf8"); - }, - /** True if either byte or line limit was exceeded */ - get hit(): boolean { - return hitLimit; - }, - }; -} - /** * Adds a truncation warnings to stdout and stderr, if appropriate. */ diff --git a/codex-cli/tests/create-truncating-collector.test.ts b/codex-cli/tests/create-truncating-collector.test.ts new file mode 100644 index 0000000000..ad3dee558f --- /dev/null +++ b/codex-cli/tests/create-truncating-collector.test.ts @@ -0,0 +1,55 @@ +import { PassThrough } from "stream"; +import { once } from "events"; +import { describe, it, expect } from "vitest"; +import { createTruncatingCollector } from "../src/utils/agent/sandbox/create-truncating-collector.js"; + +describe("createTruncatingCollector", () => { + it("collects data under limits without truncation", async () => { + const stream = new PassThrough(); + const collector = createTruncatingCollector(stream, 100, 10); + const data = "line1\nline2\n"; + stream.end(Buffer.from(data)); + await once(stream, "end"); + expect(collector.getString()).toBe(data); + expect(collector.hit).toBe(false); + }); + + it("truncates data over byte limit", async () => { + const stream = new PassThrough(); + const collector = createTruncatingCollector(stream, 5, 100); + stream.end(Buffer.from("hello world")); + await once(stream, "end"); + expect(collector.getString()).toBe("hello"); + expect(collector.hit).toBe(true); + }); + + it("truncates data over line limit", async () => { + const stream = new PassThrough(); + const collector = createTruncatingCollector(stream, 1000, 2); + const data = "a\nb\nc\nd\n"; + stream.end(Buffer.from(data)); + await once(stream, "end"); + expect(collector.getString()).toBe("a\nb\n"); + expect(collector.hit).toBe(true); + }); + + it("stops collecting after limit is hit across multiple writes", async () => { + const stream = new PassThrough(); + const collector = createTruncatingCollector(stream, 10, 2); + stream.write(Buffer.from("1\n")); + stream.write(Buffer.from("2\n3\n4\n")); + stream.end(); + await once(stream, "end"); + expect(collector.getString()).toBe("1\n2\n"); + expect(collector.hit).toBe(true); + }); + + it("handles zero limits", async () => { + const stream = new PassThrough(); + const collector = createTruncatingCollector(stream, 0, 0); + stream.end(Buffer.from("anything\n")); + await once(stream, "end"); + expect(collector.getString()).toBe(""); + expect(collector.hit).toBe(true); + }); +}); From 9f5ccbb618d6d14b16870fca0a629ac2492947b1 Mon Sep 17 00:00:00 2001 From: Fouad Matin <169186268+fouad-openai@users.noreply.github.com> Date: Tue, 22 Apr 2025 01:30:16 -0700 Subject: [PATCH 59/82] feat: add support for ZDR orgs (#481) - Add `store: boolean` to `AgentLoop` to enable client-side storage of response items - Add `--disable-response-storage` arg + `disableResponseStorage` config --- codex-cli/src/cli.tsx | 14 +++ .../src/components/chat/terminal-chat.tsx | 1 + codex-cli/src/shims-external.d.ts | 24 ++++ codex-cli/src/utils/agent/agent-loop.ts | 113 +++++++++++++++++- codex-cli/src/utils/config.ts | 6 + 5 files changed, 153 insertions(+), 5 deletions(-) create mode 100644 codex-cli/src/shims-external.d.ts diff --git a/codex-cli/src/cli.tsx b/codex-cli/src/cli.tsx index 65147b9e5b..0bed07bf52 100644 --- a/codex-cli/src/cli.tsx +++ b/codex-cli/src/cli.tsx @@ -70,6 +70,9 @@ const cli = meow( --full-stdout Do not truncate stdout/stderr from command outputs --notify Enable desktop notifications for responses + --disable-response-storage Disable server‑side response storage (sends the + full conversation context with every request) + --flex-mode Use "flex-mode" processing mode for the request (only supported with models o3 and o4-mini) @@ -160,6 +163,12 @@ const cli = meow( description: "Enable desktop notifications for responses", }, + disableResponseStorage: { + type: "boolean", + description: + "Disable server-side response storage (sends full conversation context with every request)", + }, + // Experimental mode where whole directory is loaded in context and model is requested // to make code edits in a single pass. fullContext: { @@ -262,6 +271,10 @@ config = { notify: Boolean(cli.flags.notify), flexMode: Boolean(cli.flags.flexMode), provider, + disableResponseStorage: + cli.flags.disableResponseStorage !== undefined + ? Boolean(cli.flags.disableResponseStorage) + : config.disableResponseStorage, }; // Check for updates after loading config. This is important because we write state file in @@ -463,6 +476,7 @@ async function runQuietMode({ instructions: config.instructions, approvalPolicy, additionalWritableRoots, + disableResponseStorage: config.disableResponseStorage, onItem: (item: ResponseItem) => { // eslint-disable-next-line no-console console.log(formatResponseItemForQuietMode(item)); diff --git a/codex-cli/src/components/chat/terminal-chat.tsx b/codex-cli/src/components/chat/terminal-chat.tsx index 4859513a4f..9092932a6c 100644 --- a/codex-cli/src/components/chat/terminal-chat.tsx +++ b/codex-cli/src/components/chat/terminal-chat.tsx @@ -241,6 +241,7 @@ export default function TerminalChat({ config, instructions: config.instructions, approvalPolicy, + disableResponseStorage: config.disableResponseStorage, additionalWritableRoots, onLastResponseId: setLastResponseId, onItem: (item) => { diff --git a/codex-cli/src/shims-external.d.ts b/codex-cli/src/shims-external.d.ts new file mode 100644 index 0000000000..95530e6110 --- /dev/null +++ b/codex-cli/src/shims-external.d.ts @@ -0,0 +1,24 @@ +// Ambient module declarations for optional/runtime‑only dependencies so that +// `tsc --noEmit` succeeds without installing their full type definitions. + +declare module "package-manager-detector" { + export type AgentName = "npm" | "pnpm" | "yarn" | "bun" | "deno"; + + /** Detects the package manager based on environment variables. */ + export function getUserAgent(): AgentName | null | undefined; +} + +declare module "fast-npm-meta" { + export interface LatestVersionMeta { + version: string; + } + + export function getLatestVersion( + pkgName: string, + opts?: Record, + ): Promise; +} + +declare module "semver" { + export function gt(v1: string, v2: string): boolean; +} diff --git a/codex-cli/src/utils/agent/agent-loop.ts b/codex-cli/src/utils/agent/agent-loop.ts index 9cf5d30ff7..5cde3b164a 100644 --- a/codex-cli/src/utils/agent/agent-loop.ts +++ b/codex-cli/src/utils/agent/agent-loop.ts @@ -46,6 +46,14 @@ type AgentLoopParams = { config?: AppConfig; instructions?: string; approvalPolicy: ApprovalPolicy; + /** + * Whether the model responses should be stored on the server side (allows + * using `previous_response_id` to provide conversational context). Defaults + * to `true` to preserve the current behaviour. When set to `false` the agent + * will instead send the *full* conversation context as the `input` payload + * on every request and omit the `previous_response_id` parameter. + */ + disableResponseStorage?: boolean; onItem: (item: ResponseItem) => void; onLoading: (loading: boolean) => void; @@ -67,6 +75,8 @@ export class AgentLoop { private approvalPolicy: ApprovalPolicy; private config: AppConfig; private additionalWritableRoots: ReadonlyArray; + /** Whether we ask the API to persist conversation state on the server */ + private readonly disableResponseStorage: boolean; // Using `InstanceType` sidesteps typing issues with the OpenAI package under // the TS 5+ `moduleResolution=bundler` setup. OpenAI client instance. We keep the concrete @@ -97,6 +107,13 @@ export class AgentLoop { private execAbortController: AbortController | null = null; /** Set to true when `cancel()` is called so `run()` can exit early. */ private canceled = false; + + /** + * Local conversation transcript used when `disableResponseStorage === false`. Holds + * all non‑system items exchanged so far so we can provide full context on + * every request. + */ + private transcript: Array = []; /** Function calls that were emitted by the model but never answered because * the user cancelled the run. We keep the `call_id`s around so the *next* * request can send a dummy `function_call_output` that satisfies the @@ -206,6 +223,7 @@ export class AgentLoop { provider = "openai", instructions, approvalPolicy, + disableResponseStorage, // `config` used to be required. Some unit‑tests (and potentially other // callers) instantiate `AgentLoop` without passing it, so we make it // optional and fall back to sensible defaults. This keeps the public @@ -240,6 +258,8 @@ export class AgentLoop { this.onLoading = onLoading; this.getCommandConfirmation = getCommandConfirmation; this.onLastResponseId = onLastResponseId; + + this.disableResponseStorage = disableResponseStorage ?? false; this.sessionId = getSessionId() || randomUUID().replaceAll("-", ""); // Configure OpenAI client with optional timeout (ms) from environment const timeoutMs = OPENAI_TIMEOUT_MS; @@ -418,7 +438,13 @@ export class AgentLoop { // accumulate listeners which in turn triggered Node's // `MaxListenersExceededWarning` after ten invocations. - let lastResponseId: string = previousResponseId; + // Track the response ID from the last *stored* response so we can use + // `previous_response_id` when `disableResponseStorage` is enabled. When storage + // is disabled we deliberately ignore the caller‑supplied value because + // the backend will not retain any state that could be referenced. + let lastResponseId: string = this.disableResponseStorage + ? previousResponseId + : ""; // If there are unresolved function calls from a previously cancelled run // we have to emit dummy tool outputs so that the API no longer expects @@ -440,7 +466,48 @@ export class AgentLoop { this.pendingAborts.clear(); } - let turnInput = [...abortOutputs, ...input]; + // Build the input list for this turn. When responses are stored on the + // server we can simply send the *delta* (the new user input as well as + // any pending abort outputs) and rely on `previous_response_id` for + // context. When storage is disabled the server has no memory of the + // conversation, so we must include the *entire* transcript (minus system + // messages) on every call. + + let turnInput: Array; + + const stripInternalFields = ( + item: ResponseInputItem, + ): ResponseInputItem => { + // Clone shallowly and remove fields that are not part of the public + // schema expected by the OpenAI Responses API. + // We shallow‑clone the item so that subsequent mutations (deleting + // internal fields) do not affect the original object which may still + // be referenced elsewhere (e.g. UI components). + const clean = { ...item } as Record; + delete clean["duration_ms"]; + return clean as unknown as ResponseInputItem; + }; + + if (this.disableResponseStorage) { + // Ensure the transcript is up‑to‑date with the latest user input so + // that subsequent iterations see a complete history. + const newUserItems: Array = input.filter((it) => { + if ( + (it.type === "message" && it.role !== "system") || + it.type === "reasoning" + ) { + return false; + } + return true; + }); + this.transcript.push(...newUserItems); + + turnInput = [...this.transcript, ...abortOutputs].map( + stripInternalFields, + ); + } else { + turnInput = [...abortOutputs, ...input].map(stripInternalFields); + } this.onLoading(true); @@ -471,6 +538,33 @@ export class AgentLoop { this.onItem(item); // Mark as delivered so flush won't re-emit it staged[idx] = undefined; + + // When we operate without server‑side storage we keep our own + // transcript so we can provide full context on subsequent calls. + if (this.disableResponseStorage) { + // Exclude system messages from transcript as they do not form + // part of the assistant/user dialogue that the model needs. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const role = (item as any).role; + if (role !== "system") { + // Clone the item to avoid mutating the object that is also + // rendered in the UI. We need to strip auxiliary metadata + // such as `duration_ms` which is not part of the Responses + // API schema and therefore causes a 400 error when included + // in subsequent requests whose context is sent verbatim. + + const clone: ResponseInputItem = { + ...(item as unknown as ResponseInputItem), + } as ResponseInputItem; + // The `duration_ms` field is only added to reasoning items to + // show elapsed time in the UI. It must not be forwarded back + // to the server. + // eslint-disable-next-line @typescript-eslint/no-explicit-any + delete (clone as any).duration_ms; + + this.transcript.push(clone); + } + } } }, 10); }; @@ -481,7 +575,11 @@ export class AgentLoop { return; } // send request to openAI - for (const item of turnInput) { + // Only surface the *new* input items to the UI – replaying the entire + // transcript would duplicate messages that have already been shown in + // earlier turns. + const deltaInput = [...abortOutputs, ...input]; + for (const item of deltaInput) { stageItem(item as ResponseItem); } // Send request to OpenAI with retry on timeout @@ -520,18 +618,23 @@ export class AgentLoop { stream = await responseCall({ model: this.model, instructions: mergedInstructions, - previous_response_id: lastResponseId || undefined, input: turnInput, stream: true, parallel_tool_calls: false, reasoning, ...(this.config.flexMode ? { service_tier: "flex" } : {}), + ...(this.disableResponseStorage + ? { store: false } + : { + store: true, + previous_response_id: lastResponseId || undefined, + }), tools: [ { type: "function", name: "shell", description: "Runs a shell command, and returns its output.", - strict: false, + strict: true, parameters: { type: "object", properties: { diff --git a/codex-cli/src/utils/config.ts b/codex-cli/src/utils/config.ts index 190d111762..a8d73feb01 100644 --- a/codex-cli/src/utils/config.ts +++ b/codex-cli/src/utils/config.ts @@ -73,6 +73,8 @@ export type StoredConfig = { memory?: MemoryConfig; /** Whether to enable desktop notifications for responses */ notify?: boolean; + /** Disable server-side response storage (send full transcript each request) */ + disableResponseStorage?: boolean; history?: { maxSize?: number; saveHistory?: boolean; @@ -104,6 +106,9 @@ export type AppConfig = { /** Whether to enable desktop notifications for responses */ notify: boolean; + /** Disable server-side response storage (send full transcript each request) */ + disableResponseStorage?: boolean; + /** Enable the "flex-mode" processing mode for supported models (o3, o4-mini) */ flexMode?: boolean; history?: { @@ -293,6 +298,7 @@ export const loadConfig = ( instructions: combinedInstructions, notify: storedConfig.notify === true, approvalMode: storedConfig.approvalMode, + disableResponseStorage: storedConfig.disableResponseStorage ?? false, }; // ----------------------------------------------------------------------- From 2cb8355968f3f9ae7432aeb56dab08f89707705b Mon Sep 17 00:00:00 2001 From: Fouad Matin <169186268+fouad-openai@users.noreply.github.com> Date: Tue, 22 Apr 2025 01:45:30 -0700 Subject: [PATCH 60/82] bump(version): 0.1.2504220136 (#518) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## `0.1.2504220136` ### 🚀 Features - Add support for ZDR orgs (#481) - Include fractional portion of chunk that exceeds stdout/stderr limit (#497) --- CHANGELOG.md | 7 +++++++ cliff.toml | 2 +- codex-cli/package.json | 2 +- codex-cli/src/utils/session.ts | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec32ba3f56..f8cb037207 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ You can install any of these versions: `npm install -g codex@version` +## `0.1.2504220136` + +### 🚀 Features + +- Add support for ZDR orgs (#481) +- Include fractional portion of chunk that exceeds stdout/stderr limit (#497) + ## `0.1.2504211509` ### 🚀 Features diff --git a/cliff.toml b/cliff.toml index 856665b08e..b8e59ee443 100644 --- a/cliff.toml +++ b/cliff.toml @@ -38,7 +38,7 @@ commit_parsers = [ { message = "^fix", group = "🐛 Bug Fixes" }, { message = "^bump", group = "🛳️ Release" }, # Fallback – skip anything that didn't match the above rules. - { message = ".*", group = "💼 Other", skip = true }, + { message = ".*", group = "💼 Other" }, ] filter_unconventional = false diff --git a/codex-cli/package.json b/codex-cli/package.json index 0e70895f38..d438e2ab26 100644 --- a/codex-cli/package.json +++ b/codex-cli/package.json @@ -1,6 +1,6 @@ { "name": "@openai/codex", - "version": "0.1.2504211509", + "version": "0.1.2504220136", "license": "Apache-2.0", "bin": { "codex": "bin/codex.js" diff --git a/codex-cli/src/utils/session.ts b/codex-cli/src/utils/session.ts index 923143d72a..1697f7cec7 100644 --- a/codex-cli/src/utils/session.ts +++ b/codex-cli/src/utils/session.ts @@ -1,4 +1,4 @@ -export const CLI_VERSION = "0.1.2504211509"; // Must be in sync with package.json. +export const CLI_VERSION = "0.1.2504220136"; // Must be in sync with package.json. export const ORIGIN = "codex_cli_ts"; export type TerminalChatSession = { From c00ae2dcc158e6a80565182a737e95508fc17ed8 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Tue, 22 Apr 2025 07:07:40 -0700 Subject: [PATCH 61/82] Enforce ASCII in README.md (#513) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This all started because I was going to write a script to autogenerate the Table of Contents in the root `README.md`, but I noticed that the `href` for the "Why Codex?" heading was `#whycodex` instead of `#why-codex`. This piqued my curiosity and it turned out that the space in "Why Codex?" was not an ASCII space but **U+00A0**, a non-breaking space, and so GitHub ignored it when generating the `href` for the heading. This also meant that when I did a text search for `why codex` in the `README.md` in VS Code, the "Why Codex" heading did not match because of the presence of **U+00A0**. In short, these types of Unicode characters seem like a hazard, so I decided to introduce this script to flag them, and if desired, to replace them with "good enough" ASCII equivalents. For now, this only applies to the root `README.md` file, but I think we should ultimately apply this across our source code, as well, as we seem to have quite a lot of non-ASCII Unicode and it's probably going to cause `rg` to miss things. Contributions of this PR: * `./scripts/asciicheck.py`, which takes a list of filepaths and returns non-zero if any of them contain non-ASCII characters. (Currently, there is one exception for ✨ aka **U+2728**, though I would like to default to an empty allowlist and then require all exceptions to be specified as flags.) * A `--fix` option that will attempt to rewrite files with violations using a equivalents from a hardcoded substitution list. * An update to `ci.yml` to verify `./scripts/asciicheck.py README.md` succeeds. * A cleanup of `README.md` using the `--fix` option as well as some editorial decisions on my part. * I tried to update the `href`s in the Table of Contents to reflect the changes in the heading titles. (TIL that if a heading has a character like `&` surrounded by spaces, it becomes `--` in the generated `href`.) --- .github/workflows/ci.yml | 3 + README.md | 163 +++++++++++++++++++-------------------- scripts/asciicheck.py | 127 ++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+), 82 deletions(-) create mode 100755 scripts/asciicheck.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e99c2daa04..1f5dd0d31a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,3 +67,6 @@ jobs: - name: Build run: pnpm run build + + - name: Ensure README.md contains only ASCII and certain Unicode code points + run: ./scripts/asciicheck.py README.md diff --git a/README.md b/README.md index c9fcc93c80..9ac89499ac 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,13 @@ - [Experimental Technology Disclaimer](#experimental-technology-disclaimer) - [Quickstart](#quickstart) -- [Why Codex?](#whycodex) -- [Security Model \& Permissions](#securitymodelpermissions) +- [Why Codex?](#why-codex) +- [Security Model & Permissions](#security-model--permissions) - [Platform sandboxing details](#platform-sandboxing-details) -- [System Requirements](#systemrequirements) -- [CLI Reference](#clireference) -- [Memory \& Project Docs](#memoryprojectdocs) -- [Non‑interactive / CI mode](#noninteractivecimode) +- [System Requirements](#system-requirements) +- [CLI Reference](#cli-reference) +- [Memory & Project Docs](#memory--project-docs) +- [Non-interactive / CI mode](#non-interactive--ci-mode) - [Recipes](#recipes) - [Installation](#installation) - [Configuration](#configuration) @@ -27,7 +27,7 @@ - [Contributing](#contributing) - [Development workflow](#development-workflow) - [Nix Flake Development](#nix-flake-development) - - [Writing high‑impact code changes](#writing-highimpact-code-changes) + - [Writing high-impact code changes](#writing-high-impact-code-changes) - [Opening a pull request](#opening-a-pull-request) - [Review process](#review-process) - [Community values](#community-values) @@ -35,7 +35,7 @@ - [Contributor License Agreement (CLA)](#contributor-license-agreement-cla) - [Quick fixes](#quick-fixes) - [Releasing `codex`](#releasing-codex) -- [Security \& Responsible AI](#securityresponsibleai) +- [Security & Responsible AI](#security--responsible-ai) - [License](#license) - [Zero Data Retention (ZDR) Organization Limitation](#zero-data-retention-zdr-organization-limitation) @@ -45,7 +45,7 @@ ## Experimental Technology Disclaimer -Codex CLI is an experimental project under active development. It is not yet stable, may contain bugs, incomplete features, or undergo breaking changes. We’re building it in the open with the community and welcome: +Codex CLI is an experimental project under active development. It is not yet stable, may contain bugs, incomplete features, or undergo breaking changes. We're building it in the open with the community and welcome: - Bug reports - Feature requests @@ -115,59 +115,59 @@ codex "explain this codebase to me" codex --approval-mode full-auto "create the fanciest todo-list app" ``` -That’s it – Codex will scaffold a file, run it inside a sandbox, install any +That's it - Codex will scaffold a file, run it inside a sandbox, install any missing dependencies, and show you the live result. Approve the changes and -they’ll be committed to your working directory. +they'll be committed to your working directory. --- -## Why Codex? +## Why Codex? Codex CLI is built for developers who already **live in the terminal** and want -ChatGPT‑level reasoning **plus** the power to actually run code, manipulate -files, and iterate – all under version control. In short, it’s _chat‑driven +ChatGPT-level reasoning **plus** the power to actually run code, manipulate +files, and iterate - all under version control. In short, it's _chat-driven development_ that understands and executes your repo. -- **Zero setup** — bring your OpenAI API key and it just works! +- **Zero setup** - bring your OpenAI API key and it just works! - **Full auto-approval, while safe + secure** by running network-disabled and directory-sandboxed -- **Multimodal** — pass in screenshots or diagrams to implement features ✨ +- **Multimodal** - pass in screenshots or diagrams to implement features ✨ And it's **fully open-source** so you can see and contribute to how it develops! --- -## Security Model & Permissions +## Security Model & Permissions Codex lets you decide _how much autonomy_ the agent receives and auto-approval policy via the `--approval-mode` flag (or the interactive onboarding prompt): -| Mode | What the agent may do without asking | Still requires approval | -| ------------------------- | -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | -| **Suggest**
(default) | • Read any file in the repo | • **All** file writes/patches
• **Any** arbitrary shell commands (aside from reading files) | -| **Auto Edit** | • Read **and** apply‑patch writes to files | • **All** shell commands | -| **Full Auto** | • Read/write files
• Execute shell commands (network disabled, writes limited to your workdir) | – | +| Mode | What the agent may do without asking | Still requires approval | +| ------------------------- | --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| **Suggest**
(default) |
  • Read any file in the repo |
  • **All** file writes/patches
  • **Any** arbitrary shell commands (aside from reading files) | +| **Auto Edit** |
  • Read **and** apply-patch writes to files |
  • **All** shell commands | +| **Full Auto** |
  • Read/write files
  • Execute shell commands (network disabled, writes limited to your workdir) | - | -In **Full Auto** every command is run **network‑disabled** and confined to the -current working directory (plus temporary files) for defense‑in‑depth. Codex -will also show a warning/confirmation if you start in **auto‑edit** or -**full‑auto** while the directory is _not_ tracked by Git, so you always have a +In **Full Auto** every command is run **network-disabled** and confined to the +current working directory (plus temporary files) for defense-in-depth. Codex +will also show a warning/confirmation if you start in **auto-edit** or +**full-auto** while the directory is _not_ tracked by Git, so you always have a safety net. -Coming soon: you’ll be able to whitelist specific commands to auto‑execute with -the network enabled, once we’re confident in additional safeguards. +Coming soon: you'll be able to whitelist specific commands to auto-execute with +the network enabled, once we're confident in additional safeguards. ### Platform sandboxing details The hardening mechanism Codex uses depends on your OS: -- **macOS 12+** – commands are wrapped with **Apple Seatbelt** (`sandbox-exec`). +- **macOS 12+** - commands are wrapped with **Apple Seatbelt** (`sandbox-exec`). - - Everything is placed in a read‑only jail except for a small set of + - Everything is placed in a read-only jail except for a small set of writable roots (`$PWD`, `$TMPDIR`, `~/.codex`, etc.). - - Outbound network is _fully blocked_ by default – even if a child process + - Outbound network is _fully blocked_ by default - even if a child process tries to `curl` somewhere it will fail. -- **Linux** – there is no sandboxing by default. +- **Linux** - there is no sandboxing by default. We recommend using Docker for sandboxing, where Codex launches itself inside a **minimal container image** and mounts your repo _read/write_ at the same path. A custom `iptables`/`ipset` firewall script denies all egress except the @@ -176,47 +176,47 @@ The hardening mechanism Codex uses depends on your OS: --- -## System Requirements +## System Requirements | Requirement | Details | | --------------------------- | --------------------------------------------------------------- | -| Operating systems | macOS 12+, Ubuntu 20.04+/Debian 10+, or Windows 11 **via WSL2** | +| Operating systems | macOS 12+, Ubuntu 20.04+/Debian 10+, or Windows 11 **via WSL2** | | Node.js | **22 or newer** (LTS recommended) | -| Git (optional, recommended) | 2.23+ for built‑in PR helpers | -| RAM | 4‑GB minimum (8‑GB recommended) | +| Git (optional, recommended) | 2.23+ for built-in PR helpers | +| RAM | 4-GB minimum (8-GB recommended) | > Never run `sudo npm install -g`; fix npm permissions instead. --- -## CLI Reference +## CLI Reference | Command | Purpose | Example | | ------------------------------------ | ----------------------------------- | ------------------------------------ | | `codex` | Interactive REPL | `codex` | -| `codex "…"` | Initial prompt for interactive REPL | `codex "fix lint errors"` | -| `codex -q "…"` | Non‑interactive "quiet mode" | `codex -q --json "explain utils.ts"` | +| `codex "..."` | Initial prompt for interactive REPL | `codex "fix lint errors"` | +| `codex -q "..."` | Non-interactive "quiet mode" | `codex -q --json "explain utils.ts"` | | `codex completion ` | Print shell completion script | `codex completion bash` | Key flags: `--model/-m`, `--approval-mode/-a`, `--quiet/-q`, and `--notify`. --- -## Memory & Project Docs +## Memory & Project Docs Codex merges Markdown instructions in this order: -1. `~/.codex/instructions.md` – personal global guidance -2. `codex.md` at repo root – shared project notes -3. `codex.md` in cwd – sub‑package specifics +1. `~/.codex/instructions.md` - personal global guidance +2. `codex.md` at repo root - shared project notes +3. `codex.md` in cwd - sub-package specifics Disable with `--no-project-doc` or `CODEX_DISABLE_PROJECT_DOC=1`. --- -## Non‑interactive / CI mode +## Non-interactive / CI mode -Run Codex head‑less in pipelines. Example GitHub Action step: +Run Codex head-less in pipelines. Example GitHub Action step: ```yaml - name: Update changelog via Codex @@ -240,15 +240,15 @@ DEBUG=true codex ## Recipes -Below are a few bite‑size examples you can copy‑paste. Replace the text in quotes with your own task. See the [prompting guide](https://github.com/openai/codex/blob/main/codex-cli/examples/prompting_guide.md) for more tips and usage patterns. +Below are a few bite-size examples you can copy-paste. Replace the text in quotes with your own task. See the [prompting guide](https://github.com/openai/codex/blob/main/codex-cli/examples/prompting_guide.md) for more tips and usage patterns. | ✨ | What you type | What happens | | --- | ------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | -| 1 | `codex "Refactor the Dashboard component to React Hooks"` | Codex rewrites the class component, runs `npm test`, and shows the diff. | +| 1 | `codex "Refactor the Dashboard component to React Hooks"` | Codex rewrites the class component, runs `npm test`, and shows the diff. | | 2 | `codex "Generate SQL migrations for adding a users table"` | Infers your ORM, creates migration files, and runs them in a sandboxed DB. | | 3 | `codex "Write unit tests for utils/date.ts"` | Generates tests, executes them, and iterates until they pass. | -| 4 | `codex "Bulk‑rename *.jpeg → *.jpg with git mv"` | Safely renames files and updates imports/usages. | -| 5 | `codex "Explain what this regex does: ^(?=.*[A-Z]).{8,}$"` | Outputs a step‑by‑step human explanation. | +| 4 | `codex "Bulk-rename *.jpeg -> *.jpg with git mv"` | Safely renames files and updates imports/usages. | +| 5 | `codex "Explain what this regex does: ^(?=.*[A-Z]).{8,}$"` | Outputs a step-by-step human explanation. | | 6 | `codex "Carefully review this repo, and propose 3 high impact well-scoped PRs"` | Suggests impactful PRs in the current codebase. | | 7 | `codex "Look for vulnerabilities and create a security review report"` | Finds and explains security bugs. | @@ -257,7 +257,7 @@ Below are a few bite‑size examples you can copy‑paste. Replace the text in q ## Installation
    -From npm (Recommended) +From npm (Recommended) ```bash npm install -g @openai/codex @@ -272,7 +272,7 @@ pnpm add -g @openai/codex
    -Build from source +Build from source ```bash # Clone the repository and navigate to the CLI package @@ -289,7 +289,7 @@ pnpm build # Get the usage and the options node ./dist/cli.js --help -# Run the locally‑built CLI directly +# Run the locally-built CLI directly node ./dist/cli.js # Or link the command globally for convenience @@ -363,7 +363,7 @@ Codex runs model-generated commands in a sandbox. If a proposed command or file
    Does it work on Windows? -Not directly. It requires [Windows Subsystem for Linux (WSL2)](https://learn.microsoft.com/en-us/windows/wsl/install) – Codex has been tested on macOS and Linux with Node ≥ 22. +Not directly. It requires [Windows Subsystem for Linux (WSL2)](https://learn.microsoft.com/en-us/windows/wsl/install) - Codex has been tested on macOS and Linux with Node 22.
    @@ -394,12 +394,12 @@ OpenAI rejected the request. Error details: Status: 400, Code: unsupported_param ## Funding Opportunity -We’re excited to launch a **$1 million initiative** supporting open source projects that use Codex CLI and other OpenAI models. +We're excited to launch a **$1 million initiative** supporting open source projects that use Codex CLI and other OpenAI models. - Grants are awarded in **$25,000** API credit increments. - Applications are reviewed **on a rolling basis**. -**Interested? [Apply here](https://openai.com/form/codex-open-source-fund/).** +**Interested? [Apply here](https://openai.com/form/codex-open-source-fund/).** --- @@ -407,14 +407,14 @@ We’re excited to launch a **$1 million initiative** supporting open source pr This project is under active development and the code will likely change pretty significantly. We'll update this message once that's complete! -More broadly we welcome contributions – whether you are opening your very first pull request or you’re a seasoned maintainer. At the same time we care about reliability and long‑term maintainability, so the bar for merging code is intentionally **high**. The guidelines below spell out what “high‑quality” means in practice and should make the whole process transparent and friendly. +More broadly we welcome contributions - whether you are opening your very first pull request or you're a seasoned maintainer. At the same time we care about reliability and long-term maintainability, so the bar for merging code is intentionally **high**. The guidelines below spell out what "high-quality" means in practice and should make the whole process transparent and friendly. ### Development workflow -- Create a _topic branch_ from `main` – e.g. `feat/interactive-prompt`. +- Create a _topic branch_ from `main` - e.g. `feat/interactive-prompt`. - Keep your changes focused. Multiple unrelated fixes should be opened as separate PRs. -- Use `pnpm test:watch` during development for super‑fast feedback. -- We use **Vitest** for unit tests, **ESLint** + **Prettier** for style, and **TypeScript** for type‑checking. +- Use `pnpm test:watch` during development for super-fast feedback. +- We use **Vitest** for unit tests, **ESLint** + **Prettier** for style, and **TypeScript** for type-checking. - Before pushing, run the full test/type/lint suite: ### Git Hooks with Husky @@ -436,16 +436,16 @@ npm test && npm run lint && npm run typecheck I have read the CLA Document and I hereby sign the CLA ``` - The CLA‑Assistant bot will turn the PR status green once all authors have signed. + The CLA-Assistant bot will turn the PR status green once all authors have signed. ```bash -# Watch mode (tests rerun on change) +# Watch mode (tests rerun on change) pnpm test:watch -# Type‑check without emitting files +# Type-check without emitting files pnpm typecheck -# Automatically fix lint + prettier issues +# Automatically fix lint + prettier issues pnpm lint:fix pnpm format:fix ``` @@ -475,35 +475,35 @@ Run the CLI via the flake app: nix run .#codex ``` -### Writing high‑impact code changes +### Writing high-impact code changes 1. **Start with an issue.** Open a new one or comment on an existing discussion so we can agree on the solution before code is written. -2. **Add or update tests.** Every new feature or bug‑fix should come with test coverage that fails before your change and passes afterwards. 100 % coverage is not required, but aim for meaningful assertions. -3. **Document behaviour.** If your change affects user‑facing behaviour, update the README, inline help (`codex --help`), or relevant example projects. +2. **Add or update tests.** Every new feature or bug-fix should come with test coverage that fails before your change and passes afterwards. 100% coverage is not required, but aim for meaningful assertions. +3. **Document behaviour.** If your change affects user-facing behaviour, update the README, inline help (`codex --help`), or relevant example projects. 4. **Keep commits atomic.** Each commit should compile and the tests should pass. This makes reviews and potential rollbacks easier. ### Opening a pull request -- Fill in the PR template (or include similar information) – **What? Why? How?** +- Fill in the PR template (or include similar information) - **What? Why? How?** - Run **all** checks locally (`npm test && npm run lint && npm run typecheck`). CI failures that could have been caught locally slow down the process. -- Make sure your branch is up‑to‑date with `main` and that you have resolved merge conflicts. -- Mark the PR as **Ready for review** only when you believe it is in a merge‑able state. +- Make sure your branch is up-to-date with `main` and that you have resolved merge conflicts. +- Mark the PR as **Ready for review** only when you believe it is in a merge-able state. ### Review process 1. One maintainer will be assigned as a primary reviewer. -2. We may ask for changes – please do not take this personally. We value the work, we just also value consistency and long‑term maintainability. -3. When there is consensus that the PR meets the bar, a maintainer will squash‑and‑merge. +2. We may ask for changes - please do not take this personally. We value the work, we just also value consistency and long-term maintainability. +3. When there is consensus that the PR meets the bar, a maintainer will squash-and-merge. ### Community values - **Be kind and inclusive.** Treat others with respect; we follow the [Contributor Covenant](https://www.contributor-covenant.org/). -- **Assume good intent.** Written communication is hard – err on the side of generosity. +- **Assume good intent.** Written communication is hard - err on the side of generosity. - **Teach & learn.** If you spot something confusing, open an issue or PR with improvements. ### Getting help -If you run into problems setting up the project, would like feedback on an idea, or just want to say _hi_ – please open a Discussion or jump into the relevant issue. We are happy to help. +If you run into problems setting up the project, would like feedback on an idea, or just want to say _hi_ - please open a Discussion or jump into the relevant issue. We are happy to help. Together we can make Codex CLI an incredible tool. **Happy hacking!** :rocket: @@ -512,22 +512,21 @@ Together we can make Codex CLI an incredible tool. **Happy hacking!** :rocket: All contributors **must** accept the CLA. The process is lightweight: 1. Open your pull request. -2. Paste the following comment (or reply `recheck` if you’ve signed before): +2. Paste the following comment (or reply `recheck` if you've signed before): ```text I have read the CLA Document and I hereby sign the CLA ``` -3. The CLA‑Assistant bot records your signature in the repo and marks the status check as passed. +3. The CLA-Assistant bot records your signature in the repo and marks the status check as passed. No special Git commands, email attachments, or commit footers required. #### Quick fixes -| Scenario | Command | -| ----------------- | ----------------------------------------------------------------------------------------- | -| Amend last commit | `git commit --amend -s --no-edit && git push -f` | -| GitHub UI only | Edit the commit message in the PR → add
    `Signed-off-by: Your Name ` | +| Scenario | Command | +| ----------------- | ------------------------------------------------ | +| Amend last commit | `git commit --amend -s --no-edit && git push -f` | The **DCO check** blocks merges until every commit in the PR carries the footer (with squash this is just the one). @@ -548,12 +547,12 @@ To publish a new version of the CLI, run the release scripts defined in `codex-c --- -## Security & Responsible AI +## Security & Responsible AI -Have you discovered a vulnerability or have concerns about model output? Please e‑mail **security@openai.com** and we will respond promptly. +Have you discovered a vulnerability or have concerns about model output? Please e-mail **security@openai.com** and we will respond promptly. --- ## License -This repository is licensed under the [Apache-2.0 License](LICENSE). +This repository is licensed under the [Apache-2.0 License](LICENSE). diff --git a/scripts/asciicheck.py b/scripts/asciicheck.py new file mode 100755 index 0000000000..812d1c6b69 --- /dev/null +++ b/scripts/asciicheck.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 + +import argparse +import sys +from pathlib import Path + +""" +Utility script that takes a list of files and returns non-zero if any of them +contain non-ASCII characters other than those in the allowed list. + +If --fix is used, it will attempt to replace non-ASCII characters with ASCII +equivalents. + +The motivation behind this script is that characters like U+00A0 (non-breaking +space) can cause regexes not to match and can result in surprising anchor +values for headings when GitHub renders Markdown as HTML. +""" + + +""" +When --fix is used, perform the following substitutions. +""" +substitutions: dict[int, str] = { + 0x00A0: " ", # non-breaking space + 0x2011: "-", # non-breaking hyphen + 0x2013: "-", # en dash + 0x2014: "-", # em dash + 0x2018: "'", # left single quote + 0x2019: "'", # right single quote + 0x201C: '"', # left double quote + 0x201D: '"', # right double quote + 0x2026: "...", # ellipsis + 0x202F: " ", # narrow non-breaking space +} + +""" +Unicode codepoints that are allowed in addition to ASCII. +Be conservative with this list. + +Note that it is always an option to use the hex HTML representation +instead of the character itself so the source code is ASCII-only. +For example, U+2728 (sparkles) can be written as `✨`. +""" +allowed_unicode_codepoints = { + 0x2728, # sparkles +} + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Check for non-ASCII characters in files." + ) + parser.add_argument( + "--fix", + action="store_true", + help="Rewrite files, replacing non-ASCII characters with ASCII equivalents, where possible.", + ) + parser.add_argument( + "files", + nargs="+", + help="Files to check for non-ASCII characters.", + ) + args = parser.parse_args() + + has_errors = False + for filename in args.files: + path = Path(filename) + has_errors |= lint_utf8_ascii(path, fix=args.fix) + return 1 if has_errors else 0 + + +def lint_utf8_ascii(filename: Path, fix: bool) -> bool: + """Returns True if an error was printed.""" + try: + with open(filename, "rb") as f: + raw = f.read() + text = raw.decode("utf-8") + except UnicodeDecodeError as e: + print("UTF-8 decoding error:") + print(f" byte offset: {e.start}") + print(f" reason: {e.reason}") + # Attempt to find line/column + partial = raw[: e.start] + line = partial.count(b"\n") + 1 + col = e.start - (partial.rfind(b"\n") if b"\n" in partial else -1) + print(f" location: line {line}, column {col}") + return True + + errors = [] + for lineno, line in enumerate(text.splitlines(keepends=True), 1): + for colno, char in enumerate(line, 1): + codepoint = ord(char) + if char == "\n": + continue + if ( + not (0x20 <= codepoint <= 0x7E) + and codepoint not in allowed_unicode_codepoints + ): + errors.append((lineno, colno, char, codepoint)) + + if errors: + for lineno, colno, char, codepoint in errors: + safe_char = repr(char)[1:-1] # nicely escape things like \u202f + print( + f"Invalid character at line {lineno}, column {colno}: U+{codepoint:04X} ({safe_char})" + ) + + if errors and fix: + print(f"Attempting to fix {filename}...") + num_replacements = 0 + new_contents = "" + for char in text: + codepoint = ord(char) + if codepoint in substitutions: + num_replacements += 1 + new_contents += substitutions[codepoint] + else: + new_contents += char + with open(filename, "w", encoding="utf-8") as f: + f.write(new_contents) + print(f"Fixed {num_replacements} of {len(errors)} errors in {filename}.") + + return bool(errors) + + +if __name__ == "__main__": + sys.exit(main()) From d78f77edb707a1a8147cc6b28a677ec553d83182 Mon Sep 17 00:00:00 2001 From: Thomas Date: Wed, 23 Apr 2025 00:32:36 +1000 Subject: [PATCH 62/82] =?UTF-8?q?fix(agent-loop):=20update=20required=20pr?= =?UTF-8?q?operties=20to=20include=20workdir=20and=20ti=E2=80=A6=20(#530)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this I get an issue running codex it in a docker container. I receive: ``` { "answer": "{\"role\":\"user\",\"content\":[{\"type\":\"input_text\",\"text\":\"\\\"Say hello world\\\"\"}],\"type\":\"message\"}\n{\"id\":\"error-1745325184914\",\"type\":\"message\",\"role\":\"system\",\"content\":[{\"type\":\"input_text\",\"text\":\"⚠️ OpenAI rejected the request (request ID: req_f9027b59ebbce00061e9cd2dbb2d529a). Error details: Status: 400, Code: invalid_function_parameters, Type: invalid_request_error, Message: 400 Invalid schema for function 'shell': In context=(), 'required' is required to be supplied and to be an array including every key in properties. Missing 'workdir'.. Please verify your settings and try again.\"}]}\n" } ``` This fix makes it work. --- codex-cli/src/utils/agent/agent-loop.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codex-cli/src/utils/agent/agent-loop.ts b/codex-cli/src/utils/agent/agent-loop.ts index 5cde3b164a..3d34dee35e 100644 --- a/codex-cli/src/utils/agent/agent-loop.ts +++ b/codex-cli/src/utils/agent/agent-loop.ts @@ -649,7 +649,7 @@ export class AgentLoop { "The maximum time to wait for the command to complete in milliseconds.", }, }, - required: ["command"], + required: ["command", "workdir", "timeout"], additionalProperties: false, }, }, From 98a22273d957ded6ef766ad197b2a7d014f9abe7 Mon Sep 17 00:00:00 2001 From: Gabriel Bianconi <1275491+GabrielBianconi@users.noreply.github.com> Date: Tue, 22 Apr 2025 10:51:26 -0400 Subject: [PATCH 63/82] fix: inconsistent usage of base URL and API key (#507) A recent commit introduced the ability to use third-party model providers. (Really appreciate it!) However, the usage is inconsistent: some pieces of code use the custom providers, whereas others still have the old behavior. Additionally, `OPENAI_BASE_URL` is now being disregarded when it shouldn't be. This PR normalizes the usage to `getApiKey` and `getBaseUrl`, and enables the use of `OPENAI_BASE_URL` if present. --------- Co-authored-by: Gabriel Bianconi --- .../src/components/chat/terminal-chat.tsx | 11 +++++++--- .../src/components/singlepass-cli-app.tsx | 11 +++------- codex-cli/src/utils/compact-summary.ts | 10 ++++++---- codex-cli/src/utils/config.ts | 20 +++++++++++++++++-- codex-cli/src/utils/model-utils.ts | 6 ++++-- 5 files changed, 39 insertions(+), 19 deletions(-) diff --git a/codex-cli/src/components/chat/terminal-chat.tsx b/codex-cli/src/components/chat/terminal-chat.tsx index 9092932a6c..cf304ad77e 100644 --- a/codex-cli/src/components/chat/terminal-chat.tsx +++ b/codex-cli/src/components/chat/terminal-chat.tsx @@ -13,7 +13,7 @@ import { useTerminalSize } from "../../hooks/use-terminal-size.js"; import { AgentLoop } from "../../utils/agent/agent-loop.js"; import { ReviewDecision } from "../../utils/agent/review.js"; import { generateCompactSummary } from "../../utils/compact-summary.js"; -import { OPENAI_BASE_URL, saveConfig } from "../../utils/config.js"; +import { getBaseUrl, getApiKey, saveConfig } from "../../utils/config.js"; import { extractAppliedPatches as _extractAppliedPatches } from "../../utils/extract-applied-patches.js"; import { getGitDiff } from "../../utils/get-diff.js"; import { createInputItem } from "../../utils/input-utils.js"; @@ -65,18 +65,21 @@ const colorsByPolicy: Record = { * * @param command The command to explain * @param model The model to use for generating the explanation + * @param flexMode Whether to use the flex-mode service tier + * @param config The configuration object * @returns A human-readable explanation of what the command does */ async function generateCommandExplanation( command: Array, model: string, flexMode: boolean, + config: AppConfig, ): Promise { try { // Create a temporary OpenAI client const oai = new OpenAI({ - apiKey: process.env["OPENAI_API_KEY"], - baseURL: OPENAI_BASE_URL, + apiKey: getApiKey(config.provider), + baseURL: getBaseUrl(config.provider), }); // Format the command for display @@ -156,6 +159,7 @@ export default function TerminalChat({ items, model, Boolean(config.flexMode), + config, ); setItems([ { @@ -272,6 +276,7 @@ export default function TerminalChat({ command, model, Boolean(config.flexMode), + config, ); log(`Generated explanation: ${explanation}`); diff --git a/codex-cli/src/components/singlepass-cli-app.tsx b/codex-cli/src/components/singlepass-cli-app.tsx index 0c5eeb4e11..56d1d913b4 100644 --- a/codex-cli/src/components/singlepass-cli-app.tsx +++ b/codex-cli/src/components/singlepass-cli-app.tsx @@ -5,12 +5,7 @@ import type { FileOperation } from "../utils/singlepass/file_ops"; import Spinner from "./vendor/ink-spinner"; // Third‑party / vendor components import TextInput from "./vendor/ink-text-input"; -import { - OPENAI_TIMEOUT_MS, - OPENAI_BASE_URL as _OPENAI_BASE_URL, - getBaseUrl, - getApiKey, -} from "../utils/config"; +import { OPENAI_TIMEOUT_MS, getBaseUrl, getApiKey } from "../utils/config"; import { generateDiffSummary, generateEditSummary, @@ -399,8 +394,8 @@ export function SinglePassApp({ }); const openai = new OpenAI({ - apiKey: getApiKey(config.provider ?? "openai"), - baseURL: getBaseUrl(config.provider ?? "openai"), + apiKey: getApiKey(config.provider), + baseURL: getBaseUrl(config.provider), timeout: OPENAI_TIMEOUT_MS, }); const chatResp = await openai.beta.chat.completions.parse({ diff --git a/codex-cli/src/utils/compact-summary.ts b/codex-cli/src/utils/compact-summary.ts index 040145daa2..82a337e385 100644 --- a/codex-cli/src/utils/compact-summary.ts +++ b/codex-cli/src/utils/compact-summary.ts @@ -1,8 +1,8 @@ +import type { AppConfig } from "./config.js"; import type { ResponseItem } from "openai/resources/responses/responses.mjs"; -import { OPENAI_BASE_URL } from "./config.js"; +import { getBaseUrl, getApiKey } from "./config.js"; import OpenAI from "openai"; - /** * Generate a condensed summary of the conversation items. * @param items The list of conversation items to summarize @@ -14,16 +14,18 @@ import OpenAI from "openai"; * @param items The list of conversation items to summarize * @param model The model to use for generating the summary * @param flexMode Whether to use the flex-mode service tier + * @param config The configuration object * @returns A concise structured summary string */ export async function generateCompactSummary( items: Array, model: string, flexMode = false, + config: AppConfig, ): Promise { const oai = new OpenAI({ - apiKey: process.env["OPENAI_API_KEY"], - baseURL: OPENAI_BASE_URL, + apiKey: getApiKey(config.provider), + baseURL: getBaseUrl(config.provider), }); const conversationText = items diff --git a/codex-cli/src/utils/config.ts b/codex-cli/src/utils/config.ts index a8d73feb01..0c9cffa480 100644 --- a/codex-cli/src/utils/config.ts +++ b/codex-cli/src/utils/config.ts @@ -41,15 +41,26 @@ export function setApiKey(apiKey: string): void { OPENAI_API_KEY = apiKey; } -export function getBaseUrl(provider: string): string | undefined { +export function getBaseUrl(provider: string = "openai"): string | undefined { + // If the provider is `openai` and `OPENAI_BASE_URL` is set, use it + if (provider === "openai" && OPENAI_BASE_URL !== "") { + return OPENAI_BASE_URL; + } + const providerInfo = providers[provider.toLowerCase()]; if (providerInfo) { return providerInfo.baseURL; } + + // If the provider not found in the providers list and `OPENAI_BASE_URL` is set, use it + if (OPENAI_BASE_URL !== "") { + return OPENAI_BASE_URL; + } + return undefined; } -export function getApiKey(provider: string): string | undefined { +export function getApiKey(provider: string = "openai"): string | undefined { const providerInfo = providers[provider.toLowerCase()]; if (providerInfo) { if (providerInfo.name === "Ollama") { @@ -58,6 +69,11 @@ export function getApiKey(provider: string): string | undefined { return process.env[providerInfo.envKey]; } + // If the provider not found in the providers list and `OPENAI_API_KEY` is set, use it + if (OPENAI_API_KEY !== "") { + return OPENAI_API_KEY; + } + return undefined; } diff --git a/codex-cli/src/utils/model-utils.ts b/codex-cli/src/utils/model-utils.ts index 31f1afe6c4..774371ed7d 100644 --- a/codex-cli/src/utils/model-utils.ts +++ b/codex-cli/src/utils/model-utils.ts @@ -20,9 +20,11 @@ async function fetchModels(provider: string): Promise> { throw new Error("No API key configured for provider: " + provider); } - const baseURL = getBaseUrl(provider); try { - const openai = new OpenAI({ apiKey: getApiKey(provider), baseURL }); + const openai = new OpenAI({ + apiKey: getApiKey(provider), + baseURL: getBaseUrl(provider), + }); const list = await openai.models.list(); const models: Array = []; for await (const model of list as AsyncIterable<{ id?: string }>) { From ee6e1765fa216df2f054906b68d542adef8ac25b Mon Sep 17 00:00:00 2001 From: Scott Leibrand Date: Tue, 22 Apr 2025 08:02:10 -0700 Subject: [PATCH 64/82] agent-loop: minimal mid-stream #429 retry loop using existing back-off (#506) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As requested by @tibo-openai at https://github.com/openai/codex/pull/357#issuecomment-2816554203, this attempts a more minimal implementation of #357 that preserves as much as possible of the existing code's exponential backoff logic. Adds a small retry wrapper around the streaming for‑await loop so that HTTP 429s which occur *after* the stream has started no longer crash the CLI. Highlights • Re‑uses existing RATE_LIMIT_RETRY_WAIT_MS constant and 5‑attempt limit. • Exponential back‑off identical to initial request handling. This comment is probably more useful here in the PR: // The OpenAI SDK may raise a 429 (rate‑limit) *after* the stream has // started. Prior logic already retries the initial `responses.create` // call, but we need to add equivalent resilience for mid‑stream // failures. We keep the implementation minimal by wrapping the // existing `for‑await` loop in a small retry‑for‑loop that re‑creates // the stream with exponential back‑off. --- codex-cli/src/utils/agent/agent-loop.ts | 285 ++++++++++++++++-------- 1 file changed, 196 insertions(+), 89 deletions(-) diff --git a/codex-cli/src/utils/agent/agent-loop.ts b/codex-cli/src/utils/agent/agent-loop.ts index 3d34dee35e..bd6adcd09f 100644 --- a/codex-cli/src/utils/agent/agent-loop.ts +++ b/codex-cli/src/utils/agent/agent-loop.ts @@ -842,104 +842,211 @@ export class AgentLoop { return; } - try { - // eslint-disable-next-line no-await-in-loop - for await (const event of stream as AsyncIterable) { - log(`AgentLoop.run(): response event ${event.type}`); + const MAX_STREAM_RETRIES = 5; + let streamRetryAttempt = 0; - // process and surface each item (no-op until we can depend on streaming events) - if (event.type === "response.output_item.done") { - const item = event.item; - // 1) if it's a reasoning item, annotate it - type ReasoningItem = { type?: string; duration_ms?: number }; - const maybeReasoning = item as ReasoningItem; - if (maybeReasoning.type === "reasoning") { - maybeReasoning.duration_ms = Date.now() - thinkingStart; - } - if (item.type === "function_call") { - // Track outstanding tool call so we can abort later if needed. - // The item comes from the streaming response, therefore it has - // either `id` (chat) or `call_id` (responses) – we normalise - // by reading both. - const callId = - (item as { call_id?: string; id?: string }).call_id ?? - (item as { id?: string }).id; - if (callId) { - this.pendingAborts.add(callId); + // eslint-disable-next-line no-constant-condition + while (true) { + try { + // eslint-disable-next-line no-await-in-loop + for await (const event of stream as AsyncIterable) { + log(`AgentLoop.run(): response event ${event.type}`); + + // process and surface each item (no-op until we can depend on streaming events) + if (event.type === "response.output_item.done") { + const item = event.item; + // 1) if it's a reasoning item, annotate it + type ReasoningItem = { type?: string; duration_ms?: number }; + const maybeReasoning = item as ReasoningItem; + if (maybeReasoning.type === "reasoning") { + maybeReasoning.duration_ms = Date.now() - thinkingStart; } - } else { - stageItem(item as ResponseItem); - } - } - - if (event.type === "response.completed") { - if (thisGeneration === this.generation && !this.canceled) { - for (const item of event.response.output) { + if (item.type === "function_call") { + // Track outstanding tool call so we can abort later if needed. + // The item comes from the streaming response, therefore it has + // either `id` (chat) or `call_id` (responses) – we normalise + // by reading both. + const callId = + (item as { call_id?: string; id?: string }).call_id ?? + (item as { id?: string }).id; + if (callId) { + this.pendingAborts.add(callId); + } + } else { stageItem(item as ResponseItem); } } - if (event.response.status === "completed") { - // TODO: remove this once we can depend on streaming events - const newTurnInput = await this.processEventsWithoutStreaming( - event.response.output, - stageItem, - ); - turnInput = newTurnInput; + + if (event.type === "response.completed") { + if (thisGeneration === this.generation && !this.canceled) { + for (const item of event.response.output) { + stageItem(item as ResponseItem); + } + } + if (event.response.status === "completed") { + // TODO: remove this once we can depend on streaming events + const newTurnInput = await this.processEventsWithoutStreaming( + event.response.output, + stageItem, + ); + turnInput = newTurnInput; + } + lastResponseId = event.response.id; + this.onLastResponseId(event.response.id); } - lastResponseId = event.response.id; - this.onLastResponseId(event.response.id); } - } - } catch (err: unknown) { - // Gracefully handle an abort triggered via `cancel()` so that the - // consumer does not see an unhandled exception. - if (err instanceof Error && err.name === "AbortError") { - if (!this.canceled) { - // It was aborted for some other reason; surface the error. - throw err; + // Stream finished successfully – leave the retry loop. + break; + } catch (err: unknown) { + const isRateLimitError = (e: unknown): boolean => { + if (!e || typeof e !== "object") { + return false; + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const ex: any = e; + return ( + ex.status === 429 || + ex.code === "rate_limit_exceeded" || + ex.type === "rate_limit_exceeded" + ); + }; + + if ( + isRateLimitError(err) && + streamRetryAttempt < MAX_STREAM_RETRIES + ) { + streamRetryAttempt += 1; + + const waitMs = + RATE_LIMIT_RETRY_WAIT_MS * 2 ** (streamRetryAttempt - 1); + log( + `OpenAI stream rate‑limited – retry ${streamRetryAttempt}/${MAX_STREAM_RETRIES} in ${waitMs} ms`, + ); + + // Give the server a breather before retrying. + // eslint-disable-next-line no-await-in-loop + await new Promise((res) => setTimeout(res, waitMs)); + + // Re‑create the stream with the *same* parameters. + let reasoning: Reasoning | undefined; + if (this.model.startsWith("o")) { + reasoning = { effort: "high" }; + if (this.model === "o3" || this.model === "o4-mini") { + reasoning.summary = "auto"; + } + } + + const mergedInstructions = [prefix, this.instructions] + .filter(Boolean) + .join("\n"); + + const responseCall = + !this.config.provider || + this.config.provider?.toLowerCase() === "openai" + ? (params: ResponseCreateParams) => + this.oai.responses.create(params) + : (params: ResponseCreateParams) => + responsesCreateViaChatCompletions( + this.oai, + params as ResponseCreateParams & { stream: true }, + ); + + // eslint-disable-next-line no-await-in-loop + stream = await responseCall({ + model: this.model, + instructions: mergedInstructions, + previous_response_id: lastResponseId || undefined, + input: turnInput, + stream: true, + parallel_tool_calls: false, + reasoning, + ...(this.config.flexMode ? { service_tier: "flex" } : {}), + tools: [ + { + type: "function", + name: "shell", + description: + "Runs a shell command, and returns its output.", + strict: false, + parameters: { + type: "object", + properties: { + command: { + type: "array", + items: { type: "string" }, + }, + workdir: { + type: "string", + description: "The working directory for the command.", + }, + timeout: { + type: "number", + description: + "The maximum time to wait for the command to complete in milliseconds.", + }, + }, + required: ["command"], + additionalProperties: false, + }, + }, + ], + }); + + this.currentStream = stream; + // Continue to outer while to consume new stream. + continue; } - this.onLoading(false); - return; + + // Gracefully handle an abort triggered via `cancel()` so that the + // consumer does not see an unhandled exception. + if (err instanceof Error && err.name === "AbortError") { + if (!this.canceled) { + // It was aborted for some other reason; surface the error. + throw err; + } + this.onLoading(false); + return; + } + // Suppress internal stack on JSON parse failures + if (err instanceof SyntaxError) { + this.onItem({ + id: `error-${Date.now()}`, + type: "message", + role: "system", + content: [ + { + type: "input_text", + text: "⚠️ Failed to parse streaming response (invalid JSON). Please `/clear` to reset.", + }, + ], + }); + this.onLoading(false); + return; + } + // Handle OpenAI API quota errors + if ( + err instanceof Error && + (err as { code?: string }).code === "insufficient_quota" + ) { + this.onItem({ + id: `error-${Date.now()}`, + type: "message", + role: "system", + content: [ + { + type: "input_text", + text: "⚠️ Insufficient quota. Please check your billing details and retry.", + }, + ], + }); + this.onLoading(false); + return; + } + throw err; + } finally { + this.currentStream = null; } - // Suppress internal stack on JSON parse failures - if (err instanceof SyntaxError) { - this.onItem({ - id: `error-${Date.now()}`, - type: "message", - role: "system", - content: [ - { - type: "input_text", - text: "⚠️ Failed to parse streaming response (invalid JSON). Please `/clear` to reset.", - }, - ], - }); - this.onLoading(false); - return; - } - // Handle OpenAI API quota errors - if ( - err instanceof Error && - (err as { code?: string }).code === "insufficient_quota" - ) { - this.onItem({ - id: `error-${Date.now()}`, - type: "message", - role: "system", - content: [ - { - type: "input_text", - text: "⚠️ Insufficient quota. Please check your billing details and retry.", - }, - ], - }); - this.onLoading(false); - return; - } - throw err; - } finally { - this.currentStream = null; - } + } // end while retry loop log( `Turn inputs (${turnInput.length}) - ${turnInput From dd330646d2e72e7a75f6e634da1bf55624708ee5 Mon Sep 17 00:00:00 2001 From: narenoai Date: Tue, 22 Apr 2025 09:28:21 -0700 Subject: [PATCH 65/82] =?UTF-8?q?feat:=20add=20CLI=20=E2=80=93version=20fl?= =?UTF-8?q?ag=20(#492)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a new flag to cli `--version` that prints the current version and exits --------- Co-authored-by: Thibault Sottiaux --- codex-cli/src/cli.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/codex-cli/src/cli.tsx b/codex-cli/src/cli.tsx index 0bed07bf52..6220128f8d 100644 --- a/codex-cli/src/cli.tsx +++ b/codex-cli/src/cli.tsx @@ -52,6 +52,8 @@ const cli = meow( $ codex completion Options + --version Print version and exit + -h, --help Show usage and exit -m, --model Model to use for completions (default: o4-mini) -p, --provider Provider to use for completions (default: openai) @@ -97,6 +99,7 @@ const cli = meow( flags: { // misc help: { type: "boolean", aliases: ["h"] }, + version: { type: "boolean", description: "Print version and exit" }, view: { type: "string" }, model: { type: "string", aliases: ["m"] }, provider: { type: "string", aliases: ["p"] }, From 9b06fb48a76895855374982369d7bf85f10431d9 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Tue, 22 Apr 2025 09:38:12 -0700 Subject: [PATCH 66/82] add check to ensure ToC in README.md matches headings in the file (#541) This introduces a Python script (written by Codex!) to verify that the table of contents in the root `README.md` matches the headings. Like `scripts/asciicheck.py` in https://github.com/openai/codex/pull/513, it reports differences by default (and exits non-zero if there are any) and also has a `--fix` option to synchronize the ToC with the headings. This will be enforced by CI and the changes to `README.md` in this PR were generated by the script, so you can see that our ToC was missing some entries prior to this PR. --- .github/workflows/ci.yml | 2 + README.md | 8 ++- scripts/readme_toc.py | 119 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 1 deletion(-) create mode 100755 scripts/readme_toc.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f5dd0d31a..508b5b9bd5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,3 +70,5 @@ jobs: - name: Ensure README.md contains only ASCII and certain Unicode code points run: ./scripts/asciicheck.py README.md + - name: Check README ToC + run: python3 scripts/readme_toc.py README.md diff --git a/README.md b/README.md index 9ac89499ac..ed8d3ff84c 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@
    Table of Contents + + - [Experimental Technology Disclaimer](#experimental-technology-disclaimer) - [Quickstart](#quickstart) - [Why Codex?](#why-codex) @@ -19,13 +21,16 @@ - [CLI Reference](#cli-reference) - [Memory & Project Docs](#memory--project-docs) - [Non-interactive / CI mode](#non-interactive--ci-mode) +- [Tracing / Verbose Logging](#tracing--verbose-logging) - [Recipes](#recipes) - [Installation](#installation) - [Configuration](#configuration) - [FAQ](#faq) +- [Zero Data Retention (ZDR) Organization Limitation](#zero-data-retention-zdr-organization-limitation) - [Funding Opportunity](#funding-opportunity) - [Contributing](#contributing) - [Development workflow](#development-workflow) + - [Git Hooks with Husky](#git-hooks-with-husky) - [Nix Flake Development](#nix-flake-development) - [Writing high-impact code changes](#writing-high-impact-code-changes) - [Opening a pull request](#opening-a-pull-request) @@ -37,7 +42,8 @@ - [Releasing `codex`](#releasing-codex) - [Security & Responsible AI](#security--responsible-ai) - [License](#license) -- [Zero Data Retention (ZDR) Organization Limitation](#zero-data-retention-zdr-organization-limitation) + +
    diff --git a/scripts/readme_toc.py b/scripts/readme_toc.py new file mode 100755 index 0000000000..fb1ac066a7 --- /dev/null +++ b/scripts/readme_toc.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 + +""" +Utility script to verify (and optionally fix) the Table of Contents in a +Markdown file. By default, it checks that the ToC between `` +and `` matches the headings in the file. With --fix, it +rewrites the file to update the ToC. +""" + +import argparse +import sys +import re +import difflib +from pathlib import Path +from typing import List + +# Markers for the Table of Contents section +BEGIN_TOC: str = "" +END_TOC: str = "" + + +def main() -> int: + parser = argparse.ArgumentParser( + description="Check and optionally fix the README.md Table of Contents." + ) + parser.add_argument( + "file", nargs="?", default="README.md", help="Markdown file to process" + ) + parser.add_argument( + "--fix", action="store_true", help="Rewrite file with updated ToC" + ) + args = parser.parse_args() + path = Path(args.file) + return check_or_fix(path, args.fix) + + +def generate_toc_lines(content: str) -> List[str]: + """ + Generate markdown list lines for headings (## to ######) in content. + """ + lines = content.splitlines() + headings = [] + in_code = False + for line in lines: + if line.strip().startswith("```"): + in_code = not in_code + continue + if in_code: + continue + m = re.match(r"^(#{2,6})\s+(.*)$", line) + if not m: + continue + level = len(m.group(1)) + text = m.group(2).strip() + headings.append((level, text)) + + toc = [] + for level, text in headings: + indent = " " * (level - 2) + slug = text.lower() + # normalize spaces and dashes + slug = slug.replace("\u00a0", " ") + slug = slug.replace("\u2011", "-").replace("\u2013", "-").replace("\u2014", "-") + # drop other punctuation + slug = re.sub(r"[^0-9a-z\s-]", "", slug) + slug = slug.strip().replace(" ", "-") + toc.append(f"{indent}- [{text}](#{slug})") + return toc + + +def check_or_fix(readme_path: Path, fix: bool) -> int: + if not readme_path.is_file(): + print(f"Error: file not found: {readme_path}", file=sys.stderr) + return 1 + content = readme_path.read_text(encoding="utf-8") + lines = content.splitlines() + # locate ToC markers + try: + begin_idx = next(i for i, l in enumerate(lines) if l.strip() == BEGIN_TOC) + end_idx = next(i for i, l in enumerate(lines) if l.strip() == END_TOC) + except StopIteration: + print( + f"Error: Could not locate '{BEGIN_TOC}' or '{END_TOC}' in {readme_path}.", + file=sys.stderr, + ) + return 1 + # extract current ToC list items + current_block = lines[begin_idx + 1 : end_idx] + current = [l for l in current_block if l.lstrip().startswith("- [")] + # generate expected ToC + expected = generate_toc_lines(content) + if current == expected: + return 0 + if not fix: + print( + "ERROR: README ToC is out of date. Diff between existing and generated ToC:" + ) + # Show full unified diff of current vs expected + diff = difflib.unified_diff( + current, + expected, + fromfile="existing ToC", + tofile="generated ToC", + lineterm="", + ) + for line in diff: + print(line) + return 1 + # rebuild file with updated ToC + prefix = lines[: begin_idx + 1] + suffix = lines[end_idx:] + new_lines = prefix + [""] + expected + [""] + suffix + readme_path.write_text("\n".join(new_lines) + "\n", encoding="utf-8") + print(f"Updated ToC in {readme_path}.") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From 94d5408875d4b4739b8c735463ec6eb7174c184c Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Tue, 22 Apr 2025 09:43:10 -0700 Subject: [PATCH 67/82] add instructions for connecting to a visual debugger under Contributing (#496) While here, I also moved the Nix stuff to the end of the **Contributing** section and replaced some examples with `npm` to use `pnpm`. --- README.md | 61 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index ed8d3ff84c..ab4701bf7d 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ - [Contributing](#contributing) - [Development workflow](#development-workflow) - [Git Hooks with Husky](#git-hooks-with-husky) - - [Nix Flake Development](#nix-flake-development) + - [Debugging](#debugging) - [Writing high-impact code changes](#writing-high-impact-code-changes) - [Opening a pull request](#opening-a-pull-request) - [Review process](#review-process) @@ -40,6 +40,8 @@ - [Contributor License Agreement (CLA)](#contributor-license-agreement-cla) - [Quick fixes](#quick-fixes) - [Releasing `codex`](#releasing-codex) + - [Alternative Build Options](#alternative-build-options) + - [Nix Flake Development](#nix-flake-development) - [Security & Responsible AI](#security--responsible-ai) - [License](#license) @@ -433,7 +435,7 @@ This project uses [Husky](https://typicode.github.io/husky/) to enforce code qua These hooks help maintain code quality and prevent pushing code with failing tests. For more details, see [HUSKY.md](./codex-cli/HUSKY.md). ```bash -npm test && npm run lint && npm run typecheck +pnpm test && pnpm run lint && pnpm run typecheck ``` - If you have **not** yet signed the Contributor License Agreement (CLA), add a PR comment containing the exact text @@ -456,30 +458,14 @@ pnpm lint:fix pnpm format:fix ``` -#### Nix Flake Development +### Debugging -Prerequisite: Nix >= 2.4 with flakes enabled (`experimental-features = nix-command flakes` in `~/.config/nix/nix.conf`). +To debug the CLI with a visual debugger, do the following in the `codex-cli` folder: -Enter a Nix development shell: - -```bash -nix develop -``` - -This shell includes Node.js, installs dependencies, builds the CLI, and provides a `codex` command alias. - -Build and run the CLI directly: - -```bash -nix build -./result/bin/codex --help -``` - -Run the CLI via the flake app: - -```bash -nix run .#codex -``` +- Run `pnpm run build` to build the CLI, which will generate `cli.js.map` alongside `cli.js` in the `dist` folder. +- Run the CLI with `node --inspect-brk ./dist/cli.js` The program then waits until a debugger is attached before proceeding. Options: + - In VS Code, choose **Debug: Attach to Node Process** from the command palette and choose the option in the dropdown with debug port `9229` (likely the first option) + - Go to in Chrome and find **localhost:9229** and click **trace** ### Writing high-impact code changes @@ -551,6 +537,33 @@ To publish a new version of the CLI, run the release scripts defined in `codex-c 5. Copy README, build, and publish to npm: `pnpm release` 6. Push to branch: `git push origin HEAD` +### Alternative Build Options + +#### Nix Flake Development + +Prerequisite: Nix >= 2.4 with flakes enabled (`experimental-features = nix-command flakes` in `~/.config/nix/nix.conf`). + +Enter a Nix development shell: + +```bash +nix develop +``` + +This shell includes Node.js, installs dependencies, builds the CLI, and provides a `codex` command alias. + +Build and run the CLI directly: + +```bash +nix build +./result/bin/codex --help +``` + +Run the CLI via the flake app: + +```bash +nix run .#codex +``` + --- ## Security & Responsible AI From fcd1d4bdf9b793eda003aa3675ec64db4de8cd27 Mon Sep 17 00:00:00 2001 From: Naveen Kumar Battula Date: Tue, 22 Apr 2025 22:25:08 +0530 Subject: [PATCH 68/82] feat: show actionable errors when api keys are missing (#523) Change errors on missing api key of other providers from image (missing deepseek key but still throws error for openai) to image This should help new users figure out the issue easier and go to the right place to get api keys OpenAI key missing would popup with the right link image --- codex-cli/src/cli.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/codex-cli/src/cli.tsx b/codex-cli/src/cli.tsx index 6220128f8d..2872cf0cf9 100644 --- a/codex-cli/src/cli.tsx +++ b/codex-cli/src/cli.tsx @@ -258,11 +258,19 @@ if (!apiKey) { // eslint-disable-next-line no-console console.error( `\n${chalk.red(`Missing ${provider} API key.`)}\n\n` + - `Set the environment variable ${chalk.bold("OPENAI_API_KEY")} ` + + `Set the environment variable ${chalk.bold( + `${provider.toUpperCase()}_API_KEY`, + )} ` + `and re-run this command.\n` + - `You can create a key here: ${chalk.bold( - chalk.underline("https://platform.openai.com/account/api-keys"), - )}\n`, + `${ + provider.toLowerCase() === "openai" + ? `You can create a key here: ${chalk.bold( + chalk.underline("https://platform.openai.com/account/api-keys"), + )}\n` + : `You can create a ${chalk.bold( + `${provider.toUpperCase()}_API_KEY`, + )} ` + `in the ${chalk.bold(`${provider}`)} dashboard.\n` + }`, ); process.exit(1); } From 549fc650c312b52960311dea1dd85f84ae06584e Mon Sep 17 00:00:00 2001 From: moppywhip <48742547+moppywhip@users.noreply.github.com> Date: Tue, 22 Apr 2025 13:59:31 -0400 Subject: [PATCH 69/82] fix: remove requirement for api key for ollama (#546) Fixes #540 # Skip API key validation for Ollama provider ## Description This PR modifies the CLI to not require an API key when using Ollama as the provider ## Changes - Modified the validation logic to skip API key checks for these providers - Updated the README to clarify that Ollama doesn't require an API key --- codex-cli/src/cli.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/codex-cli/src/cli.tsx b/codex-cli/src/cli.tsx index 2872cf0cf9..d4982fb161 100644 --- a/codex-cli/src/cli.tsx +++ b/codex-cli/src/cli.tsx @@ -254,7 +254,11 @@ const imagePaths = cli.flags.image; const provider = cli.flags.provider ?? config.provider ?? "openai"; const apiKey = getApiKey(provider); -if (!apiKey) { +// Set of providers that don't require API keys +const NO_API_KEY_REQUIRED = new Set(["ollama"]); + +// Skip API key validation for providers that don't require an API key +if (!apiKey && !NO_API_KEY_REQUIRED.has(provider.toLowerCase())) { // eslint-disable-next-line no-console console.error( `\n${chalk.red(`Missing ${provider} API key.`)}\n\n` + From f99c9080fd751c5ed03a0ae7c9215685d57b22db Mon Sep 17 00:00:00 2001 From: Daniil Davydov <46081038+dan0102dan@users.noreply.github.com> Date: Tue, 22 Apr 2025 22:05:48 +0300 Subject: [PATCH 70/82] fix: support [provider]_BASE_URL (#542) Resolved issue where an OLLAMA_BASE_URL was not properly handled (openai/codex#516). --- codex-cli/src/utils/config.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/codex-cli/src/utils/config.ts b/codex-cli/src/utils/config.ts index 0c9cffa480..91b35956e3 100644 --- a/codex-cli/src/utils/config.ts +++ b/codex-cli/src/utils/config.ts @@ -47,6 +47,13 @@ export function getBaseUrl(provider: string = "openai"): string | undefined { return OPENAI_BASE_URL; } + // Check for a PROVIDER-specific override: e.g. OLLAMA_BASE_URL + const envKey = `${provider.toUpperCase()}_BASE_URL`; + if (process.env[envKey]) { + return process.env[envKey]; + } + + // Use the default URL from providers if available const providerInfo = providers[provider.toLowerCase()]; if (providerInfo) { return providerInfo.baseURL; From a30e79b768f97d6ba34d9ee7bf8e24a4aafaffea Mon Sep 17 00:00:00 2001 From: Fouad Matin <169186268+fouad-openai@users.noreply.github.com> Date: Tue, 22 Apr 2025 13:49:10 -0700 Subject: [PATCH 71/82] fix: agent loop for disable response storage (#543) - Fixes post-merge of #506 --------- Co-authored-by: Ilan Bigio --- codex-cli/src/utils/agent/agent-loop.ts | 249 +++++++++++++++++------- codex-cli/src/utils/parsers.ts | 8 +- 2 files changed, 184 insertions(+), 73 deletions(-) diff --git a/codex-cli/src/utils/agent/agent-loop.ts b/codex-cli/src/utils/agent/agent-loop.ts index bd6adcd09f..329e6e8c93 100644 --- a/codex-cli/src/utils/agent/agent-loop.ts +++ b/codex-cli/src/utils/agent/agent-loop.ts @@ -7,6 +7,7 @@ import type { ResponseInputItem, ResponseItem, ResponseCreateParams, + FunctionTool, } from "openai/resources/responses/responses.mjs"; import type { Reasoning } from "openai/resources.mjs"; @@ -68,6 +69,30 @@ type AgentLoopParams = { onLastResponseId: (lastResponseId: string) => void; }; +const shellTool: FunctionTool = { + type: "function", + name: "shell", + description: "Runs a shell command, and returns its output.", + strict: false, + parameters: { + type: "object", + properties: { + command: { type: "array", items: { type: "string" } }, + workdir: { + type: "string", + description: "The working directory for the command.", + }, + timeout: { + type: "number", + description: + "The maximum time to wait for the command to complete in milliseconds.", + }, + }, + required: ["command"], + additionalProperties: false, + }, +}; + export class AgentLoop { private model: string; private provider: string; @@ -109,7 +134,7 @@ export class AgentLoop { private canceled = false; /** - * Local conversation transcript used when `disableResponseStorage === false`. Holds + * Local conversation transcript used when `disableResponseStorage === true`. Holds * all non‑system items exchanged so far so we can provide full context on * every request. */ @@ -442,9 +467,13 @@ export class AgentLoop { // `previous_response_id` when `disableResponseStorage` is enabled. When storage // is disabled we deliberately ignore the caller‑supplied value because // the backend will not retain any state that could be referenced. + // If the backend stores conversation state (`disableResponseStorage === false`) we + // forward the caller‑supplied `previousResponseId` so that the model sees the + // full context. When storage is disabled we *must not* send any ID because the + // server no longer retains the referenced response. let lastResponseId: string = this.disableResponseStorage - ? previousResponseId - : ""; + ? "" + : previousResponseId; // If there are unresolved function calls from a previously cancelled run // we have to emit dummy tool outputs so that the API no longer expects @@ -473,7 +502,11 @@ export class AgentLoop { // conversation, so we must include the *entire* transcript (minus system // messages) on every call. - let turnInput: Array; + let turnInput: Array = []; + // Keeps track of how many items in `turnInput` stem from the existing + // transcript so we can avoid re‑emitting them to the UI. Only used when + // `disableResponseStorage === true`. + let transcriptPrefixLen = 0; const stripInternalFields = ( item: ResponseInputItem, @@ -485,28 +518,47 @@ export class AgentLoop { // be referenced elsewhere (e.g. UI components). const clean = { ...item } as Record; delete clean["duration_ms"]; + // Remove OpenAI-assigned identifiers and transient status so the + // backend does not reject items that were never persisted because we + // use `store: false`. + delete clean["id"]; + delete clean["status"]; return clean as unknown as ResponseInputItem; }; if (this.disableResponseStorage) { + // Remember where the existing transcript ends – everything after this + // index in the upcoming `turnInput` list will be *new* for this turn + // and therefore needs to be surfaced to the UI. + transcriptPrefixLen = this.transcript.length; + // Ensure the transcript is up‑to‑date with the latest user input so // that subsequent iterations see a complete history. - const newUserItems: Array = input.filter((it) => { - if ( - (it.type === "message" && it.role !== "system") || - it.type === "reasoning" - ) { - return false; - } - return true; - }); - this.transcript.push(...newUserItems); + // `turnInput` is still empty at this point (it will be filled later). + // We need to look at the *input* items the user just supplied. + this.transcript.push(...filterToApiMessages(input)); turnInput = [...this.transcript, ...abortOutputs].map( stripInternalFields, ); } else { turnInput = [...abortOutputs, ...input].map(stripInternalFields); + + // When response storage is disabled we have to maintain our own + // running transcript so that the next request still contains the + // full conversational history. We skipped the transcript update in + // the branch above – ensure we do it here as well. + if (this.disableResponseStorage) { + const newUserItems: Array = input.filter((it) => { + if (it.type === "message" && it.role === "system") { + return false; + } else if (it.type === "reasoning") { + return false; + } + return true; + }); + this.transcript.push(...newUserItems.map(stripInternalFields)); + } } this.onLoading(true); @@ -553,6 +605,24 @@ export class AgentLoop { // API schema and therefore causes a 400 error when included // in subsequent requests whose context is sent verbatim. + // Skip items that we have already inserted earlier or that the + // model does not need to see again in the next turn. + // • function_call – superseded by the forthcoming + // function_call_output. + // • reasoning – internal only, never sent back. + // • user messages – we added these to the transcript when + // building the first turnInput; stageItem would add a + // duplicate. + if ( + (item as ResponseInputItem).type === "function_call" || + (item as ResponseInputItem).type === "reasoning" || + ((item as ResponseInputItem).type === "message" && + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (item as any).role === "user") + ) { + return; + } + const clone: ResponseInputItem = { ...(item as unknown as ResponseInputItem), } as ResponseInputItem; @@ -578,7 +648,18 @@ export class AgentLoop { // Only surface the *new* input items to the UI – replaying the entire // transcript would duplicate messages that have already been shown in // earlier turns. - const deltaInput = [...abortOutputs, ...input]; + // `turnInput` holds the *new* items that will be sent to the API in + // this iteration. Surface exactly these to the UI so that we do not + // re‑emit messages from previous turns (which would duplicate user + // prompts) and so that freshly generated `function_call_output`s are + // shown immediately. + // Figure out what subset of `turnInput` constitutes *new* information + // for the UI so that we don’t spam the interface with repeats of the + // entire transcript on every iteration when response storage is + // disabled. + const deltaInput = this.disableResponseStorage + ? turnInput.slice(transcriptPrefixLen) + : [...turnInput]; for (const item of deltaInput) { stageItem(item as ResponseItem); } @@ -629,31 +710,12 @@ export class AgentLoop { store: true, previous_response_id: lastResponseId || undefined, }), - tools: [ - { - type: "function", - name: "shell", - description: "Runs a shell command, and returns its output.", - strict: true, - parameters: { - type: "object", - properties: { - command: { type: "array", items: { type: "string" } }, - workdir: { - type: "string", - description: "The working directory for the command.", - }, - timeout: { - type: "number", - description: - "The maximum time to wait for the command to complete in milliseconds.", - }, - }, - required: ["command", "workdir", "timeout"], - additionalProperties: false, - }, - }, - ], + tools: [shellTool], + // Explicitly tell the model it is allowed to pick whatever + // tool it deems appropriate. Omitting this sometimes leads to + // the model ignoring the available tools and responding with + // plain text instead (resulting in a missing tool‑call). + tool_choice: "auto", }); break; } catch (error) { @@ -883,13 +945,60 @@ export class AgentLoop { stageItem(item as ResponseItem); } } - if (event.response.status === "completed") { + if ( + event.response.status === "completed" || + (event.response.status as unknown as string) === + "requires_action" + ) { // TODO: remove this once we can depend on streaming events const newTurnInput = await this.processEventsWithoutStreaming( event.response.output, stageItem, ); - turnInput = newTurnInput; + + // When we do not use server‑side storage we maintain our + // own transcript so that *future* turns still contain full + // conversational context. However, whether we advance to + // another loop iteration should depend solely on the + // presence of *new* input items (i.e. items that were not + // part of the previous request). Re‑sending the transcript + // by itself would create an infinite request loop because + // `turnInput.length` would never reach zero. + + if (this.disableResponseStorage) { + // 1) Append the freshly emitted output to our local + // transcript (minus non‑message items the model does + // not need to see again). + const cleaned = filterToApiMessages( + event.response.output.map(stripInternalFields), + ); + this.transcript.push(...cleaned); + + // 2) Determine the *delta* (newTurnInput) that must be + // sent in the next iteration. If there is none we can + // safely terminate the loop – the transcript alone + // does not constitute new information for the + // assistant to act upon. + + const delta = filterToApiMessages( + newTurnInput.map(stripInternalFields), + ); + + if (delta.length === 0) { + // No new input => end conversation. + turnInput = []; + } else { + // Re‑send full transcript *plus* the new delta so the + // stateless backend receives complete context. + turnInput = [...this.transcript, ...delta]; + // The prefix ends at the current transcript length – + // everything after this index is new for the next + // iteration. + transcriptPrefixLen = this.transcript.length; + } + } else { + turnInput = newTurnInput; + } } lastResponseId = event.response.id; this.onLastResponseId(event.response.id); @@ -951,45 +1060,27 @@ export class AgentLoop { params as ResponseCreateParams & { stream: true }, ); + log( + "agentLoop.run(): responseCall(1): turnInput: " + + JSON.stringify(turnInput), + ); // eslint-disable-next-line no-await-in-loop stream = await responseCall({ model: this.model, instructions: mergedInstructions, - previous_response_id: lastResponseId || undefined, input: turnInput, stream: true, parallel_tool_calls: false, reasoning, ...(this.config.flexMode ? { service_tier: "flex" } : {}), - tools: [ - { - type: "function", - name: "shell", - description: - "Runs a shell command, and returns its output.", - strict: false, - parameters: { - type: "object", - properties: { - command: { - type: "array", - items: { type: "string" }, - }, - workdir: { - type: "string", - description: "The working directory for the command.", - }, - timeout: { - type: "number", - description: - "The maximum time to wait for the command to complete in milliseconds.", - }, - }, - required: ["command"], - additionalProperties: false, - }, - }, - ], + ...(this.disableResponseStorage + ? { store: false } + : { + store: true, + previous_response_id: lastResponseId || undefined, + }), + tools: [shellTool], + tool_choice: "auto", }); this.currentStream = stream; @@ -1393,3 +1484,17 @@ You MUST adhere to the following criteria when executing the task: - When your task involves writing or modifying files: - Do NOT tell the user to "save the file" or "copy the code into a file" if you already created or modified the file using \`apply_patch\`. Instead, reference the file as already saved. - Do NOT show the full contents of large files you have already written, unless the user explicitly asks for them.`; + +function filterToApiMessages( + items: Array, +): Array { + return items.filter((it) => { + if (it.type === "message" && it.role === "system") { + return false; + } + if (it.type === "reasoning") { + return false; + } + return true; + }); +} diff --git a/codex-cli/src/utils/parsers.ts b/codex-cli/src/utils/parsers.ts index 4461379c02..ed86833f51 100644 --- a/codex-cli/src/utils/parsers.ts +++ b/codex-cli/src/utils/parsers.ts @@ -81,7 +81,13 @@ export function parseToolCallArguments( } const { cmd, command } = json as Record; - const commandArray = toStringArray(cmd) ?? toStringArray(command); + // The OpenAI model sometimes produces a single string instead of an array. + // Accept both shapes: + const commandArray = + toStringArray(cmd) ?? + toStringArray(command) ?? + (typeof cmd === "string" ? [cmd] : undefined) ?? + (typeof command === "string" ? [command] : undefined); if (commandArray == null) { return undefined; } From 7c1f2d7deba4bf05361bf682931444fbf552eda9 Mon Sep 17 00:00:00 2001 From: Michael Bolin Date: Tue, 22 Apr 2025 14:07:47 -0700 Subject: [PATCH 72/82] when a shell tool call invokes apply_patch, resolve relative paths against workdir, if specified (#556) Previously, we were ignoring the `workdir` field in an `ExecInput` when running it through `canAutoApprove()`. For ordinary `exec()` calls, that was sufficient, but for `apply_patch`, we need the `workdir` to resolve relative paths in the `apply_patch` argument so that we can check them in `isPathConstrainedTowritablePaths()`. Likewise, we also need the workdir when running `execApplyPatch()` because the paths need to be resolved again. Ideally, the `ApplyPatchCommand` returned by `canAutoApprove()` would not be a simple `patch: string`, but the parsed patch with all of the paths resolved, in which case `execApplyPatch()` could expect absolute paths and would not need `workdir`. --- codex-cli/src/approvals.ts | 49 +++++++++++++++++-- codex-cli/src/utils/agent/exec.ts | 13 +++-- .../src/utils/agent/handle-exec-command.ts | 6 +-- codex-cli/tests/approvals.test.ts | 8 ++- 4 files changed, 63 insertions(+), 13 deletions(-) diff --git a/codex-cli/src/approvals.ts b/codex-cli/src/approvals.ts index ff37a8903f..5ea73eab07 100644 --- a/codex-cli/src/approvals.ts +++ b/codex-cli/src/approvals.ts @@ -71,13 +71,14 @@ export type ApprovalPolicy = */ export function canAutoApprove( command: ReadonlyArray, + workdir: string | undefined, policy: ApprovalPolicy, writableRoots: ReadonlyArray, env: NodeJS.ProcessEnv = process.env, ): SafetyAssessment { if (command[0] === "apply_patch") { return command.length === 2 && typeof command[1] === "string" - ? canAutoApproveApplyPatch(command[1], writableRoots, policy) + ? canAutoApproveApplyPatch(command[1], workdir, writableRoots, policy) : { type: "reject", reason: "Invalid apply_patch command", @@ -103,7 +104,12 @@ export function canAutoApprove( ) { const applyPatchArg = tryParseApplyPatch(command[2]); if (applyPatchArg != null) { - return canAutoApproveApplyPatch(applyPatchArg, writableRoots, policy); + return canAutoApproveApplyPatch( + applyPatchArg, + workdir, + writableRoots, + policy, + ); } let bashCmd; @@ -162,6 +168,7 @@ export function canAutoApprove( function canAutoApproveApplyPatch( applyPatchArg: string, + workdir: string | undefined, writableRoots: ReadonlyArray, policy: ApprovalPolicy, ): SafetyAssessment { @@ -179,7 +186,13 @@ function canAutoApproveApplyPatch( break; } - if (isWritePatchConstrainedToWritablePaths(applyPatchArg, writableRoots)) { + if ( + isWritePatchConstrainedToWritablePaths( + applyPatchArg, + workdir, + writableRoots, + ) + ) { return { type: "auto-approve", reason: "apply_patch command is constrained to writable paths", @@ -208,6 +221,7 @@ function canAutoApproveApplyPatch( */ function isWritePatchConstrainedToWritablePaths( applyPatchArg: string, + workdir: string | undefined, writableRoots: ReadonlyArray, ): boolean { // `identify_files_needed()` returns a list of files that will be modified or @@ -222,10 +236,12 @@ function isWritePatchConstrainedToWritablePaths( return ( allPathsConstrainedTowritablePaths( identify_files_needed(applyPatchArg), + workdir, writableRoots, ) && allPathsConstrainedTowritablePaths( identify_files_added(applyPatchArg), + workdir, writableRoots, ) ); @@ -233,24 +249,47 @@ function isWritePatchConstrainedToWritablePaths( function allPathsConstrainedTowritablePaths( candidatePaths: ReadonlyArray, + workdir: string | undefined, writableRoots: ReadonlyArray, ): boolean { return candidatePaths.every((candidatePath) => - isPathConstrainedTowritablePaths(candidatePath, writableRoots), + isPathConstrainedTowritablePaths(candidatePath, workdir, writableRoots), ); } /** If candidatePath is relative, it will be resolved against cwd. */ function isPathConstrainedTowritablePaths( candidatePath: string, + workdir: string | undefined, writableRoots: ReadonlyArray, ): boolean { - const candidateAbsolutePath = path.resolve(candidatePath); + const candidateAbsolutePath = resolvePathAgainstWorkdir( + candidatePath, + workdir, + ); + return writableRoots.some((writablePath) => pathContains(writablePath, candidateAbsolutePath), ); } +/** + * If not already an absolute path, resolves `candidatePath` against `workdir` + * if specified; otherwise, against `process.cwd()`. + */ +export function resolvePathAgainstWorkdir( + candidatePath: string, + workdir: string | undefined, +): string { + if (path.isAbsolute(candidatePath)) { + return candidatePath; + } else if (workdir != null) { + return path.resolve(workdir, candidatePath); + } else { + return path.resolve(candidatePath); + } +} + /** Both `parent` and `child` must be absolute paths. */ function pathContains(parent: string, child: string): boolean { const relative = path.relative(parent, child); diff --git a/codex-cli/src/utils/agent/exec.ts b/codex-cli/src/utils/agent/exec.ts index 25c6f86abc..f0177979ec 100644 --- a/codex-cli/src/utils/agent/exec.ts +++ b/codex-cli/src/utils/agent/exec.ts @@ -10,6 +10,7 @@ import { formatCommandForDisplay } from "../../format-command.js"; import fs from "fs"; import os from "os"; import { parse } from "shell-quote"; +import { resolvePathAgainstWorkdir } from "src/approvals.js"; const DEFAULT_TIMEOUT_MS = 10_000; // 10 seconds @@ -60,16 +61,20 @@ export function exec( return execForSandbox(cmd, opts, writableRoots, abortSignal); } -export function execApplyPatch(patchText: string): ExecResult { +export function execApplyPatch( + patchText: string, + workdir: string | undefined, +): ExecResult { // This is a temporary measure to understand what are the common base commands // until we start persisting and uploading rollouts try { const result = process_patch( patchText, - (p) => fs.readFileSync(p, "utf8"), - (p, c) => fs.writeFileSync(p, c, "utf8"), - (p) => fs.unlinkSync(p), + (p) => fs.readFileSync(resolvePathAgainstWorkdir(p, workdir), "utf8"), + (p, c) => + fs.writeFileSync(resolvePathAgainstWorkdir(p, workdir), c, "utf8"), + (p) => fs.unlinkSync(resolvePathAgainstWorkdir(p, workdir)), ); return { stdout: result, diff --git a/codex-cli/src/utils/agent/handle-exec-command.ts b/codex-cli/src/utils/agent/handle-exec-command.ts index aea2c3a707..85d6869192 100644 --- a/codex-cli/src/utils/agent/handle-exec-command.ts +++ b/codex-cli/src/utils/agent/handle-exec-command.ts @@ -81,7 +81,7 @@ export async function handleExecCommand( ) => Promise, abortSignal?: AbortSignal, ): Promise { - const { cmd: command } = args; + const { cmd: command, workdir } = args; const key = deriveCommandKey(command); @@ -103,7 +103,7 @@ export async function handleExecCommand( // working directory so that edits are constrained to the project root. If // the caller wishes to broaden or restrict the set it can be made // configurable in the future. - const safety = canAutoApprove(command, policy, [process.cwd()]); + const safety = canAutoApprove(command, workdir, policy, [process.cwd()]); let runInSandbox: boolean; switch (safety.type) { @@ -247,7 +247,7 @@ async function execCommand( const start = Date.now(); const execResult = applyPatchCommand != null - ? execApplyPatch(applyPatchCommand.patch) + ? execApplyPatch(applyPatchCommand.patch, workdir) : await exec( { ...execInput, additionalWritableRoots }, await getSandbox(runInSandbox), diff --git a/codex-cli/tests/approvals.test.ts b/codex-cli/tests/approvals.test.ts index a90abad6eb..94daacce00 100644 --- a/codex-cli/tests/approvals.test.ts +++ b/codex-cli/tests/approvals.test.ts @@ -11,7 +11,13 @@ describe("canAutoApprove()", () => { const writeablePaths: Array = []; const check = (command: ReadonlyArray): SafetyAssessment => - canAutoApprove(command, "suggest", writeablePaths, env); + canAutoApprove( + command, + /* workdir */ undefined, + "suggest", + writeablePaths, + env, + ); test("simple safe commands", () => { expect(check(["ls"])).toEqual({ From dc096302e50b127244e4f015386e8d00ac5d8ef7 Mon Sep 17 00:00:00 2001 From: Nick Carchedi Date: Tue, 22 Apr 2025 15:15:28 -0600 Subject: [PATCH 73/82] fix typo in prompt (#558) --- codex-cli/src/utils/agent/agent-loop.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codex-cli/src/utils/agent/agent-loop.ts b/codex-cli/src/utils/agent/agent-loop.ts index 329e6e8c93..8d99e46cb4 100644 --- a/codex-cli/src/utils/agent/agent-loop.ts +++ b/codex-cli/src/utils/agent/agent-loop.ts @@ -1480,7 +1480,7 @@ You MUST adhere to the following criteria when executing the task: - For smaller tasks, describe in brief bullet points - For more complex tasks, include brief high-level description, use bullet points, and include details that would be relevant to a code reviewer. - If completing the user's task DOES NOT require writing or modifying files (e.g., the user asks a question about the code base): - - Respond in a friendly tune as a remote teammate, who is knowledgeable, capable and eager to help with coding. + - Respond in a friendly tone as a remote teammate, who is knowledgeable, capable and eager to help with coding. - When your task involves writing or modifying files: - Do NOT tell the user to "save the file" or "copy the code into a file" if you already created or modified the file using \`apply_patch\`. Instead, reference the file as already saved. - Do NOT show the full contents of large files you have already written, unless the user explicitly asks for them.`; From 12bc2dcc4e525154d32a1c5c070dc1f4efb31329 Mon Sep 17 00:00:00 2001 From: Fouad Matin <169186268+fouad-openai@users.noreply.github.com> Date: Tue, 22 Apr 2025 14:18:04 -0700 Subject: [PATCH 74/82] bump(version): 0.1.2504221401 (#559) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## `0.1.2504221401` ### 🚀 Features - Show actionable errors when api keys are missing (#523) - Add CLI `--version` flag (#492) ### 🐛 Bug Fixes - Agent loop for ZDR (`disableResponseStorage`) (#543) - Fix relative `workdir` check for `apply_patch` (#556) - Minimal mid-stream #429 retry loop using existing back-off (#506) - Inconsistent usage of base URL and API key (#507) - Remove requirement for api key for ollama (#546) - Support `[provider]_BASE_URL` (#542) --- CHANGELOG.md | 16 ++++++++++++++++ codex-cli/package.json | 2 +- codex-cli/src/utils/session.ts | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f8cb037207..a60f38ad30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,22 @@ You can install any of these versions: `npm install -g codex@version` +## `0.1.2504221401` + +### 🚀 Features + +- Show actionable errors when api keys are missing (#523) +- Add CLI `--version` flag (#492) + +### 🐛 Bug Fixes + +- Agent loop for ZDR (`disableResponseStorage`) (#543) +- Fix relative `workdir` check for `apply_patch` (#556) +- Minimal mid-stream #429 retry loop using existing back-off (#506) +- Inconsistent usage of base URL and API key (#507) +- Remove requirement for api key for ollama (#546) +- Support `[provider]_BASE_URL` (#542) + ## `0.1.2504220136` ### 🚀 Features diff --git a/codex-cli/package.json b/codex-cli/package.json index d438e2ab26..7b852ca35a 100644 --- a/codex-cli/package.json +++ b/codex-cli/package.json @@ -1,6 +1,6 @@ { "name": "@openai/codex", - "version": "0.1.2504220136", + "version": "0.1.2504221401", "license": "Apache-2.0", "bin": { "codex": "bin/codex.js" diff --git a/codex-cli/src/utils/session.ts b/codex-cli/src/utils/session.ts index 1697f7cec7..c439f49094 100644 --- a/codex-cli/src/utils/session.ts +++ b/codex-cli/src/utils/session.ts @@ -1,4 +1,4 @@ -export const CLI_VERSION = "0.1.2504220136"; // Must be in sync with package.json. +export const CLI_VERSION = "0.1.2504221401"; // Must be in sync with package.json. export const ORIGIN = "codex_cli_ts"; export type TerminalChatSession = { From 750d97e8ad172c05e2bfe050d98ca12c1468198b Mon Sep 17 00:00:00 2001 From: chunterb <31895576+chunterb@users.noreply.github.com> Date: Tue, 22 Apr 2025 16:31:25 -0500 Subject: [PATCH 75/82] feat: add openai model info configuration (#551) In reference to [Issue 548](https://github.com/openai/codex/issues/548) - part 1. --- codex-cli/src/utils/model-info.ts | 198 ++++++++++++++++++++++++++++ codex-cli/src/utils/model-utils.ts | 9 +- codex-cli/tests/model-info.test.ts | 19 +++ codex-cli/tests/model-utils.test.ts | 78 +++++++++++ 4 files changed, 301 insertions(+), 3 deletions(-) create mode 100644 codex-cli/src/utils/model-info.ts create mode 100644 codex-cli/tests/model-info.test.ts create mode 100644 codex-cli/tests/model-utils.test.ts diff --git a/codex-cli/src/utils/model-info.ts b/codex-cli/src/utils/model-info.ts new file mode 100644 index 0000000000..bbe0cb36a9 --- /dev/null +++ b/codex-cli/src/utils/model-info.ts @@ -0,0 +1,198 @@ +export type ModelInfo = { + /** The human-readable label for this model */ + label: string; + /** The max context window size for this model */ + maxContextLength: number; +}; + +export type SupportedModelId = keyof typeof openAiModelInfo; +export const openAiModelInfo = { + "o1-pro-2025-03-19": { + label: "o1 Pro (2025-03-19)", + maxContextLength: 200000, + }, + "o3": { + label: "o3", + maxContextLength: 200000, + }, + "o3-2025-04-16": { + label: "o3 (2025-04-16)", + maxContextLength: 200000, + }, + "o4-mini": { + label: "o4 Mini", + maxContextLength: 200000, + }, + "gpt-4.1-nano": { + label: "GPT-4.1 Nano", + maxContextLength: 1000000, + }, + "gpt-4.1-nano-2025-04-14": { + label: "GPT-4.1 Nano (2025-04-14)", + maxContextLength: 1000000, + }, + "o4-mini-2025-04-16": { + label: "o4 Mini (2025-04-16)", + maxContextLength: 200000, + }, + "gpt-4": { + label: "GPT-4", + maxContextLength: 8192, + }, + "o1-preview-2024-09-12": { + label: "o1 Preview (2024-09-12)", + maxContextLength: 128000, + }, + "gpt-4.1-mini": { + label: "GPT-4.1 Mini", + maxContextLength: 1000000, + }, + "gpt-3.5-turbo-instruct-0914": { + label: "GPT-3.5 Turbo Instruct (0914)", + maxContextLength: 4096, + }, + "gpt-4o-mini-search-preview": { + label: "GPT-4o Mini Search Preview", + maxContextLength: 128000, + }, + "gpt-4.1-mini-2025-04-14": { + label: "GPT-4.1 Mini (2025-04-14)", + maxContextLength: 1000000, + }, + "chatgpt-4o-latest": { + label: "ChatGPT-4o Latest", + maxContextLength: 128000, + }, + "gpt-3.5-turbo-1106": { + label: "GPT-3.5 Turbo (1106)", + maxContextLength: 16385, + }, + "gpt-4o-search-preview": { + label: "GPT-4o Search Preview", + maxContextLength: 128000, + }, + "gpt-4-turbo": { + label: "GPT-4 Turbo", + maxContextLength: 128000, + }, + "gpt-4o-realtime-preview-2024-12-17": { + label: "GPT-4o Realtime Preview (2024-12-17)", + maxContextLength: 128000, + }, + "gpt-3.5-turbo-instruct": { + label: "GPT-3.5 Turbo Instruct", + maxContextLength: 4096, + }, + "gpt-3.5-turbo": { + label: "GPT-3.5 Turbo", + maxContextLength: 16385, + }, + "gpt-4-turbo-preview": { + label: "GPT-4 Turbo Preview", + maxContextLength: 128000, + }, + "gpt-4o-mini-search-preview-2025-03-11": { + label: "GPT-4o Mini Search Preview (2025-03-11)", + maxContextLength: 128000, + }, + "gpt-4-0125-preview": { + label: "GPT-4 (0125) Preview", + maxContextLength: 128000, + }, + "gpt-4o-2024-11-20": { + label: "GPT-4o (2024-11-20)", + maxContextLength: 128000, + }, + "o3-mini": { + label: "o3 Mini", + maxContextLength: 200000, + }, + "gpt-4o-2024-05-13": { + label: "GPT-4o (2024-05-13)", + maxContextLength: 128000, + }, + "gpt-4-turbo-2024-04-09": { + label: "GPT-4 Turbo (2024-04-09)", + maxContextLength: 128000, + }, + "gpt-3.5-turbo-16k": { + label: "GPT-3.5 Turbo 16k", + maxContextLength: 16385, + }, + "o3-mini-2025-01-31": { + label: "o3 Mini (2025-01-31)", + maxContextLength: 200000, + }, + "o1-preview": { + label: "o1 Preview", + maxContextLength: 128000, + }, + "o1-2024-12-17": { + label: "o1 (2024-12-17)", + maxContextLength: 128000, + }, + "gpt-4-0613": { + label: "GPT-4 (0613)", + maxContextLength: 8192, + }, + "o1": { + label: "o1", + maxContextLength: 128000, + }, + "o1-pro": { + label: "o1 Pro", + maxContextLength: 200000, + }, + "gpt-4.5-preview": { + label: "GPT-4.5 Preview", + maxContextLength: 128000, + }, + "gpt-4.5-preview-2025-02-27": { + label: "GPT-4.5 Preview (2025-02-27)", + maxContextLength: 128000, + }, + "gpt-4o-search-preview-2025-03-11": { + label: "GPT-4o Search Preview (2025-03-11)", + maxContextLength: 128000, + }, + "gpt-4o": { + label: "GPT-4o", + maxContextLength: 128000, + }, + "gpt-4o-mini": { + label: "GPT-4o Mini", + maxContextLength: 128000, + }, + "gpt-4o-2024-08-06": { + label: "GPT-4o (2024-08-06)", + maxContextLength: 128000, + }, + "gpt-4.1": { + label: "GPT-4.1", + maxContextLength: 1000000, + }, + "gpt-4.1-2025-04-14": { + label: "GPT-4.1 (2025-04-14)", + maxContextLength: 1000000, + }, + "gpt-4o-mini-2024-07-18": { + label: "GPT-4o Mini (2024-07-18)", + maxContextLength: 128000, + }, + "o1-mini": { + label: "o1 Mini", + maxContextLength: 128000, + }, + "gpt-3.5-turbo-0125": { + label: "GPT-3.5 Turbo (0125)", + maxContextLength: 16385, + }, + "o1-mini-2024-09-12": { + label: "o1 Mini (2024-09-12)", + maxContextLength: 128000, + }, + "gpt-4-1106-preview": { + label: "GPT-4 (1106) Preview", + maxContextLength: 128000, + }, +} as const satisfies Record; diff --git a/codex-cli/src/utils/model-utils.ts b/codex-cli/src/utils/model-utils.ts index 774371ed7d..5670fc44db 100644 --- a/codex-cli/src/utils/model-utils.ts +++ b/codex-cli/src/utils/model-utils.ts @@ -2,6 +2,7 @@ import type { ResponseItem } from "openai/resources/responses/responses.mjs"; import { approximateTokensUsed } from "./approximate-tokens-used.js"; import { getBaseUrl, getApiKey } from "./config"; +import { type SupportedModelId, openAiModelInfo } from "./model-info.js"; import OpenAI from "openai"; const MODEL_LIST_TIMEOUT_MS = 2_000; // 2 seconds @@ -89,10 +90,12 @@ export async function isModelSupportedForResponses( } /** Returns the maximum context length (in tokens) for a given model. */ -function maxTokensForModel(model: string): number { - // TODO: These numbers are best‑effort guesses and provide a basis for UI percentages. They - // should be provider & model specific instead of being wild guesses. +export function maxTokensForModel(model: string): number { + if (model in openAiModelInfo) { + return openAiModelInfo[model as SupportedModelId].maxContextLength; + } + // fallback to heuristics for models not in the registry const lower = model.toLowerCase(); if (lower.includes("32k")) { return 32000; diff --git a/codex-cli/tests/model-info.test.ts b/codex-cli/tests/model-info.test.ts new file mode 100644 index 0000000000..9626744d29 --- /dev/null +++ b/codex-cli/tests/model-info.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, test } from "vitest"; +import { openAiModelInfo } from "../src/utils/model-info"; + +describe("Model Info", () => { + test("supportedModelInfo contains expected models", () => { + expect(openAiModelInfo).toHaveProperty("gpt-4o"); + expect(openAiModelInfo).toHaveProperty("gpt-4.1"); + expect(openAiModelInfo).toHaveProperty("o3"); + }); + + test("model info entries have required properties", () => { + Object.entries(openAiModelInfo).forEach(([_, info]) => { + expect(info).toHaveProperty("label"); + expect(info).toHaveProperty("maxContextLength"); + expect(typeof info.label).toBe("string"); + expect(typeof info.maxContextLength).toBe("number"); + }); + }); +}); diff --git a/codex-cli/tests/model-utils.test.ts b/codex-cli/tests/model-utils.test.ts new file mode 100644 index 0000000000..6dd523827c --- /dev/null +++ b/codex-cli/tests/model-utils.test.ts @@ -0,0 +1,78 @@ +import { describe, test, expect } from "vitest"; +import { + calculateContextPercentRemaining, + maxTokensForModel, +} from "../src/utils/model-utils"; +import { openAiModelInfo } from "../src/utils/model-info"; +import type { ResponseItem } from "openai/resources/responses/responses.mjs"; + +describe("Model Utils", () => { + describe("openAiModelInfo", () => { + test("model info entries have required properties", () => { + Object.entries(openAiModelInfo).forEach(([_, info]) => { + expect(info).toHaveProperty("label"); + expect(info).toHaveProperty("maxContextLength"); + expect(typeof info.label).toBe("string"); + expect(typeof info.maxContextLength).toBe("number"); + }); + }); + }); + + describe("maxTokensForModel", () => { + test("returns correct token limit for known models", () => { + const knownModel = "gpt-4o"; + const expectedTokens = openAiModelInfo[knownModel].maxContextLength; + expect(maxTokensForModel(knownModel)).toBe(expectedTokens); + }); + + test("handles models with size indicators in their names", () => { + expect(maxTokensForModel("some-model-32k")).toBe(32000); + expect(maxTokensForModel("some-model-16k")).toBe(16000); + expect(maxTokensForModel("some-model-8k")).toBe(8000); + expect(maxTokensForModel("some-model-4k")).toBe(4000); + }); + + test("defaults to 128k for unknown models not in the registry", () => { + expect(maxTokensForModel("completely-unknown-model")).toBe(128000); + }); + }); + + describe("calculateContextPercentRemaining", () => { + test("returns 100% for empty items", () => { + const result = calculateContextPercentRemaining([], "gpt-4o"); + expect(result).toBe(100); + }); + + test("calculates percentage correctly for non-empty items", () => { + const mockItems: Array = [ + { + id: "test-id", + type: "message", + role: "user", + status: "completed", + content: [ + { + type: "input_text", + text: "A".repeat( + openAiModelInfo["gpt-4o"].maxContextLength * 0.25 * 4, + ), + }, + ], + } as ResponseItem, + ]; + + const result = calculateContextPercentRemaining(mockItems, "gpt-4o"); + expect(result).toBeCloseTo(75, 0); + }); + + test("handles models that are not in the registry", () => { + const mockItems: Array = []; + + const result = calculateContextPercentRemaining( + mockItems, + "unknown-model", + ); + expect(result).toBe(100); + }); + }); +}); From 20b6ef0de8d94af28ac066f1c58b3d9e1639b518 Mon Sep 17 00:00:00 2001 From: Misha Davidov Date: Tue, 22 Apr 2025 16:45:17 -0700 Subject: [PATCH 76/82] feat: create parent directories when creating new files. (#552) apply_patch doesn't create parent directories when creating a new file leading to confusion and flailing by the agent. This will create parent directories automatically when absent. --------- Co-authored-by: Thibault Sottiaux --- codex-cli/src/utils/agent/exec.ts | 19 ++++++++-- codex-cli/tests/exec-apply-patch.test.ts | 44 ++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 codex-cli/tests/exec-apply-patch.test.ts diff --git a/codex-cli/src/utils/agent/exec.ts b/codex-cli/src/utils/agent/exec.ts index f0177979ec..9c763ef551 100644 --- a/codex-cli/src/utils/agent/exec.ts +++ b/codex-cli/src/utils/agent/exec.ts @@ -9,6 +9,7 @@ import { exec as rawExec } from "./sandbox/raw-exec.js"; import { formatCommandForDisplay } from "../../format-command.js"; import fs from "fs"; import os from "os"; +import path from "path"; import { parse } from "shell-quote"; import { resolvePathAgainstWorkdir } from "src/approvals.js"; @@ -63,7 +64,7 @@ export function exec( export function execApplyPatch( patchText: string, - workdir: string | undefined, + workdir: string | undefined = undefined, ): ExecResult { // This is a temporary measure to understand what are the common base commands // until we start persisting and uploading rollouts @@ -72,8 +73,20 @@ export function execApplyPatch( const result = process_patch( patchText, (p) => fs.readFileSync(resolvePathAgainstWorkdir(p, workdir), "utf8"), - (p, c) => - fs.writeFileSync(resolvePathAgainstWorkdir(p, workdir), c, "utf8"), + (p, c) => { + const resolvedPath = resolvePathAgainstWorkdir(p, workdir); + + // Ensure the parent directory exists before writing the file. This + // mirrors the behaviour of the standalone apply_patch CLI (see + // write_file() in apply-patch.ts) and prevents errors when adding a + // new file in a not‑yet‑created sub‑directory. + const dir = path.dirname(resolvedPath); + if (dir !== ".") { + fs.mkdirSync(dir, { recursive: true }); + } + + fs.writeFileSync(resolvedPath, c, "utf8"); + }, (p) => fs.unlinkSync(resolvePathAgainstWorkdir(p, workdir)), ); return { diff --git a/codex-cli/tests/exec-apply-patch.test.ts b/codex-cli/tests/exec-apply-patch.test.ts new file mode 100644 index 0000000000..d28411fa78 --- /dev/null +++ b/codex-cli/tests/exec-apply-patch.test.ts @@ -0,0 +1,44 @@ +import { execApplyPatch } from "../src/utils/agent/exec.js"; +import fs from "fs"; +import os from "os"; +import path from "path"; +import { test, expect } from "vitest"; + +/** + * This test verifies that `execApplyPatch()` is able to add a new file whose + * parent directory does not yet exist. Prior to the fix, the call would throw + * because `fs.writeFileSync()` could not create intermediate directories. The + * test creates an isolated temporary directory to avoid polluting the project + * workspace. + */ +test("execApplyPatch creates missing directories when adding a file", () => { + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "apply-patch-test-")); + + // Ensure we start from a clean slate. + const nestedFileRel = path.join("foo", "bar", "baz.txt"); + const nestedFileAbs = path.join(tmpDir, nestedFileRel); + expect(fs.existsSync(nestedFileAbs)).toBe(false); + + const patch = `*** Begin Patch\n*** Add File: ${nestedFileRel}\n+hello new world\n*** End Patch`; + + // Run execApplyPatch() with cwd switched to tmpDir so that the relative + // path in the patch is resolved inside the temporary location. + const prevCwd = process.cwd(); + try { + process.chdir(tmpDir); + + const result = execApplyPatch(patch); + expect(result.exitCode).toBe(0); + expect(result.stderr).toBe(""); + } finally { + process.chdir(prevCwd); + } + + // The file (and its parent directories) should have been created with the + // expected contents. + const fileContents = fs.readFileSync(nestedFileAbs, "utf8"); + expect(fileContents).toBe("hello new world"); + + // Cleanup to keep tmpdir tidy. + fs.rmSync(tmpDir, { recursive: true, force: true }); +}); From 23f0887df3323a2b2a5bd87780c8845776b56bba Mon Sep 17 00:00:00 2001 From: Daniel Nakov Date: Tue, 22 Apr 2025 20:37:48 -0400 Subject: [PATCH 77/82] bug: non-openai mode - fix for gemini content: null, fix 429 to throw before stream (#563) Gemini's API is finicky, it 400's without an error when you pass content: null Also fixed the rate limiting issues by throwing outside of the iterator. I think there's a separate issue with the second isRateLimit check in agent-loop - turnInput is cleared by that time, so it retries without the last message. --- codex-cli/src/utils/responses.ts | 563 +++++++++--------- .../tests/responses-chat-completions.test.ts | 29 +- 2 files changed, 273 insertions(+), 319 deletions(-) diff --git a/codex-cli/src/utils/responses.ts b/codex-cli/src/utils/responses.ts index bc97291368..99bbd7eeeb 100644 --- a/codex-cli/src/utils/responses.ts +++ b/codex-cli/src/utils/responses.ts @@ -3,6 +3,7 @@ import type { ResponseCreateParams, Response, } from "openai/resources/responses/responses"; + // Define interfaces based on OpenAI API documentation type ResponseCreateInput = ResponseCreateParams; type ResponseOutput = Response; @@ -260,31 +261,7 @@ function convertTools( })); } -// Main function with overloading -async function responsesCreateViaChatCompletions( - openai: OpenAI, - input: ResponseCreateInput & { stream: true }, -): Promise>; -async function responsesCreateViaChatCompletions( - openai: OpenAI, - input: ResponseCreateInput & { stream?: false }, -): Promise; -async function responsesCreateViaChatCompletions( - openai: OpenAI, - input: ResponseCreateInput, -): Promise> { - if (input.stream) { - return streamResponses(openai, input); - } else { - return nonStreamResponses(openai, input); - } -} - -// Non-streaming implementation -async function nonStreamResponses( - openai: OpenAI, - input: ResponseCreateInput, -): Promise { +const createCompletion = (openai: OpenAI, input: ResponseCreateInput) => { const fullMessages = getFullMessages(input); const chatTools = convertTools(input.tools); const webSearchOptions = input.tools?.some( @@ -298,17 +275,55 @@ async function nonStreamResponses( messages: fullMessages, tools: chatTools, web_search_options: webSearchOptions, - temperature: input.temperature, - top_p: input.top_p, + temperature: input.temperature ?? 1.0, + top_p: input.top_p ?? 1.0, tool_choice: (input.tool_choice === "auto" ? "auto" : input.tool_choice) as OpenAI.Chat.Completions.ChatCompletionCreateParams["tool_choice"], + stream: input.stream || false, user: input.user, metadata: input.metadata, }; + return openai.chat.completions.create(chatInput); +}; + +// Main function with overloading +async function responsesCreateViaChatCompletions( + openai: OpenAI, + input: ResponseCreateInput & { stream: true }, +): Promise>; +async function responsesCreateViaChatCompletions( + openai: OpenAI, + input: ResponseCreateInput & { stream?: false }, +): Promise; +async function responsesCreateViaChatCompletions( + openai: OpenAI, + input: ResponseCreateInput, +): Promise> { + const completion = await createCompletion(openai, input); + if (input.stream) { + return streamResponses( + input, + completion as AsyncIterable, + ); + } else { + return nonStreamResponses( + input, + completion as unknown as OpenAI.Chat.Completions.ChatCompletion, + ); + } +} + +// Non-streaming implementation +async function nonStreamResponses( + input: ResponseCreateInput, + completion: OpenAI.Chat.Completions.ChatCompletion, +): Promise { + const fullMessages = getFullMessages(input); + try { - const chatResponse = await openai.chat.completions.create(chatInput); + const chatResponse = completion; if (!("choices" in chatResponse) || chatResponse.choices.length === 0) { throw new Error("No choices in chat completion response"); } @@ -429,56 +444,211 @@ async function nonStreamResponses( // Streaming implementation async function* streamResponses( - openai: OpenAI, input: ResponseCreateInput, + completion: AsyncIterable, ): AsyncGenerator { const fullMessages = getFullMessages(input); - const chatTools = convertTools(input.tools); - const webSearchOptions = input.tools?.some( - (tool) => tool.type === "function" && tool.name === "web_search", - ) - ? {} - : undefined; - const chatInput: OpenAI.Chat.Completions.ChatCompletionCreateParams = { + const responseId = generateId("resp"); + const outputItemId = generateId("msg"); + let textContentAdded = false; + let textContent = ""; + const toolCalls = new Map(); + let usage: UsageData | null = null; + const finalOutputItem: Array = []; + // Initial response + const initialResponse: Partial = { + id: responseId, + object: "response" as const, + created_at: Math.floor(Date.now() / 1000), + status: "in_progress" as const, model: input.model, - messages: fullMessages, - tools: chatTools, - web_search_options: webSearchOptions, + output: [], + error: null, + incomplete_details: null, + instructions: null, + max_output_tokens: null, + parallel_tool_calls: true, + previous_response_id: input.previous_response_id ?? null, + reasoning: null, temperature: input.temperature ?? 1.0, + text: { format: { type: "text" } }, + tool_choice: input.tool_choice ?? "auto", + tools: input.tools ?? [], top_p: input.top_p ?? 1.0, - tool_choice: (input.tool_choice === "auto" - ? "auto" - : input.tool_choice) as OpenAI.Chat.Completions.ChatCompletionCreateParams["tool_choice"], - stream: true, - user: input.user, - metadata: input.metadata, + truncation: input.truncation ?? "disabled", + usage: undefined, + user: input.user ?? undefined, + metadata: input.metadata ?? {}, + output_text: "", }; + yield { type: "response.created", response: initialResponse }; + yield { type: "response.in_progress", response: initialResponse }; + let isToolCall = false; + for await (const chunk of completion as AsyncIterable) { + // console.error('\nCHUNK: ', JSON.stringify(chunk)); + const choice = chunk.choices[0]; + if (!choice) { + continue; + } + if ( + !isToolCall && + (("tool_calls" in choice.delta && choice.delta.tool_calls) || + choice.finish_reason === "tool_calls") + ) { + isToolCall = true; + } - try { - // console.error("chatInput", JSON.stringify(chatInput)); - const stream = await openai.chat.completions.create(chatInput); + if (chunk.usage) { + usage = { + prompt_tokens: chunk.usage.prompt_tokens, + completion_tokens: chunk.usage.completion_tokens, + total_tokens: chunk.usage.total_tokens, + input_tokens: chunk.usage.prompt_tokens, + input_tokens_details: { cached_tokens: 0 }, + output_tokens: chunk.usage.completion_tokens, + output_tokens_details: { reasoning_tokens: 0 }, + }; + } + if (isToolCall) { + for (const tcDelta of choice.delta.tool_calls || []) { + const tcIndex = tcDelta.index; + const content_index = textContentAdded ? tcIndex + 1 : tcIndex; - // Initialize state - const responseId = generateId("resp"); - const outputItemId = generateId("msg"); - let textContentAdded = false; - let textContent = ""; - const toolCalls = new Map(); - let usage: UsageData | null = null; - const finalOutputItem: Array = []; - // Initial response - const initialResponse: Partial = { + if (!toolCalls.has(tcIndex)) { + // New tool call + const toolCallId = tcDelta.id || generateId("call"); + const functionName = tcDelta.function?.name || ""; + + yield { + type: "response.output_item.added", + item: { + type: "function_call", + id: outputItemId, + status: "in_progress", + call_id: toolCallId, + name: functionName, + arguments: "", + }, + output_index: 0, + }; + toolCalls.set(tcIndex, { + id: toolCallId, + name: functionName, + arguments: "", + }); + } + + if (tcDelta.function?.arguments) { + const current = toolCalls.get(tcIndex); + if (current) { + current.arguments += tcDelta.function.arguments; + yield { + type: "response.function_call_arguments.delta", + item_id: outputItemId, + output_index: 0, + content_index, + delta: tcDelta.function.arguments, + }; + } + } + } + + if (choice.finish_reason === "tool_calls") { + for (const [tcIndex, tc] of toolCalls) { + const item = { + type: "function_call", + id: outputItemId, + status: "completed", + call_id: tc.id, + name: tc.name, + arguments: tc.arguments, + }; + yield { + type: "response.function_call_arguments.done", + item_id: outputItemId, + output_index: tcIndex, + content_index: textContentAdded ? tcIndex + 1 : tcIndex, + arguments: tc.arguments, + }; + yield { + type: "response.output_item.done", + output_index: tcIndex, + item, + }; + finalOutputItem.push(item as unknown as ResponseContentOutput); + } + } else { + continue; + } + } else { + if (!textContentAdded) { + yield { + type: "response.content_part.added", + item_id: outputItemId, + output_index: 0, + content_index: 0, + part: { type: "output_text", text: "", annotations: [] }, + }; + textContentAdded = true; + } + if (choice.delta.content?.length) { + yield { + type: "response.output_text.delta", + item_id: outputItemId, + output_index: 0, + content_index: 0, + delta: choice.delta.content, + }; + textContent += choice.delta.content; + } + if (choice.finish_reason) { + yield { + type: "response.output_text.done", + item_id: outputItemId, + output_index: 0, + content_index: 0, + text: textContent, + }; + yield { + type: "response.content_part.done", + item_id: outputItemId, + output_index: 0, + content_index: 0, + part: { type: "output_text", text: textContent, annotations: [] }, + }; + const item = { + type: "message", + id: outputItemId, + status: "completed", + role: "assistant", + content: [ + { type: "output_text", text: textContent, annotations: [] }, + ], + }; + yield { + type: "response.output_item.done", + output_index: 0, + item, + }; + finalOutputItem.push(item as unknown as ResponseContentOutput); + } else { + continue; + } + } + + // Construct final response + const finalResponse: ResponseOutput = { id: responseId, object: "response" as const, - created_at: Math.floor(Date.now() / 1000), - status: "in_progress" as const, - model: input.model, - output: [], + created_at: initialResponse.created_at || Math.floor(Date.now() / 1000), + status: "completed" as const, error: null, incomplete_details: null, instructions: null, max_output_tokens: null, + model: chunk.model || input.model, + output: finalOutputItem as unknown as ResponseOutput["output"], parallel_tool_calls: true, previous_response_id: input.previous_response_id ?? null, reasoning: null, @@ -488,243 +658,54 @@ async function* streamResponses( tools: input.tools ?? [], top_p: input.top_p ?? 1.0, truncation: input.truncation ?? "disabled", - usage: undefined, + usage: usage as ResponseOutput["usage"], user: input.user ?? undefined, metadata: input.metadata ?? {}, output_text: "", - }; - yield { type: "response.created", response: initialResponse }; - yield { type: "response.in_progress", response: initialResponse }; - let isToolCall = false; - for await (const chunk of stream as AsyncIterable) { - // console.error('\nCHUNK: ', JSON.stringify(chunk)); - const choice = chunk.choices[0]; - if (!choice) { - continue; - } - if ( - !isToolCall && - (("tool_calls" in choice.delta && choice.delta.tool_calls) || - choice.finish_reason === "tool_calls") - ) { - isToolCall = true; - } + } as ResponseOutput; - if (chunk.usage) { - usage = { - prompt_tokens: chunk.usage.prompt_tokens, - completion_tokens: chunk.usage.completion_tokens, - total_tokens: chunk.usage.total_tokens, - input_tokens: chunk.usage.prompt_tokens, - input_tokens_details: { cached_tokens: 0 }, - output_tokens: chunk.usage.completion_tokens, - output_tokens_details: { reasoning_tokens: 0 }, - }; - } - if (isToolCall) { - for (const tcDelta of choice.delta.tool_calls || []) { - const tcIndex = tcDelta.index; - const content_index = textContentAdded ? tcIndex + 1 : tcIndex; - - if (!toolCalls.has(tcIndex)) { - // New tool call - const toolCallId = tcDelta.id || generateId("call"); - const functionName = tcDelta.function?.name || ""; - - yield { - type: "response.output_item.added", - item: { - type: "function_call", - id: outputItemId, - status: "in_progress", - call_id: toolCallId, - name: functionName, - arguments: "", - }, - output_index: 0, - }; - toolCalls.set(tcIndex, { - id: toolCallId, - name: functionName, - arguments: "", - }); - } - - if (tcDelta.function?.arguments) { - const current = toolCalls.get(tcIndex); - if (current) { - current.arguments += tcDelta.function.arguments; - yield { - type: "response.function_call_arguments.delta", - item_id: outputItemId, - output_index: 0, - content_index, - delta: tcDelta.function.arguments, - }; - } - } - } - - if (choice.finish_reason === "tool_calls") { - for (const [tcIndex, tc] of toolCalls) { - const item = { - type: "function_call", - id: outputItemId, - status: "completed", - call_id: tc.id, - name: tc.name, - arguments: tc.arguments, - }; - yield { - type: "response.function_call_arguments.done", - item_id: outputItemId, - output_index: tcIndex, - content_index: textContentAdded ? tcIndex + 1 : tcIndex, - arguments: tc.arguments, - }; - yield { - type: "response.output_item.done", - output_index: tcIndex, - item, - }; - finalOutputItem.push(item as unknown as ResponseContentOutput); - } - } else { - continue; - } - } else { - if (!textContentAdded) { - yield { - type: "response.content_part.added", - item_id: outputItemId, - output_index: 0, - content_index: 0, - part: { type: "output_text", text: "", annotations: [] }, - }; - textContentAdded = true; - } - if (choice.delta.content?.length) { - yield { - type: "response.output_text.delta", - item_id: outputItemId, - output_index: 0, - content_index: 0, - delta: choice.delta.content, - }; - textContent += choice.delta.content; - } - if (choice.finish_reason) { - yield { - type: "response.output_text.done", - item_id: outputItemId, - output_index: 0, - content_index: 0, - text: textContent, - }; - yield { - type: "response.content_part.done", - item_id: outputItemId, - output_index: 0, - content_index: 0, - part: { type: "output_text", text: textContent, annotations: [] }, - }; - const item = { - type: "message", - id: outputItemId, - status: "completed", - role: "assistant", - content: [ - { type: "output_text", text: textContent, annotations: [] }, - ], - }; - yield { - type: "response.output_item.done", - output_index: 0, - item, - }; - finalOutputItem.push(item as unknown as ResponseContentOutput); - } else { - continue; - } - } - - // Construct final response - const finalResponse: ResponseOutput = { - id: responseId, - object: "response" as const, - created_at: initialResponse.created_at || Math.floor(Date.now() / 1000), - status: "completed" as const, - error: null, - incomplete_details: null, - instructions: null, - max_output_tokens: null, - model: chunk.model || input.model, - output: finalOutputItem as unknown as ResponseOutput["output"], - parallel_tool_calls: true, - previous_response_id: input.previous_response_id ?? null, - reasoning: null, - temperature: input.temperature ?? 1.0, - text: { format: { type: "text" } }, - tool_choice: input.tool_choice ?? "auto", - tools: input.tools ?? [], - top_p: input.top_p ?? 1.0, - truncation: input.truncation ?? "disabled", - usage: usage as ResponseOutput["usage"], - user: input.user ?? undefined, - metadata: input.metadata ?? {}, - output_text: "", - } as ResponseOutput; - - // Store history - const assistantMessage = { + // Store history + const assistantMessage: OpenAI.Chat.Completions.ChatCompletionMessageParam = + { role: "assistant" as const, - content: textContent || null, }; - // Add tool_calls property if needed - if (toolCalls.size > 0) { - const toolCallsArray = Array.from(toolCalls.values()).map((tc) => ({ - id: tc.id, - type: "function" as const, - function: { name: tc.name, arguments: tc.arguments }, - })); - - // Define a more specific type for the assistant message with tool calls - type AssistantMessageWithToolCalls = - OpenAI.Chat.Completions.ChatCompletionMessageParam & { - tool_calls: Array<{ - id: string; - type: "function"; - function: { - name: string; - arguments: string; - }; - }>; - }; - - // Use type assertion with the defined type - (assistantMessage as AssistantMessageWithToolCalls).tool_calls = - toolCallsArray; - } - const newHistory = [...fullMessages, assistantMessage]; - conversationHistories.set(responseId, { - previous_response_id: input.previous_response_id ?? null, - messages: newHistory, - }); - - yield { type: "response.completed", response: finalResponse }; + if (textContent) { + assistantMessage.content = textContent; } - } catch (error) { - // console.error('\nERROR: ', JSON.stringify(error)); - yield { - type: "error", - code: - error instanceof Error && "code" in error - ? (error as { code: string }).code - : "unknown", - message: error instanceof Error ? error.message : String(error), - param: null, - }; + + // Add tool_calls property if needed + if (toolCalls.size > 0) { + const toolCallsArray = Array.from(toolCalls.values()).map((tc) => ({ + id: tc.id, + type: "function" as const, + function: { name: tc.name, arguments: tc.arguments }, + })); + + // Define a more specific type for the assistant message with tool calls + type AssistantMessageWithToolCalls = + OpenAI.Chat.Completions.ChatCompletionMessageParam & { + tool_calls: Array<{ + id: string; + type: "function"; + function: { + name: string; + arguments: string; + }; + }>; + }; + + // Use type assertion with the defined type + (assistantMessage as AssistantMessageWithToolCalls).tool_calls = + toolCallsArray; + } + const newHistory = [...fullMessages, assistantMessage]; + conversationHistories.set(responseId, { + previous_response_id: input.previous_response_id ?? null, + messages: newHistory, + }); + + yield { type: "response.completed", response: finalResponse }; } } diff --git a/codex-cli/tests/responses-chat-completions.test.ts b/codex-cli/tests/responses-chat-completions.test.ts index 85ab7d7dd8..e48366f8f4 100644 --- a/codex-cli/tests/responses-chat-completions.test.ts +++ b/codex-cli/tests/responses-chat-completions.test.ts @@ -294,7 +294,7 @@ describe("responsesCreateViaChatCompletions", () => { expect(callArgs.messages).toEqual([ { role: "user", content: "Hello world" }, ]); - expect(callArgs.stream).toBeUndefined(); + expect(callArgs.stream).toBe(false); } // Verify result format @@ -736,33 +736,6 @@ describe("responsesCreateViaChatCompletions", () => { } }); - it("should handle errors gracefully", async () => { - // Setup mock to throw an error - openAiState.createSpy = vi - .fn() - .mockRejectedValue(new Error("API connection error")); - - const openaiClient = new (await import("openai")).default({ - apiKey: "test-key", - }) as unknown as OpenAI; - - const inputMessage = createTestInput({ - model: "gpt-4o", - userMessage: "Test message", - stream: false, - }); - - // Expect the function to throw an error - await expect( - responsesModule.responsesCreateViaChatCompletions( - openaiClient, - inputMessage as unknown as ResponseCreateParamsNonStreaming & { - stream?: false | undefined; - }, - ), - ).rejects.toThrow("Failed to process chat completion"); - }); - it("handles streaming with tool calls", async () => { // Mock a streaming response with tool calls const mockStream = createToolCallsStream(); From 42619734678b200d75c94a225df70ece74f4022b Mon Sep 17 00:00:00 2001 From: Daniel Nakov Date: Wed, 23 Apr 2025 01:07:40 -0400 Subject: [PATCH 78/82] bug: non-openai mode - don't default temp and top_p (#572) I haven't seen any actual errors due to this, but it's been bothering me that I had it defaulted to 1. I think best to leave it undefined and have each provider do their thing --- codex-cli/src/utils/responses.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/codex-cli/src/utils/responses.ts b/codex-cli/src/utils/responses.ts index 99bbd7eeeb..6a763ebfa0 100644 --- a/codex-cli/src/utils/responses.ts +++ b/codex-cli/src/utils/responses.ts @@ -275,8 +275,8 @@ const createCompletion = (openai: OpenAI, input: ResponseCreateInput) => { messages: fullMessages, tools: chatTools, web_search_options: webSearchOptions, - temperature: input.temperature ?? 1.0, - top_p: input.top_p ?? 1.0, + temperature: input.temperature, + top_p: input.top_p, tool_choice: (input.tool_choice === "auto" ? "auto" : input.tool_choice) as OpenAI.Chat.Completions.ChatCompletionCreateParams["tool_choice"], @@ -385,11 +385,11 @@ async function nonStreamResponses( parallel_tool_calls: input.parallel_tool_calls ?? false, previous_response_id: input.previous_response_id ?? null, reasoning: null, - temperature: input.temperature ?? 1.0, + temperature: input.temperature, text: { format: { type: "text" } }, tool_choice: input.tool_choice ?? "auto", tools: input.tools ?? [], - top_p: input.top_p ?? 1.0, + top_p: input.top_p, truncation: input.truncation ?? "disabled", usage: chatResponse.usage ? { @@ -471,11 +471,11 @@ async function* streamResponses( parallel_tool_calls: true, previous_response_id: input.previous_response_id ?? null, reasoning: null, - temperature: input.temperature ?? 1.0, + temperature: input.temperature, text: { format: { type: "text" } }, tool_choice: input.tool_choice ?? "auto", tools: input.tools ?? [], - top_p: input.top_p ?? 1.0, + top_p: input.top_p, truncation: input.truncation ?? "disabled", usage: undefined, user: input.user ?? undefined, @@ -652,11 +652,11 @@ async function* streamResponses( parallel_tool_calls: true, previous_response_id: input.previous_response_id ?? null, reasoning: null, - temperature: input.temperature ?? 1.0, + temperature: input.temperature, text: { format: { type: "text" } }, tool_choice: input.tool_choice ?? "auto", tools: input.tools ?? [], - top_p: input.top_p ?? 1.0, + top_p: input.top_p, truncation: input.truncation ?? "disabled", usage: usage as ResponseOutput["usage"], user: input.user ?? undefined, From cbeb5c30574a414961c44df697453484a7d264dd Mon Sep 17 00:00:00 2001 From: Connor Christie Date: Tue, 22 Apr 2025 22:08:52 -0700 Subject: [PATCH 79/82] fix: remove unreachable "disableResponseStorage" logic flow introduced in #543 (#573) This PR cleans up unreachable code that was added as a result of https://github.com/openai/codex/pull/543. The code being removed is already being handled above: https://github.com/openai/codex/blob/23f0887df3323a2b2a5bd87780c8845776b56bba/codex-cli/src/utils/agent/agent-loop.ts#L535-L539 --- codex-cli/src/utils/agent/agent-loop.ts | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/codex-cli/src/utils/agent/agent-loop.ts b/codex-cli/src/utils/agent/agent-loop.ts index 8d99e46cb4..aaca48f37a 100644 --- a/codex-cli/src/utils/agent/agent-loop.ts +++ b/codex-cli/src/utils/agent/agent-loop.ts @@ -543,22 +543,6 @@ export class AgentLoop { ); } else { turnInput = [...abortOutputs, ...input].map(stripInternalFields); - - // When response storage is disabled we have to maintain our own - // running transcript so that the next request still contains the - // full conversational history. We skipped the transcript update in - // the branch above – ensure we do it here as well. - if (this.disableResponseStorage) { - const newUserItems: Array = input.filter((it) => { - if (it.type === "message" && it.role === "system") { - return false; - } else if (it.type === "reasoning") { - return false; - } - return true; - }); - this.transcript.push(...newUserItems.map(stripInternalFields)); - } } this.onLoading(true); From b428d66f2b6ce9e4e1df6476c657f8585d05c775 Mon Sep 17 00:00:00 2001 From: Erick Date: Wed, 23 Apr 2025 00:12:18 -0500 Subject: [PATCH 80/82] feat: added provider to run quiet mode function (#571) Adding support to be able to run other models in quiet mode ie: `codex --approval-mode full-auto -q "explain the current directory" --provider xai --model grok-3-beta` --- codex-cli/src/cli.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/codex-cli/src/cli.tsx b/codex-cli/src/cli.tsx index d4982fb161..c0aa728f66 100644 --- a/codex-cli/src/cli.tsx +++ b/codex-cli/src/cli.tsx @@ -489,6 +489,7 @@ async function runQuietMode({ model: config.model, config: config, instructions: config.instructions, + provider: config.provider, approvalPolicy, additionalWritableRoots, disableResponseStorage: config.disableResponseStorage, From 146a61b073ae413407d1135e304f75dd097be7f1 Mon Sep 17 00:00:00 2001 From: kshern Date: Wed, 23 Apr 2025 13:45:56 +0800 Subject: [PATCH 81/82] feat: add support for custom provider configuration in the user config (#537) ### What - Add support for loading and merging custom provider configurations from a local `providers.json` file. - Allow users to override or extend default providers with their own settings. ### Why This change enables users to flexibly customize and extend provider endpoints and API keys without modifying the codebase, making the CLI more adaptable for various LLM backends and enterprise use cases. ### How - Introduced `loadProvidersFromFile` and `getMergedProviders` in config logic. - Added/updated related tests in [tests/config.test.tsx] ### Checklist - [x] Lint passes for changed files - [x] Tests pass for all files - [x] Documentation/comments updated as needed --------- Co-authored-by: Thibault Sottiaux --- .../src/components/chat/terminal-chat.tsx | 1 + codex-cli/src/components/model-overlay.tsx | 3 +- codex-cli/src/utils/config.ts | 33 ++++--- codex-cli/tests/config.test.tsx | 86 +++++++++++++++++++ 4 files changed, 109 insertions(+), 14 deletions(-) diff --git a/codex-cli/src/components/chat/terminal-chat.tsx b/codex-cli/src/components/chat/terminal-chat.tsx index cf304ad77e..6a1cfe1b84 100644 --- a/codex-cli/src/components/chat/terminal-chat.tsx +++ b/codex-cli/src/components/chat/terminal-chat.tsx @@ -572,6 +572,7 @@ export default function TerminalChat({ {overlayMode === "model" && ( { diff --git a/codex-cli/src/components/model-overlay.tsx b/codex-cli/src/components/model-overlay.tsx index ec5e40d50b..c9dde0e6b4 100644 --- a/codex-cli/src/components/model-overlay.tsx +++ b/codex-cli/src/components/model-overlay.tsx @@ -3,7 +3,6 @@ import { getAvailableModels, RECOMMENDED_MODELS as _RECOMMENDED_MODELS, } from "../utils/model-utils.js"; -import { providers } from "../utils/providers.js"; import { Box, Text, useInput } from "ink"; import React, { useEffect, useState } from "react"; @@ -19,6 +18,7 @@ type Props = { currentModel: string; currentProvider?: string; hasLastResponse: boolean; + providers?: Record; onSelect: (model: string) => void; onSelectProvider?: (provider: string) => void; onExit: () => void; @@ -26,6 +26,7 @@ type Props = { export default function ModelOverlay({ currentModel, + providers = {}, currentProvider = "openai", hasLastResponse, onSelect, diff --git a/codex-cli/src/utils/config.ts b/codex-cli/src/utils/config.ts index 91b35956e3..1df2e197c7 100644 --- a/codex-cli/src/utils/config.ts +++ b/codex-cli/src/utils/config.ts @@ -42,33 +42,33 @@ export function setApiKey(apiKey: string): void { } export function getBaseUrl(provider: string = "openai"): string | undefined { - // If the provider is `openai` and `OPENAI_BASE_URL` is set, use it - if (provider === "openai" && OPENAI_BASE_URL !== "") { - return OPENAI_BASE_URL; - } - - // Check for a PROVIDER-specific override: e.g. OLLAMA_BASE_URL + // Check for a PROVIDER-specific override: e.g. OPENAI_BASE_URL or OLLAMA_BASE_URL. const envKey = `${provider.toUpperCase()}_BASE_URL`; if (process.env[envKey]) { return process.env[envKey]; } - // Use the default URL from providers if available - const providerInfo = providers[provider.toLowerCase()]; + // Get providers config from config file. + const config = loadConfig(); + const providersConfig = config.providers ?? providers; + const providerInfo = providersConfig[provider.toLowerCase()]; if (providerInfo) { return providerInfo.baseURL; } - // If the provider not found in the providers list and `OPENAI_BASE_URL` is set, use it + // If the provider not found in the providers list and `OPENAI_BASE_URL` is set, use it. if (OPENAI_BASE_URL !== "") { return OPENAI_BASE_URL; } + // We tried. return undefined; } export function getApiKey(provider: string = "openai"): string | undefined { - const providerInfo = providers[provider.toLowerCase()]; + const config = loadConfig(); + const providersConfig = config.providers ?? providers; + const providerInfo = providersConfig[provider.toLowerCase()]; if (providerInfo) { if (providerInfo.name === "Ollama") { return process.env[providerInfo.envKey] ?? "dummy"; @@ -81,12 +81,10 @@ export function getApiKey(provider: string = "openai"): string | undefined { return OPENAI_API_KEY; } + // We tried. return undefined; } -// Formatting (quiet mode-only). -export const PRETTY_PRINT = Boolean(process.env["PRETTY_PRINT"] || ""); - // Represents config as persisted in config.json. export type StoredConfig = { model?: string; @@ -98,6 +96,7 @@ export type StoredConfig = { notify?: boolean; /** Disable server-side response storage (send full transcript each request) */ disableResponseStorage?: boolean; + providers?: Record; history?: { maxSize?: number; saveHistory?: boolean; @@ -134,6 +133,7 @@ export type AppConfig = { /** Enable the "flex-mode" processing mode for supported models (o3, o4-mini) */ flexMode?: boolean; + providers?: Record; history?: { maxSize: number; saveHistory: boolean; @@ -141,6 +141,9 @@ export type AppConfig = { }; }; +// Formatting (quiet mode-only). +export const PRETTY_PRINT = Boolean(process.env["PRETTY_PRINT"] || ""); + // --------------------------------------------------------------------------- // Project doc support (codex.md) // --------------------------------------------------------------------------- @@ -399,6 +402,9 @@ export const loadConfig = ( }; } + // Merge default providers with user configured providers in the config. + config.providers = { ...providers, ...storedConfig.providers }; + return config; }; @@ -431,6 +437,7 @@ export const saveConfig = ( const configToSave: StoredConfig = { model: config.model, provider: config.provider, + providers: config.providers, approvalMode: config.approvalMode, }; diff --git a/codex-cli/tests/config.test.tsx b/codex-cli/tests/config.test.tsx index 867b957dfc..e94b5b2951 100644 --- a/codex-cli/tests/config.test.tsx +++ b/codex-cli/tests/config.test.tsx @@ -5,6 +5,7 @@ import { AutoApprovalMode } from "../src/utils/auto-approval-mode.js"; import { tmpdir } from "os"; import { join } from "path"; import { test, expect, beforeEach, afterEach, vi } from "vitest"; +import { providers as defaultProviders } from "../src/utils/providers"; // In‑memory FS store let memfs: Record = {}; @@ -148,3 +149,88 @@ test("loads and saves approvalMode correctly", () => { }); expect(reloadedConfig.approvalMode).toBe(AutoApprovalMode.FULL_AUTO); }); + +test("loads and saves providers correctly", () => { + // Setup custom providers configuration + const customProviders = { + openai: { + name: "Custom OpenAI", + baseURL: "https://custom-api.openai.com/v1", + envKey: "CUSTOM_OPENAI_API_KEY", + }, + anthropic: { + name: "Anthropic", + baseURL: "https://api.anthropic.com", + envKey: "ANTHROPIC_API_KEY", + }, + }; + + // Create config with providers + const testConfig = { + model: "test-model", + provider: "anthropic", + providers: customProviders, + instructions: "test instructions", + notify: false, + }; + + // Save the config + saveConfig(testConfig, testConfigPath, testInstructionsPath); + + // Verify saved config contains providers + expect(memfs[testConfigPath]).toContain(`"providers"`); + expect(memfs[testConfigPath]).toContain(`"Custom OpenAI"`); + expect(memfs[testConfigPath]).toContain(`"Anthropic"`); + expect(memfs[testConfigPath]).toContain(`"provider": "anthropic"`); + + // Load config and verify providers were loaded correctly + const loadedConfig = loadConfig(testConfigPath, testInstructionsPath, { + disableProjectDoc: true, + }); + + // Check providers were loaded correctly + expect(loadedConfig.provider).toBe("anthropic"); + expect(loadedConfig.providers).toEqual({ + ...defaultProviders, + ...customProviders, + }); + + // Test merging with built-in providers + // Create a config with only one custom provider + const partialProviders = { + customProvider: { + name: "Custom Provider", + baseURL: "https://custom-api.example.com", + envKey: "CUSTOM_API_KEY", + }, + }; + + const partialConfig = { + model: "test-model", + provider: "customProvider", + providers: partialProviders, + instructions: "test instructions", + notify: false, + }; + + // Save the partial config + saveConfig(partialConfig, testConfigPath, testInstructionsPath); + + // Load config and verify providers were merged with built-in providers + const mergedConfig = loadConfig(testConfigPath, testInstructionsPath, { + disableProjectDoc: true, + }); + + // Check providers is defined + expect(mergedConfig.providers).toBeDefined(); + + // Use bracket notation to access properties + if (mergedConfig.providers) { + expect(mergedConfig.providers["customProvider"]).toBeDefined(); + expect(mergedConfig.providers["customProvider"]).toEqual( + partialProviders.customProvider, + ); + // Built-in providers should still be there (like openai) + expect(mergedConfig.providers["openai"]).toBeDefined(); + } +}); From c75cb507f08502af9e28e3de399e89306f50a67f Mon Sep 17 00:00:00 2001 From: Connor Christie Date: Wed, 23 Apr 2025 15:21:00 -0700 Subject: [PATCH 82/82] bug: fix error catching when checking for updates (#597) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes https://github.com/openai/codex/issues/480 where the latest code was crashing when attempting to be run inside docker since the update checker attempts to reach out to `npm.antfu.dev` but that DNS is not allowed in the firewall rules. I believe the original code was attempting to catch and ignore any errors when checking for updates but was doing so incorrectly. If you use await on a promise, you have to use a standard try/catch instead of `Promise.catch` so this fixes that. ## Testing ### Before ``` $ scripts/run_in_container.sh "explain this project to me" 7d1aa845edf9a36fe4d5b331474b5cb8ba79537b682922b554ea677f14996c6b Resolving api.openai.com... Adding 162.159.140.245 for api.openai.com Adding 172.66.0.243 for api.openai.com Host network detected as: 172.17.0.0/24 Firewall configuration complete Verifying firewall rules... Firewall verification passed - unable to reach https://example.com as expected Firewall verification passed - able to reach https://api.openai.com as expected TypeError: fetch failed at node:internal/deps/undici/undici:13510:13 at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async getLatestVersionBatch (file:///usr/local/share/npm-global/lib/node_modules/@openai/codex/dist/cli.js:132669:17) at async getLatestVersion (file:///usr/local/share/npm-global/lib/node_modules/@openai/codex/dist/cli.js:132674:19) at async getUpdateCheckInfo (file:///usr/local/share/npm-global/lib/node_modules/@openai/codex/dist/cli.js:132748:20) at async checkForUpdates (file:///usr/local/share/npm-global/lib/node_modules/@openai/codex/dist/cli.js:132772:23) at async file:///usr/local/share/npm-global/lib/node_modules/@openai/codex/dist/cli.js:142027:1 { [cause]: AggregateError [ECONNREFUSED]: at internalConnectMultiple (node:net:1122:18) at afterConnectMultiple (node:net:1689:7) { code: 'ECONNREFUSED', [errors]: [ [Error], [Error] ] } } ``` ### After ``` $ scripts/run_in_container.sh "explain this project to me" 91aa716e3d3f86c9cf6013dd567be31b2c44eb5d7ab184d55ef498731020bb8d Resolving api.openai.com... Adding 162.159.140.245 for api.openai.com Adding 172.66.0.243 for api.openai.com Host network detected as: 172.17.0.0/24 Firewall configuration complete Verifying firewall rules... Firewall verification passed - unable to reach https://example.com as expected Firewall verification passed - able to reach https://api.openai.com as expected ╭──────────────────────────────────────────────────────────────╮ │ ● OpenAI Codex (research preview) v0.1.2504221401 │ ╰──────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────╮ │ localhost session: 7c782f196ae04503866e39f071e26a69 │ │ ↳ model: o4-mini │ │ ↳ provider: openai │ │ ↳ approval: full-auto │ ╰──────────────────────────────────────────────────────────────╯ user explain this project to me ╭───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │( ● ) 2s Thinking │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ send q or ctrl+c to exit | send "/clear" to reset | send "/help" for commands | press enter to send | shift+enter for new line — 100% context left ``` --- codex-cli/src/cli.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/codex-cli/src/cli.tsx b/codex-cli/src/cli.tsx index c0aa728f66..1118bfe6df 100644 --- a/codex-cli/src/cli.tsx +++ b/codex-cli/src/cli.tsx @@ -294,7 +294,11 @@ config = { // Check for updates after loading config. This is important because we write state file in // the config dir. -await checkForUpdates().catch(); +try { + await checkForUpdates(); +} catch { + // ignore +} // For --flex-mode, validate and exit if incorrect. if (cli.flags.flexMode) {