chore(types): reduce any usage with safety tooling + 7-batch phase 1 (#2208)

* chore(types): add any-usage baseline + lint budget gate (Phase 0)

Phase 0 of a multi-phase plan to reduce explicit `any` usage safely.
No source files modified in this PR.

- Add eslint@9 + @typescript-eslint deps (lint only, no fix rules).
- New minimal flat eslint.config.js enabling only
  `@typescript-eslint/no-explicit-any` as warn. Stub plugins registered
  for legacy `custom-rules`, `eslint-plugin-n`, and `react-hooks`
  references in existing `// eslint-disable` comments so the comments
  stay valid (no rule name resolution errors).
- New `scripts/any-usage-report.ts`: walks src/, tests/, scripts/ and
  buckets `any` usage into `: any` annotations, `as any` casts,
  `[key: ...]: any` index signatures, `// @ts-ignore` directives, and
  generic `any` args. Emits `reports/any-usage.{json,md}`. `--check`
  mode exits non-zero if the count grows vs the baseline.
- Lock the baseline at 1,031 occurrences across 198 files.
- Add `lint`, `lint:any-budget`, `check:strict` scripts.
- Plan: docs/plans/imperative-hugging-sphinx.md

Verification:
- `bun run typecheck` clean
- `bun run lint` exit 0 (845 warnings, 0 errors)
- `bun run lint:any-budget` ok (current=1031 baseline=1031)

* chore(types): narrow any in 6 small src files (Phase 1, batch 1)

Phase 1 of the plan to reduce `any` usage. Targets only files with
1–2 lint hits where the concrete type is reachable in scope.

- src/services/api/withRetry.ts: isQuotaExhausted signature `any` → `unknown`,
  add local structural narrowing for `.message`/`.status` access.
- src/utils/validation.ts: assertFunction predicate signature `any[]`/`any` →
  `unknown[]`/`unknown`.
- src/utils/conversationArc.ts: extractTextFromContent — replace `(block: any)`
  in filter/map with explicit type predicate narrowing.
- src/utils/optionalRuntimeModule.ts: dynamic-import wrapper returns
  `Promise<unknown>` instead of `Promise<any>`.
- src/entrypoints/mcp.ts: tighten block mapping with the existing structural
  type already in scope; replace `(finalResult as any).isError` with a
  narrow cast.
- src/components/EffortPicker.tsx: drop redundant `: any` on useAppState
  selector — the function's generic infers `AppState` for free.

Verification:
- bun run typecheck clean
- bun run lint: 835 warnings (was 845)
- bun run lint:any-budget: 1022 / 1031 baseline (delta -9)
- bun test src/utils/optionalRuntimeModule.test.ts + conversationArc.test.ts:
  36 pass / 0 fail
- bun test src/services/api/withRetry.test.ts: 44 pass / 0 fail

* chore(types): narrow any in WebSearchTool + adapters (Phase 1, batch 2)

Phase 1 continues. These are all boundary code (HTTP responses from
external search APIs), so every `any` is at a module boundary per the
plan. Each adapter now types its response with `unknown` + structural
narrowing instead of `any`.

- providers/types.ts: firstMatch/normalizeHit accept `unknown` and narrow
  to `Record<string, unknown>` before index access.
- providers/timeout.ts: fetchJsonWithWebSearchTimeout returns
  Promise<unknown>.
- providers/{bing,brave,jina,linkup,mojeek,tavily,you,exa,custom}.ts: each
  adapter's response data is cast to a minimal shape (`{ results?: unknown }`
  etc.), then array elements are narrowed to `Record<string, unknown>` with
  `typeof` checks per field.
- WebSearchTool.ts: getCodexSources and extractCodexWebSearchFailure now
  take `Record<string, unknown>`; nested `action`/`error`/`result` are
  cast locally to `Record<string, unknown>` so `?.field` chains type-check.

Verification:
- bun run typecheck clean
- bun run lint: 805 warnings (was 835, -30)
- bun run lint:any-budget: 992 / 1022 baseline
- bun test src/tools/WebSearchTool: 138 pass / 0 fail across 9 files

* chore(types): narrow any in 6 mid-size src files (Phase 1, batch 3)

Phase 1 continues. Each replacement uses a precise structural cast
(`as { uuid?: string }` etc.) or `unknown` with typeof/Array.isArray
narrows at use sites.

- src/utils/settings/types.ts: zod preprocess callback `any` → `unknown`,
  copy via `Record<string, unknown>`.
- src/entrypoints/sdk/v2.ts: initialMessages param/var `any[]` → `Message[]`;
  local cast at the boundary with stripChainFields.
- src/entrypoints/sdk/query.ts: msg `.uuid` access uses
  `as { uuid?: string }`; `fileHistoryCanRestore` etc. accept `UUID` (from
  `crypto`); `mcpServerStatus` accesses `client.error` / `client.config`
  directly since the union members already have them.
- src/commands/cache-probe/cache-probe.ts: getField reduce `any` → `unknown`
  with typeof guard; catch (err: any) → `err: unknown` with
  `err instanceof Error`; mainMakeUsage/mainConvertChunkUsage take `unknown`
  and read fields via typeof checks.
- src/services/compact/snipProjection.ts: snipMetadata / uuid accessed via
  narrow structural casts, no longer `as any`.
- src/utils/messages.ts: tool_use block destructuring for extra_content uses
  `as { extra_content?: unknown; [k: string]: unknown }` instead of
  `as any`.

Public SDK surface (entrypoints/sdk.d.ts, entrypoints/sdk/index.ts
SdkMcpToolDefinition<Schema = any>) is intentionally permissive for
consumer ergonomics and is left untouched per the plan's do-not-touch
list.

Verification:
- bun run typecheck clean
- bun run lint: 782 warnings (was 805, -23)
- bun run lint:any-budget: 969 / 992 baseline
- bun test (sdk v2/factories + compact + messages): 91 pass / 0 fail

* fix(types): correct BetaContentBlock cast in messages.ts (Phase 1 fix)

Verifier caught that the `as { [k: string]: unknown; extra_content?: unknown }`
cast on a `BetaToolUseBlock` did not sufficiently overlap (BetaToolUseBlock
lacks an index signature), so the destructured spread of `restBlock`
yielded a malformed union member that failed to assign to BetaContentBlock[].

Fix: cast through `unknown` first, then assert the final return value as
`BetaContentBlock` (the declared element type of the outer `.map`). This
narrows from the prior `as any` while keeping the previous structural
intent (preserve all block fields, optionally inject `extra_content`).

Verification:
- bun run typecheck clean
- bun test src/utils/messages/: 79 pass / 0 fail

* chore(types): narrow any in 5 dense src + 1 script (Phase 1, batch 4)

Each replacement preserves runtime behavior while using concrete types
where reachable and `unknown` with structural narrowing where not.

- src/components/ClaudeMdExternalIncludesDialog.tsx: 4 helper updaters
  (acceptProject/User, declineProject/User) take `ProjectConfig` from
  `src/utils/config.ts` instead of `any` (matches the
  `saveCurrentProjectConfig` updater signature).
- src/components/ProviderManager.tsx: env-clearing objects in
  `activateGithubProvider` and the GitHub disable path used
  `undefined as any`. The intent is "delete this key on merge" but the
  zod schema coerces to string. Replaced with
  `undefined as unknown as string` (honest about the lie) and added a
  comment explaining intent.
- src/services/compact/snipCompact.ts: introduced a structural
  `SnipMessage` type for the public function params. Tests construct
  fake messages with `uuid: 'u4'` (not branded UUID), so a strict
  `Message[]` would reject them; the structural type accepts both
  production messages and test fixtures. `snipCompactIfNeeded` is
  generic `<T extends SnipMessage>` so callers get the same shape back.
  Internal content blocks use `unknown` + type-predicate narrowing.
- src/query.ts: yield site for the boundary message casts to `Message`
  since the value is constructed as a real `Message` in production.
- scripts/grpc-cli.ts: proto descriptor, stream, and message callback
  typed as `unknown` / `ClientDuplexStream<unknown, unknown>`. The
  dynamic `.proto` payload is cast once at the boundary into a
  structural shape so per-field access (`text_chunk.text` etc.) is
  tracked explicitly.
- scripts/generate-sdk-types.ts: the JSON-Schema-walking converter
  takes `unknown` everywhere and narrows via typeof / Array.isArray at
  each access. `Map<unknown, string>` for placeholder identity
  comparison, `Set<string | undefined>` no longer used here.

Verification:
- bun run typecheck clean
- bun run lint: 728 warnings (was 782, -54)
- bun run lint:any-budget: 916 / 969 baseline
- bun test src/services/compact: 91 pass / 0 fail

* chore(types): remove 3 eslint-disable no-explicit-any suppressions

Tighten three small call sites where the `any` was used as a lazy
escape hatch and a real type was reachable in scope.

- src/main.tsx: `(global as any).require('inspector')` becomes
  `(global as unknown as { require: (s: string) => { url: () => string } })`.
  The dynamic require is being phased out, but while it remains the
  narrow structural cast documents the shape we actually consume.
- src/ink/reconciler.ts: catch (error: any) → catch (error: unknown);
  `error.code === 'ERR_MODULE_NOT_FOUND'` accesses the code via
  `(error as { code?: unknown }).code`.
- src/components/wizard/WizardProvider.tsx:
  `createContext<WizardContextValue<any> | null>(null)` →
  `createContext<WizardContextValue | null>(null)`. The
  `WizardContextValue` generic already defaults to
  `Record<string, unknown>`, so the `<any>` was redundant; consumers
  that need a tighter type can supply it via
  `useContext<WizardContextValue<T>>`.

Note: most remaining lint warnings come from file-level
`/* eslint-disable @typescript-eslint/no-explicit-any */` blocks at the
top of do-not-touch files (src/types/{message,utils,tools}.ts,
src/constants/querySource.ts, src/entrypoints/sdk/controlTypes.ts) and
from public SDK surface (src/entrypoints/sdk{,.d.ts,v2.ts}). Both are
intentional per the plan.

Verification:
- bun run typecheck clean

* chore(types): refresh any-usage baseline

* fix(types): address PR review feedback on any-reduction tooling

- Pin eslint to resolved version (9.39.5) so lint warning totals stay
  stable across lockfile updates.
- Wire lint:any-budget into the regular `check` gate (was only in
  check:strict), so any-usage regressions block PRs.
- any-usage-report: use lstat and skip symlinks so a self-referencing
  link under src/tests/scripts can't stall the budget scan.
- any-usage-report --check: exit nonzero when the baseline file is
  missing or invalid so the gate can't be silently disabled.
- Remove dangling references to docs/plans/imperative-hugging-sphinx.md
  from eslint.config.js and scripts/any-usage-report.ts headers.
- mcp.ts: defensively skip non-object tool-output entries (primitives
  / null) instead of crashing on boundary data.
- messages.ts: realign the mis-indented tool_search-off branch so the
  blockExtra + return block reads at the right level.

No budget delta (913 -> 913). Typecheck, lint, budget, and the affected
unit tests all green.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore(types): narrow any in src/grpc/server.ts (5 warnings)

- protoDescriptor cast: replace `as any` with a structural type that only
  declares the AgentService shape we actually use.
- handleChat: ServerDuplexStream<any, any> -> <unknown, unknown>; the
  clientMessage is narrowed locally where it's read.
- previousMessages: any[] -> Message[] (already imported, already used
  as the type of this.sessions).
- catch (err: any) -> catch (err); err.message is now gated behind an
  instanceof Error check.

Typecheck, lint, budget, and the 2 grpc unit tests all green.
Lint 728 -> 723 (-5). Budget 913 -> 909 (-4).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore(types): narrow any in src/utils/knowledgeGraph.ts (18 warnings)

Add explicit LegacyEntity / LegacyRelation / LegacySummary / LegacyData
types so the migration path stops round-tripping through `any`.

- LegacySource.data, mergeLegacySources, normalizeLegacyData,
  readLegacySqliteStore, SqliteReadResult.data, and doMigration all take
  the new LegacyData shape.
- LegacyData.entities is typed `Record<string, LegacyEntity> | LegacyEntity[]`
  because older JSON dumps stored entities as an array. normalizeLegacyData
  converts the array form to the record form so downstream code can keep
  using Object.entries. (Old loop relied on the same `id ?? entryKey`
  fallback, so behavior is preserved; verified by all 20 unit tests.)
- SQLite row accessor now returns `Record<string, unknown>` (was `any[]`);
  per-row readers narrow `row.id`, `row.attributes`, `row.keywords`,
  `row.content` from `unknown` and the existing typeof guards preserve
  the previous fail-soft behavior on malformed rows.

Lint 723 -> 705 (-18). Budget 909 -> 894 (-15). All 20 knowledgeGraph
unit tests pass; typecheck clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore(types): narrow SDK tool() generic and handler signature (12 warnings)

The public SDK MCP tool() helper previously defaulted its Schema generic
to `any` and typed handler args as `any`. Switch both to `unknown`:

- SdkMcpToolDefinition<Schema = unknown>
- tool<Schema = unknown> with handler (args: Schema, extra: unknown)
- annotations?: any -> ToolAnnotations (imported from MCP SDK types)

This is a strictly tighter API: callers who pass a concrete schema get
the same behavior; callers who relied on implicit any now have to
validate handler args (which they should have been doing anyway).

All 4 SDK MCP tool wiring tests pass; typecheck clean.
Lint 705 -> 695 (-10). Budget 894 -> 884 (-10).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(types): address PR feedback on SDK + grpc type hardening

Apply 4 of 5 CodeRabbit review items (skipped item 5: SDK resume flow
JSONL validation, deferred as larger refactor).

- sdk.d.ts: switch MCP type imports to the package's public
  `@modelcontextprotocol/sdk/types.js` subpath (was the internal
  dist/esm/types.js path).
- sdk.d.ts: handler return Promise<any> -> Promise<CallToolResult> in
  both SdkMcpToolDefinition and the tool() helper, matching the
  runtime contract and the sibling declarations in sdk/index.ts and
  sdk/v2.ts.
- grpc/server.ts: introduce a local ClientMessage interface (matching
  the openclaude.proto oneof payload) and type the duplex-stream
  request as ServerDuplexStream<ClientMessage, unknown>. The handler
  body was already reading these fields by name; the type now
  documents them.
- grpc/server.ts: ServiceDefinition generic was being passed a
  ServerDuplexStream, which isn't a valid T (T must be a record
  keyed by method name). Switch to ServiceDefinition<{ Chat: never }>
  so the call to addService accepts a real implementation-map key.

Typecheck, lint, budget, and the 6 affected unit tests all green.
Lint 695 -> 693 (-2). Budget 884 -> 882 (-2).

* fix(types): address 6 reviewer findings on PR #2208

Six findings (four P2, two P3) from the second review pass.

1. knowledgeGraph legacy array normalization: previously dropped entries
   whose `id` was neither string nor number, breaking recovery for
   idless array entries. Use the array index as the fallback key so
   mergeLegacySources' existing `id ?? entryKey` logic continues to
   recover the entry. All 20 unit tests still pass.

2. SDK tool() generic conflated schema descriptor with invocation
   value type, breaking the test fixture (and any external consumer)
   that declares the handler with a concrete value type. Restore
   `args: any` for the handler across the factory, SdkMcpToolDefinition,
   and the .d.ts declaration; keep `Schema` as the generic for
   inputSchema typing.

3. sdk.d.ts now declares ToolAnnotations and CallToolResult locally
   instead of importing from @modelcontextprotocol/sdk, preserving
   the optional peer contract. A consumer without MCP installed no
   longer gets TS2307 on a no-op SDKSession type import.

4. any-usage-report now strips comments, string literals, and
   template-literal contents before applying the annotation regex,
   so `// example: any`, `': any'`, and similar non-type occurrences
   no longer count. @ts-ignore / @ts-expect-error / @ts-nocheck
   directives are preserved for the ignore category. Budget went
   from 882 to 776.

5. generate-sdk-types: schemas module cast as Record<string, () => unknown>
   was wrong because HOOK_EVENTS is a non-callable; cast as
   Record<string, unknown> and narrow per-access. def.getter() and
   Reflect.get(schema, 'description') both narrowed to a callable /
   object shape before use. Generated output unchanged (diff empty).

6. any-usage-report walk(): readdir failures now propagate instead
   of being silently swallowed, so a partial scan cannot publish a
   too-low replacement baseline or pass --check.

Typecheck, lint, budget, and 26 affected unit tests all green.

* chore(types): refresh any-usage baseline after upstream rebase

Baseline 776 -> 779. The +3 comes from upstream #2196 (Command Code
gateway) test mocks merged to main after the baseline was recorded:
2x (payload: any) in ProviderManager.test.tsx, 1x 'as any' in
providerProfiles.test.ts. Symptom-level narrowing of another merged
PR's test fixtures is out of scope; re-baseline to the rebased tree.

---------

Co-authored-by: Gravirei <gravirei@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Gravirei
2026-09-21 09:08:34 +08:00
committed by GitHub
co-authored by Gravirei Claude Opus 4.6
parent 35d7edc2f6
commit 6d09260291
42 changed files with 1291 additions and 345 deletions
+181 -4
View File
@@ -29,6 +29,8 @@
"@types/bun": "1.3.11",
"@types/node": "25.5.0",
"@types/react": "19.2.14",
"@typescript-eslint/eslint-plugin": "^8.69.0",
"@typescript-eslint/parser": "^8.69.0",
"ajv": "8.18.0",
"auto-bind": "5.0.1",
"axios": "1.16.0",
@@ -43,6 +45,7 @@
"duck-duck-scrape": "^2.2.7",
"emoji-regex": "10.6.0",
"env-paths": "3.0.0",
"eslint": "9",
"execa": "9.6.1",
"fflate": "0.8.2",
"figures": "6.1.0",
@@ -234,12 +237,40 @@
"@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="],
"@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.10.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg=="],
"@eslint-community/regexpp": ["@eslint-community/regexpp@4.12.2", "", {}, "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew=="],
"@eslint/config-array": ["@eslint/config-array@0.21.2", "", { "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.5" } }, "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw=="],
"@eslint/config-helpers": ["@eslint/config-helpers@0.4.2", "", { "dependencies": { "@eslint/core": "^0.17.0" } }, "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw=="],
"@eslint/core": ["@eslint/core@0.17.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ=="],
"@eslint/eslintrc": ["@eslint/eslintrc@3.3.7", "", { "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.3.2", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" } }, "sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw=="],
"@eslint/js": ["@eslint/js@9.39.5", "", {}, "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A=="],
"@eslint/object-schema": ["@eslint/object-schema@2.1.7", "", {}, "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA=="],
"@eslint/plugin-kit": ["@eslint/plugin-kit@0.4.1", "", { "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" } }, "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA=="],
"@grpc/grpc-js": ["@grpc/grpc-js@1.14.3", "", { "dependencies": { "@grpc/proto-loader": "^0.8.0", "@js-sdsl/ordered-map": "^4.4.2" } }, "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA=="],
"@grpc/proto-loader": ["@grpc/proto-loader@0.8.1", "", { "dependencies": { "lodash.camelcase": "^4.3.0", "long": "^5.0.0", "protobufjs": "^7.5.5", "yargs": "^17.7.2" }, "bin": { "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" } }, "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg=="],
"@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="],
"@humanfs/core": ["@humanfs/core@0.19.2", "", { "dependencies": { "@humanfs/types": "^0.15.0" } }, "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA=="],
"@humanfs/node": ["@humanfs/node@0.16.8", "", { "dependencies": { "@humanfs/core": "^0.19.2", "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" } }, "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ=="],
"@humanfs/types": ["@humanfs/types@0.15.0", "", {}, "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q=="],
"@humanwhocodes/module-importer": ["@humanwhocodes/module-importer@1.0.1", "", {}, "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA=="],
"@humanwhocodes/retry": ["@humanwhocodes/retry@0.4.3", "", {}, "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="],
"@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="],
"@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="],
@@ -506,10 +537,32 @@
"@types/estree": ["@types/estree@1.0.9", "", {}, "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg=="],
"@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="],
"@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
"@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.69.0", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.69.0", "@typescript-eslint/type-utils": "8.69.0", "@typescript-eslint/utils": "8.69.0", "@typescript-eslint/visitor-keys": "8.69.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.69.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-t5jQTKPIgVW1PE6dR6H6Qz5gm8zjMlX5/2gRaOGd9eO6V7J+tQc6iWKukEe7dY8u9HyYasQ0yfF0/FSSTEO2gA=="],
"@typescript-eslint/parser": ["@typescript-eslint/parser@8.69.0", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.69.0", "@typescript-eslint/types": "8.69.0", "@typescript-eslint/typescript-estree": "8.69.0", "@typescript-eslint/visitor-keys": "8.69.0", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-l4b0DhWioGg6Gt2ebGlvfkFMOjRsauxtsnDRwUSRX1qHq3HdTfQHV8wW9zEXeciai6HfeaKOedQn2Zoofx3WBw=="],
"@typescript-eslint/project-service": ["@typescript-eslint/project-service@8.69.0", "", { "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.69.0", "@typescript-eslint/types": "^8.69.0", "debug": "^4.4.3" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-yi4obFrHMmnsesWehHbkg9zMA7Jt8cXT+mKM08G999pH1yT6nqgsHx7MYm0uY1wAj8CqiBXYRJ7WAT0QdQHQXg=="],
"@typescript-eslint/scope-manager": ["@typescript-eslint/scope-manager@8.69.0", "", { "dependencies": { "@typescript-eslint/types": "8.69.0", "@typescript-eslint/visitor-keys": "8.69.0" } }, "sha512-ewfspqWvSxKSOaplqAUNbaSFO0eB6w1EtQ+esfYFRm3614Ty4uNtExkcbgd6nWsXphbqKyf9ZYdbZdv2xEoWEQ=="],
"@typescript-eslint/tsconfig-utils": ["@typescript-eslint/tsconfig-utils@8.69.0", "", { "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-xNqK7YTDZsLniQMV/4rpFR8Z5JlqeRvVjuG1YgF/mdPVH84HSD19L8CczMA0qg2RfwEV231GHH3VnToJDo4MfQ=="],
"@typescript-eslint/type-utils": ["@typescript-eslint/type-utils@8.69.0", "", { "dependencies": { "@typescript-eslint/types": "8.69.0", "@typescript-eslint/typescript-estree": "8.69.0", "@typescript-eslint/utils": "8.69.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-ZfoJAVg3JZndQEpEl9petVlxau3lRuElc4HRMuAlLCf8to04/iHz692RUSNmXKDjEuJmIL+KZ2/BsOcBc16dsA=="],
"@typescript-eslint/types": ["@typescript-eslint/types@8.69.0", "", {}, "sha512-K3VrubUPhlo9VDBS6QdI8YB5j7ClpqLRdefcz6PFrhnwicehBweqQ9Evhl4l+FYz0HdDmMqIiSX0aldGRYtDCA=="],
"@typescript-eslint/typescript-estree": ["@typescript-eslint/typescript-estree@8.69.0", "", { "dependencies": { "@typescript-eslint/project-service": "8.69.0", "@typescript-eslint/tsconfig-utils": "8.69.0", "@typescript-eslint/types": "8.69.0", "@typescript-eslint/visitor-keys": "8.69.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.5.0" }, "peerDependencies": { "typescript": ">=4.8.4 <6.1.0" } }, "sha512-AdFkgqck3Vudb/kWnxlyafU/4aBhHrbQ9locP2N4psXTy5mOBg0SHJumnLvx7r6g1gV4DKvUFwV2nJZBoqOD8w=="],
"@typescript-eslint/utils": ["@typescript-eslint/utils@8.69.0", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.69.0", "@typescript-eslint/types": "8.69.0", "@typescript-eslint/typescript-estree": "8.69.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-tUbx60BBqQa31kXF5MCsOOLL5E/WzUuxIn7YpAvq+eaUlqvk8/NXnXMBNAdLCr0icjkzem7iUA5QqWHe/hJ1aw=="],
"@typescript-eslint/visitor-keys": ["@typescript-eslint/visitor-keys@8.69.0", "", { "dependencies": { "@typescript-eslint/types": "8.69.0", "eslint-visitor-keys": "^5.0.0" } }, "sha512-+rmdgPA+EXkNgKYvHvFfhrs35utXbwaC5PGpDquSXcoXQDKUA5UjV0LmTucG/4JXkM31BTu4TilHtrN8IVBe8w=="],
"@typespec/ts-http-runtime": ["@typespec/ts-http-runtime@0.3.6", "", { "dependencies": { "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" } }, "sha512-jIXhD0eWQ1JA6ln/5Dltyx22UxWNrw0hZmhy2rlv6m6KgF7kplHx3g0fzi09lNmTJQRR91OlemYp3xFnvDK9og=="],
"@vscode/ripgrep": ["@vscode/ripgrep@1.18.0", "", { "optionalDependencies": { "@vscode/ripgrep-darwin-arm64": "1.18.0", "@vscode/ripgrep-darwin-x64": "1.18.0", "@vscode/ripgrep-linux-arm": "1.18.0", "@vscode/ripgrep-linux-arm64": "1.18.0", "@vscode/ripgrep-linux-ia32": "1.18.0", "@vscode/ripgrep-linux-ppc64": "1.18.0", "@vscode/ripgrep-linux-riscv64": "1.18.0", "@vscode/ripgrep-linux-s390x": "1.18.0", "@vscode/ripgrep-linux-x64": "1.18.0", "@vscode/ripgrep-win32-arm64": "1.18.0", "@vscode/ripgrep-win32-ia32": "1.18.0", "@vscode/ripgrep-win32-x64": "1.18.0" } }, "sha512-ns5lWe44tSfbTMbVUsyB+I1819PVSw4AdpgK0RNkzfWfwy6+3IUNSxwSrfTno1/oWaS/hERNz+XLWVyga2aJBQ=="],
@@ -542,6 +595,10 @@
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
"acorn": ["acorn@8.18.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ=="],
"acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="],
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
"ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="],
@@ -554,6 +611,8 @@
"any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="],
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"astring": ["astring@1.9.0", "", { "bin": { "astring": "bin/astring" } }, "sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg=="],
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
@@ -562,6 +621,8 @@
"axios": ["axios@1.16.0", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", "proxy-from-env": "^2.1.0" } }, "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
"bidi-js": ["bidi-js@1.0.3", "", { "dependencies": { "require-from-string": "^2.0.2" } }, "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw=="],
@@ -572,6 +633,8 @@
"bowser": ["bowser@2.14.1", "", {}, "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg=="],
"brace-expansion": ["brace-expansion@1.1.18", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw=="],
"buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
"bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
@@ -584,6 +647,8 @@
"call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="],
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
"camelcase": ["camelcase@5.3.1", "", {}, "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="],
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
@@ -606,6 +671,8 @@
"commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="],
"content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="],
@@ -628,6 +695,8 @@
"decamelize": ["decamelize@1.2.0", "", {}, "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA=="],
"deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="],
"default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="],
"default-browser-id": ["default-browser-id@5.0.1", "", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="],
@@ -674,10 +743,24 @@
"escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="],
"escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="],
"eslint": ["eslint@9.39.5", "", { "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.6", "@eslint/js": "9.39.5", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "peerDependencies": { "jiti": "*" }, "optionalPeers": ["jiti"], "bin": { "eslint": "bin/eslint.js" } }, "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw=="],
"eslint-scope": ["eslint-scope@8.4.0", "", { "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" } }, "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg=="],
"eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="],
"espree": ["espree@10.4.0", "", { "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" } }, "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ=="],
"esquery": ["esquery@1.7.0", "", { "dependencies": { "estraverse": "^5.1.0" } }, "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g=="],
"esrecurse": ["esrecurse@4.3.0", "", { "dependencies": { "estraverse": "^5.2.0" } }, "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag=="],
"estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="],
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
"etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="],
"events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
@@ -696,6 +779,10 @@
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
"fast-json-stable-stringify": ["fast-json-stable-stringify@2.1.0", "", {}, "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="],
"fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="],
"fast-uri": ["fast-uri@3.1.2", "", {}, "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ=="],
"fast-xml-builder": ["fast-xml-builder@1.2.0", "", { "dependencies": { "path-expression-matcher": "^1.5.0", "xml-naming": "^0.1.0" } }, "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q=="],
@@ -712,9 +799,15 @@
"figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="],
"file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="],
"finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="],
"find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
"find-up": ["find-up@5.0.0", "", { "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" } }, "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng=="],
"flat-cache": ["flat-cache@4.0.1", "", { "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" } }, "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw=="],
"flatted": ["flatted@3.4.4", "", {}, "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q=="],
"follow-redirects": ["follow-redirects@1.16.0", "", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="],
@@ -748,6 +841,10 @@
"get-tsconfig": ["get-tsconfig@4.14.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA=="],
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
"globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="],
"google-auth-library": ["google-auth-library@10.6.2", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.1.4", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" } }, "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw=="],
"google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
@@ -794,8 +891,12 @@
"ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="],
"import-fresh": ["import-fresh@3.3.1", "", { "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" } }, "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ=="],
"import-in-the-middle": ["import-in-the-middle@3.3.3", "", { "dependencies": { "cjs-module-lexer": "^2.2.0", "es-module-lexer": "^2.2.0", "module-details-from-path": "^1.0.4" } }, "sha512-AiohS3H80sXO6owEltjGX+glb7qXaDhBoJb9XcQVH4UI207xu/bDLUcadVKp7Qe576reg9yr/PXZjV5qx8gfbA=="],
"imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="],
"indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="],
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
@@ -806,8 +907,12 @@
"is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
"is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="],
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
"is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
"is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
@@ -828,14 +933,20 @@
"js-tiktoken": ["js-tiktoken@1.0.21", "", { "dependencies": { "base64-js": "^1.5.1" } }, "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g=="],
"js-yaml": ["js-yaml@4.3.2", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA=="],
"json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
"json-buffer": ["json-buffer@3.0.1", "", {}, "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="],
"json-schema-to-ts": ["json-schema-to-ts@3.1.1", "", { "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" } }, "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g=="],
"json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
"json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="],
"json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="],
"jsonc-parser": ["jsonc-parser@3.3.1", "", {}, "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="],
"jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="],
@@ -844,9 +955,13 @@
"jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="],
"keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="],
"knip": ["knip@6.16.1", "", { "dependencies": { "fdir": "^6.5.0", "formatly": "^0.3.0", "get-tsconfig": "4.14.0", "jiti": "^2.7.0", "oxc-parser": "^0.133.0", "oxc-resolver": "^11.20.0", "picomatch": "^4.0.4", "smol-toml": "^1.6.1", "strip-json-comments": "5.0.3", "tinyglobby": "^0.2.16", "unbash": "^3.0.0", "yaml": "^2.9.0", "zod": "^4.1.11" }, "bin": { "knip": "bin/knip.js", "knip-bun": "bin/knip-bun.js" } }, "sha512-TKMn1rxgH6h9vXR9Y0B+Cq7AdPTr9EI02IwoT65NzqYUkvoDQAaJ/aPybiFpAhZ1px6cNYYwXf86iHkBgzCo9w=="],
"locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
"levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="],
"locate-path": ["locate-path@6.0.0", "", { "dependencies": { "p-locate": "^5.0.0" } }, "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw=="],
"lodash-es": ["lodash-es@4.18.1", "", {}, "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A=="],
@@ -866,6 +981,8 @@
"lodash.isstring": ["lodash.isstring@4.0.1", "", {}, "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="],
"lodash.merge": ["lodash.merge@4.6.2", "", {}, "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ=="],
"lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="],
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
@@ -888,6 +1005,8 @@
"mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="],
"minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
"mnemonist": ["mnemonist@0.39.8", "", { "dependencies": { "obliterator": "^2.0.1" } }, "sha512-vyWo2K3fjrUw8YeeZ1zF0fy6Mu59RHokURlld8ymdUPjMlD9EC9ov1/YPqTgqRvUN9nTr3Gqfz29LYAmu0PHPQ=="],
"module-details-from-path": ["module-details-from-path@1.0.4", "", {}, "sha512-EGWKgxALGMgzvxYF1UyGTy0HXX/2vHLkw6+NvDKW2jypWbHpjQuj4UMcqQWXHERJhVGKikolT06G3bcKe4fi7w=="],
@@ -896,6 +1015,8 @@
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
"needle": ["needle@3.5.0", "", { "dependencies": { "iconv-lite": "^0.6.3", "sax": "^1.2.4" }, "bin": { "needle": "bin/needle" } }, "sha512-jaQyPKKk2YokHrEg+vFDYxXIHTCBgiZwSHOoVx/8V3GIBS8/VN6NdVRmg8q1ERtPkMvmOvebsgga4sAj5hls/w=="],
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
@@ -918,13 +1039,15 @@
"open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
"optionator": ["optionator@0.9.4", "", { "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" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="],
"oxc-parser": ["oxc-parser@0.133.0", "", { "dependencies": { "@oxc-project/types": "^0.133.0" }, "optionalDependencies": { "@oxc-parser/binding-android-arm-eabi": "0.133.0", "@oxc-parser/binding-android-arm64": "0.133.0", "@oxc-parser/binding-darwin-arm64": "0.133.0", "@oxc-parser/binding-darwin-x64": "0.133.0", "@oxc-parser/binding-freebsd-x64": "0.133.0", "@oxc-parser/binding-linux-arm-gnueabihf": "0.133.0", "@oxc-parser/binding-linux-arm-musleabihf": "0.133.0", "@oxc-parser/binding-linux-arm64-gnu": "0.133.0", "@oxc-parser/binding-linux-arm64-musl": "0.133.0", "@oxc-parser/binding-linux-ppc64-gnu": "0.133.0", "@oxc-parser/binding-linux-riscv64-gnu": "0.133.0", "@oxc-parser/binding-linux-riscv64-musl": "0.133.0", "@oxc-parser/binding-linux-s390x-gnu": "0.133.0", "@oxc-parser/binding-linux-x64-gnu": "0.133.0", "@oxc-parser/binding-linux-x64-musl": "0.133.0", "@oxc-parser/binding-openharmony-arm64": "0.133.0", "@oxc-parser/binding-wasm32-wasi": "0.133.0", "@oxc-parser/binding-win32-arm64-msvc": "0.133.0", "@oxc-parser/binding-win32-ia32-msvc": "0.133.0", "@oxc-parser/binding-win32-x64-msvc": "0.133.0" } }, "sha512-661RSx+ZcjBmjBYid+Fpp/2F5EbtildpeoZh5HdgnGs+jZ03nqQEQW8yGkt4BGyOC3OMPDQQRl8M5kqD2/g6jw=="],
"oxc-resolver": ["oxc-resolver@11.20.0", "", { "optionalDependencies": { "@oxc-resolver/binding-android-arm-eabi": "11.20.0", "@oxc-resolver/binding-android-arm64": "11.20.0", "@oxc-resolver/binding-darwin-arm64": "11.20.0", "@oxc-resolver/binding-darwin-x64": "11.20.0", "@oxc-resolver/binding-freebsd-x64": "11.20.0", "@oxc-resolver/binding-linux-arm-gnueabihf": "11.20.0", "@oxc-resolver/binding-linux-arm-musleabihf": "11.20.0", "@oxc-resolver/binding-linux-arm64-gnu": "11.20.0", "@oxc-resolver/binding-linux-arm64-musl": "11.20.0", "@oxc-resolver/binding-linux-ppc64-gnu": "11.20.0", "@oxc-resolver/binding-linux-riscv64-gnu": "11.20.0", "@oxc-resolver/binding-linux-riscv64-musl": "11.20.0", "@oxc-resolver/binding-linux-s390x-gnu": "11.20.0", "@oxc-resolver/binding-linux-x64-gnu": "11.20.0", "@oxc-resolver/binding-linux-x64-musl": "11.20.0", "@oxc-resolver/binding-openharmony-arm64": "11.20.0", "@oxc-resolver/binding-wasm32-wasi": "11.20.0", "@oxc-resolver/binding-win32-arm64-msvc": "11.20.0", "@oxc-resolver/binding-win32-x64-msvc": "11.20.0" } }, "sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g=="],
"p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
"p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="],
"p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
"p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="],
"p-map": ["p-map@7.0.4", "", {}, "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ=="],
@@ -932,6 +1055,8 @@
"pandemonium": ["pandemonium@2.4.1", "", { "dependencies": { "mnemonist": "^0.39.2" } }, "sha512-wRqjisUyiUfXowgm7MFH2rwJzKIr20rca5FsHXCMNm1W5YPP1hCtrZfgmQ62kP7OZ7Xt+cR858aB28lu5NX55g=="],
"parent-module": ["parent-module@1.0.1", "", { "dependencies": { "callsites": "^3.0.0" } }, "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g=="],
"parse-ms": ["parse-ms@4.0.0", "", {}, "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw=="],
"parse5": ["parse5@5.1.1", "", {}, "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug=="],
@@ -954,6 +1079,8 @@
"pngjs": ["pngjs@5.0.0", "", {}, "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw=="],
"prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="],
"pretty-ms": ["pretty-ms@9.3.0", "", { "dependencies": { "parse-ms": "^4.0.0" } }, "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ=="],
"proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="],
@@ -964,6 +1091,8 @@
"proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
"qrcode": ["qrcode@1.5.4", "", { "dependencies": { "dijkstrajs": "^1.0.1", "pngjs": "^5.0.0", "yargs": "^15.3.1" }, "bin": { "qrcode": "bin/qrcode" } }, "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg=="],
"qs": ["qs@6.15.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg=="],
@@ -988,6 +1117,8 @@
"require-main-filename": ["require-main-filename@2.0.0", "", {}, "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="],
"resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="],
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
"retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="],
@@ -1070,10 +1201,14 @@
"ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="],
"ts-api-utils": ["ts-api-utils@2.5.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"turndown": ["turndown@7.2.2", "", { "dependencies": { "@mixmark-io/domino": "^2.2.0" } }, "sha512-1F7db8BiExOKxjSMU2b7if62D/XOyQyZbPKq/nUwopfgnHlqXHqQ0lvfUTeUIr1lZJzOPFn43dODyMSIfvWRKQ=="],
"type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="],
"type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="],
"type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="],
@@ -1090,6 +1225,8 @@
"unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="],
"uri-js": ["uri-js@4.4.1", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="],
"usehooks-ts": ["usehooks-ts@3.1.1", "", { "dependencies": { "lodash.debounce": "^4.0.8" }, "peerDependencies": { "react": "^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc" } }, "sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA=="],
"vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="],
@@ -1110,6 +1247,8 @@
"which-module": ["which-module@2.0.1", "", {}, "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="],
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
"wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="],
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
@@ -1130,6 +1269,8 @@
"yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="],
"yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="],
"yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="],
"zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
@@ -1228,6 +1369,14 @@
"@aws-sdk/xml-builder/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="],
"@eslint/eslintrc/ajv": ["ajv@6.15.0", "", { "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" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="],
"@eslint/eslintrc/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
"@eslint/eslintrc/strip-json-comments": ["strip-json-comments@3.1.1", "", {}, "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig=="],
"@smithy/core/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
"@smithy/credential-provider-imds/@smithy/types": ["@smithy/types@4.14.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw=="],
@@ -1262,6 +1411,10 @@
"@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@3.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^3.0.0", "tslib": "^2.6.2" } }, "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA=="],
"@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.6", "", { "dependencies": { "brace-expansion": "^5.0.8" } }, "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A=="],
"@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@5.0.1", "", {}, "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA=="],
"cli-highlight/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"cli-highlight/yargs": ["yargs@16.2.0", "", { "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" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="],
@@ -1272,6 +1425,12 @@
"cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
"eslint/ajv": ["ajv@6.15.0", "", { "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" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="],
"eslint/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
"eslint/ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
"fetch-blob/node-domexception": ["node-domexception@file:vendor/node-domexception-shim", {}],
"form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
@@ -1308,10 +1467,14 @@
"@aws-sdk/nested-clients/@aws-crypto/sha256-js/@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
"@eslint/eslintrc/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
"@smithy/eventstream-codec/@aws-crypto/crc32/@aws-crypto/util": ["@aws-crypto/util@3.0.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-utf8-browser": "^3.0.0", "tslib": "^1.11.1" } }, "sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w=="],
"@smithy/eventstream-codec/@aws-crypto/crc32/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.9", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg=="],
"cli-highlight/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"cli-highlight/yargs/cliui": ["cliui@7.0.4", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="],
@@ -1328,10 +1491,16 @@
"cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"eslint/ajv/json-schema-traverse": ["json-schema-traverse@0.4.1", "", {}, "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="],
"eslint/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
"qrcode/yargs/cliui": ["cliui@6.0.0", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^6.2.0" } }, "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ=="],
"qrcode/yargs/find-up": ["find-up@4.1.0", "", { "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" } }, "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw=="],
"qrcode/yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="],
"qrcode/yargs/y18n": ["y18n@4.0.3", "", {}, "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="],
@@ -1354,6 +1523,8 @@
"@aws-sdk/nested-clients/@aws-crypto/sha256-js/@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
"@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="],
"cli-highlight/yargs/cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"cli-highlight/yargs/cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
@@ -1368,6 +1539,8 @@
"qrcode/yargs/cliui/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="],
"qrcode/yargs/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="],
"qrcode/yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="],
"qrcode/yargs/string-width/is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
@@ -1386,6 +1559,10 @@
"qrcode/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"qrcode/yargs/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="],
"qrcode/yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"qrcode/yargs/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="],
}
}
+120
View File
@@ -0,0 +1,120 @@
// Minimal flat config — only the rule we need to track `any` usage.
// Part of the phased `any`-reduction work landing on fix/reduce-any-types.
//
// Note: the repo has many existing `// eslint-disable custom-rules/<name>`
// comments referencing rules from a previous (now-removed) custom plugin.
// We register a stub `custom-rules` plugin whose rules are all "off"; this
// makes the comments valid so eslint doesn't error on unknown rule names.
import tseslint from "@typescript-eslint/eslint-plugin";
import tsparser from "@typescript-eslint/parser";
const DEPRECATED_CUSTOM_RULE_NAMES = [
"bootstrap-isolation",
"no-cross-platform-process-issues",
"no-direct-json-operations",
"no-direct-ps-commands",
"no-lookbehind-regex",
"no-process-cwd",
"no-process-env-top-level",
"no-process-exit",
"no-sync-fs",
"no-top-level-dynamic-import",
"no-top-level-side-effects",
"prefer-use-keybindings",
"prefer-use-terminal-size",
"prompt-spacing",
"require-bun-typeof-guard",
"require-tool-match-name",
"safe-env-boolean-check",
];
// Stub plugin: each rule is a no-op listener so eslint resolves the name
// but does nothing.
const stubRules = Object.fromEntries(
DEPRECATED_CUSTOM_RULE_NAMES.map((name) => [
name,
{
meta: { schema: [], deprecated: true },
create() {
return {};
},
},
]),
);
/** @type {import("eslint").ESLint.Plugin} */
const customRulesPlugin = {
rules: stubRules,
};
// Stub plugin for legacy `eslint-plugin-n/*` references in source comments.
const eslintPluginNPlugin = {
rules: {
"no-unsupported-features/node-builtins": {
meta: { schema: [], deprecated: true },
create() {
return {};
},
},
},
};
// Stub plugin for legacy `react-hooks/*` references in source comments.
const reactHooksPlugin = {
rules: {
"exhaustive-deps": {
meta: { schema: [], deprecated: true },
create() {
return {};
},
},
"rules-of-hooks": {
meta: { schema: [], deprecated: true },
create() {
return {};
},
},
},
};
export default [
{
ignores: [
"dist/**",
"node_modules/**",
"vendor/**",
"web/**",
"vscode-extension/**",
"coverage/**",
"reports/**",
],
},
{
files: ["src/**/*.{ts,tsx}", "tests/**/*.{ts,tsx}", "scripts/**/*.ts"],
languageOptions: {
parser: tsparser,
parserOptions: {
ecmaVersion: 2023,
sourceType: "module",
ecmaFeatures: { jsx: true },
},
},
plugins: {
"@typescript-eslint": tseslint,
"custom-rules": customRulesPlugin,
"eslint-plugin-n": eslintPluginNPlugin,
"react-hooks": reactHooksPlugin,
},
linterOptions: {
// Source has many `// eslint-disable` comments that may now refer to
// rules not enabled here; don't fail on unused directives.
reportUnusedDisableDirectives: "off",
},
rules: {
// Flags both `: any` annotations and `as any` casts.
// warn (not error) so existing code keeps building while we
// surface counts and reduce incrementally.
"@typescript-eslint/no-explicit-any": "warn",
},
},
];
+11 -5
View File
@@ -65,7 +65,10 @@
"typecheck:type-tests": "bun run scripts/typecheck-type-tests.ts",
"smoke": "bun run build && node dist/cli.mjs --version",
"deadcode": "knip --include files,dependencies",
"check": "bun run smoke && bun run deadcode && bun run test:full",
"lint": "eslint .",
"lint:any-budget": "bun run scripts/any-usage-report.ts --check",
"check": "bun run lint:any-budget && bun run smoke && bun run deadcode && bun run test:full",
"check:strict": "bun run typecheck && bun run lint && bun run lint:any-budget && bun run smoke && bun run deadcode && bun run test:full",
"verify:privacy": "bun run scripts/verify-no-phone-home.ts",
"install:verify": "bun run scripts/verify-clean-install.ts",
"install:verify:published": "bun run scripts/verify-clean-install.ts --published",
@@ -86,7 +89,6 @@
"devDependencies": {
"@alcalzone/ansi-tokenize": "0.3.0",
"@anthropic-ai/bedrock-sdk": "0.29.1",
"@sentry/node": "^10.70.0",
"@anthropic-ai/foundry-sdk": "0.2.3",
"@anthropic-ai/sandbox-runtime": "0.0.55",
"@anthropic-ai/sdk": "0.94.0",
@@ -98,11 +100,14 @@
"@grpc/grpc-js": "^1.14.3",
"@grpc/proto-loader": "^0.8.0",
"@modelcontextprotocol/sdk": "1.29.0",
"@sentry/node": "^10.70.0",
"@smithy/core": "3.24.3",
"@smithy/node-http-handler": "4.7.3",
"@types/bun": "1.3.11",
"@types/node": "25.5.0",
"@types/react": "19.2.14",
"@typescript-eslint/eslint-plugin": "^8.69.0",
"@typescript-eslint/parser": "^8.69.0",
"ajv": "8.18.0",
"auto-bind": "5.0.1",
"axios": "1.16.0",
@@ -117,16 +122,17 @@
"duck-duck-scrape": "^2.2.7",
"emoji-regex": "10.6.0",
"env-paths": "3.0.0",
"eslint": "9.39.5",
"execa": "9.6.1",
"fflate": "0.8.2",
"figures": "6.1.0",
"fuse.js": "7.1.0",
"graphology": "0.26.0",
"get-east-asian-width": "1.5.0",
"google-auth-library": "10.6.2",
"graphology": "0.26.0",
"graphology-metrics": "2.4.0",
"https-proxy-agent": "7.0.6",
"ignore": "7.0.5",
"graphology-metrics": "2.4.0",
"indent-string": "5.0.0",
"js-tiktoken": "1.0.21",
"jsonc-parser": "3.3.1",
@@ -153,8 +159,8 @@
"typescript": "5.9.3",
"undici": "7.28.0",
"usehooks-ts": "3.1.1",
"web-tree-sitter": "0.25.10",
"vscode-languageserver-protocol": "3.17.5",
"web-tree-sitter": "0.25.10",
"wrap-ansi": "9.0.2",
"ws": "8.21.0",
"xss": "1.0.15",
+4
View File
@@ -0,0 +1,4 @@
{
"total": 779,
"generatedAt": "2026-09-06T02:24:17.929Z"
}
+331
View File
@@ -0,0 +1,331 @@
// Walk src/, tests/, scripts/ and report `any` usage by category.
// Produces reports/any-usage.{json,md} and a budget gate against
// reports/any-usage.baseline.json.
//
// Part of the phased `any`-reduction work landing on fix/reduce-any-types.
import { mkdir, readFile, writeFile } from 'fs/promises'
import { resolve } from 'path'
const ROOT = process.cwd()
const REPORT_DIR = resolve(ROOT, 'reports')
const JSON_OUT = resolve(REPORT_DIR, 'any-usage.json')
const MD_OUT = resolve(REPORT_DIR, 'any-usage.md')
const BASELINE_PATH = resolve(REPORT_DIR, 'any-usage.baseline.json')
const SCAN_DIRS = ['src', 'tests', 'scripts']
const EXCLUDE_DIR_NAMES = new Set([
'node_modules',
'dist',
'vendor',
'web',
'vscode-extension',
'coverage',
'reports',
])
type CategoryCounts = {
annotation: number
cast: number
indexSig: number
ignore: number
genericArg: number
}
type FileReport = {
path: string
total: number
byCategory: CategoryCounts
}
type Report = {
generatedAt: string
total: number
byCategory: CategoryCounts
files: FileReport[]
}
// Regexes (each match counted; multi-line `m`, no `g` so we don't loop with /lastIndex).
// Match `any` as a complete identifier (avoid `many`, `company`, etc.).
const ANNOTATION_RE = /(?<![A-Za-z0-9_$])any(?![A-Za-z0-9_$])/m
// `: any` annotation including optional whitespace.
const ANNOTATION_NEAR_RE = /:\s*(?<![A-Za-z0-9_$.])any(?![A-Za-z0-9_$])/m
const CAST_RE = /\bas\s+(?<![A-Za-z0-9_$.])any(?![A-Za-z0-9_$])/m
const INDEX_SIG_RE = /\[\s*(?:k?e?y?[^:\]]*:\s*[^:\]]*)?:\s*(?<![A-Za-z0-9_$.])any(?![A-Za-z0-9_$])/m
const IGNORE_RE = /\/\/\s*@(ts-ignore|ts-expect-error|ts-nocheck)\b/m
const GENERIC_RE = /<[^>]*\bany\b[^>]*>/m
function emptyCategory(): CategoryCounts {
return { annotation: 0, cast: 0, indexSig: 0, ignore: 0, genericArg: 0 }
}
async function walk(dir: string, out: string[]): Promise<void> {
const { readdir, lstat } = await import('fs/promises')
let entries: string[]
try {
entries = await readdir(dir)
} catch (err) {
// Propagate enumeration failures so a partial scan doesn't silently
// report a too-low count. The caller wraps this in a try and exits
// nonzero with the original error.
throw new Error(`readdir failed for ${dir}: ${err instanceof Error ? err.message : String(err)}`)
}
for (const name of entries) {
if (EXCLUDE_DIR_NAMES.has(name)) continue
const full = resolve(dir, name)
// Use lstat so we don't follow symlinks — a self-referencing link would
// recurse forever and stall the budget check.
const st = await lstat(full)
if (st.isSymbolicLink()) continue
if (st.isDirectory()) {
await walk(full, out)
} else if (/\.tsx?$/.test(name)) {
out.push(full)
}
}
}
// Replace the contents of comments, strings, and template literals with
// spaces of equal length so the type-annotation regex only matches real
// TypeScript code. Newlines and offsets are preserved so report line numbers
// stay correct.
function stripNonCode(source: string): string {
let out = ''
let i = 0
const n = source.length
while (i < n) {
const c = source[i]
const c2 = i + 1 < n ? source[i + 1] : ''
// Block comment /* ... */ — preserve @ts-ignore markers in IGNORE_RE
// by NOT stripping the directive prefix; IGNORE_RE only matches
// `// @ts-...` (line) so block comments are safe to fully strip.
if (c === '/' && c2 === '*') {
const end = source.indexOf('*/', i + 2)
if (end === -1) {
out += ' '.repeat(n - i)
i = n
break
}
// Replace with spaces, keep newlines so line numbers stay stable.
for (let j = i; j < end + 2; j++) {
out += source[j] === '\n' ? '\n' : ' '
}
i = end + 2
continue
}
// Line comment — keep `// @ts-...` markers for IGNORE_RE; strip the
// rest of the line. This preserves the existing ignore-directive
// accounting while still excluding `: any` in prose.
if (c === '/' && c2 === '/') {
// Find next newline
let end = i
while (end < n && source[end] !== '\n') end++
const segment = source.slice(i, end)
const isDirective = /^\s*\/\/\s*@(ts-ignore|ts-expect-error|ts-nocheck)\b/.test(segment)
if (isDirective) {
out += segment
} else {
out += ' '.repeat(segment.length)
}
i = end
continue
}
// String literals
if (c === '"' || c === "'") {
const quote = c
let end = i + 1
while (end < n) {
if (source[end] === '\\' && end + 1 < n) {
end += 2
continue
}
if (source[end] === quote) {
end++
break
}
if (source[end] === '\n') {
// Unterminated string — bail and keep the rest verbatim.
break
}
end++
}
out += ' '.repeat(end - i)
i = end
continue
}
// Template literal — simpler handling: strip ${...} interpolation
// contents as well, since the inner expression has its own type scope.
if (c === '`') {
let end = i + 1
while (end < n && source[end] !== '`') {
if (source[end] === '\\' && end + 1 < n) {
end += 2
continue
}
if (source[end] === '$' && source[end + 1] === '{') {
// Strip interpolation body
let depth = 1
end += 2
while (end < n && depth > 0) {
if (source[end] === '{') depth++
else if (source[end] === '}') depth--
if (depth > 0) end++
}
continue
}
end++
}
if (end < n) end++ // consume closing `
out += ' '.repeat(end - i)
i = end
continue
}
out += c
i++
}
return out
}
function countMatches(source: string, re: RegExp): number {
// Rebuild with `g` for counting while preserving the anchored pattern.
const flags = re.flags.replace('g', '') + 'g'
const global = new RegExp(re.source, flags)
let n = 0
while (global.exec(source) !== null) {
n++
if (global.lastIndex === 0) break
}
return n
}
function categorize(source: string): CategoryCounts {
const counts = emptyCategory()
counts.annotation = countMatches(source, ANNOTATION_NEAR_RE)
counts.cast = countMatches(source, CAST_RE)
counts.indexSig = countMatches(source, INDEX_SIG_RE)
counts.ignore = countMatches(source, IGNORE_RE)
counts.genericArg = countMatches(source, GENERIC_RE)
return counts
}
function totalOf(c: CategoryCounts): number {
return c.annotation + c.cast + c.indexSig + c.ignore + c.genericArg
}
async function scan(): Promise<Report> {
const files: FileReport[] = []
const totals = emptyCategory()
for (const dir of SCAN_DIRS) {
const root = resolve(ROOT, dir)
const found: string[] = []
await walk(root, found)
for (const absPath of found) {
const raw = await readFile(absPath, 'utf8')
// Strip comments, strings, and template literals so `: any` in prose
// and `': any'` literals don't count as type annotations.
const source = stripNonCode(raw)
if (!ANNOTATION_RE.test(source) && !CAST_RE.test(source) && !IGNORE_RE.test(source)) {
continue
}
const byCategory = categorize(source)
const total = totalOf(byCategory)
if (total === 0) continue
files.push({
path: absPath.slice(ROOT.length + 1),
total,
byCategory,
})
for (const k of Object.keys(totals) as (keyof CategoryCounts)[]) {
totals[k] += byCategory[k]
}
}
}
files.sort((a, b) => b.total - a.total)
return {
generatedAt: new Date().toISOString(),
total: totalOf(totals),
byCategory: totals,
files,
}
}
function renderMarkdown(report: Report, baselineTotal: number | null): string {
const lines: string[] = []
lines.push('# `any` Usage Report')
lines.push('')
lines.push(`Generated: ${report.generatedAt}`)
lines.push('')
lines.push('## Totals')
lines.push('')
lines.push(`| Category | Count |`)
lines.push(`| --- | ---: |`)
lines.push(`| \`: any\` annotation | ${report.byCategory.annotation} |`)
lines.push(`| \`as any\` cast | ${report.byCategory.cast} |`)
lines.push(`| \`[key: ...]: any\` index signature | ${report.byCategory.indexSig} |`)
lines.push(`| \`// @ts-ignore\` (and friends) | ${report.byCategory.ignore} |`)
lines.push(`| Generic \`any\` arg | ${report.byCategory.genericArg} |`)
lines.push(`| **Total** | **${report.total}** |`)
if (baselineTotal !== null) {
const delta = report.total - baselineTotal
const sign = delta > 0 ? '+' : ''
lines.push('')
lines.push(`Baseline: ${baselineTotal} Delta: ${sign}${delta}`)
}
lines.push('')
lines.push(`## Top offenders (${Math.min(20, report.files.length)} of ${report.files.length})`)
lines.push('')
lines.push('| File | Total | Annot | Cast | IndexSig | Ignore | Generic |')
lines.push('| --- | ---: | ---: | ---: | ---: | ---: | ---: |')
for (const f of report.files.slice(0, 20)) {
lines.push(
`| ${f.path} | ${f.total} | ${f.byCategory.annotation} | ${f.byCategory.cast} | ${f.byCategory.indexSig} | ${f.byCategory.ignore} | ${f.byCategory.genericArg} |`,
)
}
lines.push('')
return lines.join('\n')
}
async function readBaselineTotal(): Promise<number | null> {
try {
const raw = await readFile(BASELINE_PATH, 'utf8')
const parsed = JSON.parse(raw) as Report
return typeof parsed.total === 'number' ? parsed.total : null
} catch {
return null
}
}
async function main(): Promise<void> {
const checkMode = process.argv.includes('--check')
await mkdir(REPORT_DIR, { recursive: true })
const report = await scan()
const baselineTotal = await readBaselineTotal()
await writeFile(JSON_OUT, JSON.stringify(report, null, 2) + '\n')
await writeFile(MD_OUT, renderMarkdown(report, baselineTotal))
// Always refresh baseline file unless --check (we don't want CI to clobber it).
if (!checkMode) {
await writeFile(BASELINE_PATH, JSON.stringify({ total: report.total, generatedAt: report.generatedAt }, null, 2) + '\n')
}
if (checkMode) {
if (baselineTotal === null) {
console.error(`any-usage baseline is missing or invalid: ${BASELINE_PATH}`)
process.exit(1)
}
if (report.total > baselineTotal) {
console.error(
`any-usage budget exceeded: current=${report.total} > baseline=${baselineTotal} (delta=+${report.total - baselineTotal})`,
)
process.exit(1)
}
console.log(`any-usage budget ok: current=${report.total} baseline=${baselineTotal}`)
return
}
console.log(`any-usage report written: total=${report.total} files=${report.files.length}`)
}
await main()
+42 -30
View File
@@ -55,12 +55,12 @@ const TypeOverrideMap: Record<string, string> = {
// Materialize placeholder schemas once so we can detect them by identity (===)
// when they appear as fields inside other schemas.
const placeholderInstances = new Map<any, string>()
const placeholderInstances = new Map<unknown, string>()
for (const name of Object.keys(TypeOverrideMap)) {
const thunk = (schemas as any)[name]
if (typeof thunk === 'function') {
const candidate = (schemas as Record<string, unknown>)[name]
if (typeof candidate === 'function') {
try {
placeholderInstances.set(thunk(), TypeOverrideMap[name])
placeholderInstances.set((candidate as () => unknown)(), TypeOverrideMap[name])
} catch { /* ignore */ }
}
}
@@ -228,15 +228,16 @@ const EXPORT_ORDER = [
// Zod v4 uses schema.def.type as the discriminator (lowercase strings).
// All schemas have .def with { type: string, ... }.
function convert(schema: any, depth = 0): string {
if (!schema || !schema.def) return 'unknown'
function convert(schema: unknown, depth = 0): string {
if (!schema || typeof schema !== 'object' || !('def' in schema)) return 'unknown'
// Check if this schema is a known placeholder (identity comparison)
const override = placeholderInstances.get(schema)
if (override) return override
const def = schema.def
const type: string = def.type
const def = (schema as { def: unknown }).def as Record<string, unknown> | null
if (!def || typeof def !== 'object') return 'unknown'
const type = typeof def.type === 'string' ? def.type : ''
switch (type) {
case 'string':
@@ -259,16 +260,16 @@ function convert(schema: any, depth = 0): string {
return 'never'
case 'literal': {
// def.values is an array of literal values
const vals = def.values as any[]
const vals = Array.isArray(def.values) ? def.values : []
return vals.map(v => JSON.stringify(v)).join(' | ')
}
case 'enum': {
// def.entries is { key: value } or an array
const entries = def.entries
if (Array.isArray(entries)) {
return entries.map((v: any) => JSON.stringify(v)).join(' | ')
return entries.map(v => JSON.stringify(v)).join(' | ')
}
return Object.values(entries)
return Object.values(entries as Record<string, unknown>)
.filter((v): v is string => typeof v === 'string')
.map(v => JSON.stringify(v))
.join(' | ')
@@ -285,7 +286,7 @@ function convert(schema: any, depth = 0): string {
return `${needsArrayElementParens(element) ? `(${element})` : element}[]`
}
case 'tuple': {
const items = (def.items as any[]).map(t => convert(t, depth))
const items = (Array.isArray(def.items) ? def.items : []).map(t => convert(t, depth))
return `[${items.join(', ')}]`
}
case 'record':
@@ -295,7 +296,7 @@ function convert(schema: any, depth = 0): string {
case 'union':
case 'discriminated_union': {
// def.options for discriminated, def.options for plain union
const members = (def.options as any[]).map(t => {
const members = (Array.isArray(def.options) ? def.options : []).map(t => {
const ts = convert(t, depth)
return needsParens(ts) ? `(${ts})` : ts
})
@@ -309,8 +310,11 @@ function convert(schema: any, depth = 0): string {
return `${convert(def.innerType, depth)} | null`
case 'default':
return convert(def.innerType, depth)
case 'lazy':
return convert(def.getter(), depth)
case 'lazy': {
const getter = def.getter
if (typeof getter !== 'function') return 'unknown'
return convert((getter as () => unknown)(), depth)
}
case 'transform':
case 'effects':
return convert(def.schema, depth)
@@ -334,12 +338,12 @@ function convert(schema: any, depth = 0): string {
}
}
function convertObject(def: any, depth: number): string {
let shape: Record<string, any>
function convertObject(def: Record<string, unknown>, depth: number): string {
let shape: Record<string, unknown>
if (typeof def.shape === 'function') {
shape = def.shape()
shape = (def.shape as () => Record<string, unknown>)()
} else if (typeof def.shape === 'object' && def.shape !== null) {
shape = def.shape
shape = def.shape as Record<string, unknown>
} else {
return 'Record<string, unknown>'
}
@@ -359,9 +363,12 @@ function convertObject(def: any, depth: number): string {
return '{\n' + fields.join('\n') + '\n' + closeIndent + '}'
}
function isOptional(schema: any): boolean {
if (!schema?.def) return false
return schema.def.type === 'optional' || schema.def.type === 'default'
function isOptional(schema: unknown): boolean {
if (!schema || typeof schema !== 'object' || !('def' in schema)) return false
const def = (schema as { def: unknown }).def
if (!def || typeof def !== 'object') return false
const defType = (def as { type?: unknown }).type
return defType === 'optional' || defType === 'default'
}
function needsParens(ts: string): boolean {
@@ -388,7 +395,7 @@ export function generateSdkTypes(): string {
let errors = 0
for (const schemaName of EXPORT_ORDER) {
const thunk = (schemas as any)[schemaName]
const thunk = (schemas as Record<string, unknown>)[schemaName]
if (typeof thunk !== 'function') {
console.warn(` ⚠ Not found: ${schemaName}`)
errors++
@@ -403,11 +410,12 @@ export function generateSdkTypes(): string {
continue
}
let schema: any
let schema: unknown
try {
schema = thunk()
} catch (e: any) {
console.warn(` ⚠ Materialize failed: ${schemaName}: ${e.message}`)
schema = (thunk as () => unknown)()
} catch (e: unknown) {
const msg = e instanceof Error ? e.message : String(e)
console.warn(` ⚠ Materialize failed: ${schemaName}: ${msg}`)
errors++
continue
}
@@ -417,14 +425,18 @@ export function generateSdkTypes(): string {
try {
const ts = convert(schema)
// Check for top-level description
const desc = Reflect.get(schema, 'description') as string | undefined
const desc =
schema && typeof schema === 'object'
? (Reflect.get(schema, 'description') as string | undefined)
: undefined
if (desc) {
lines.push(`/** ${desc} */`)
}
lines.push(`export type ${typeName} = ${ts}`)
lines.push('')
} catch (e: any) {
console.warn(` ⚠ Convert failed: ${schemaName}: ${e.message}`)
} catch (e: unknown) {
const msg = e instanceof Error ? e.message : String(e)
console.warn(` ⚠ Convert failed: ${schemaName}: ${msg}`)
errors++
lines.push(`// ⚠ Failed: ${schemaName}`)
lines.push(`export type ${typeName} = any`)
+21 -8
View File
@@ -13,7 +13,9 @@ const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
oneofs: true,
})
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition) as any
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition) as unknown as {
openclaude: { v1: { AgentService: new (addr: string, creds: grpc.ChannelCredentials) => { Chat: () => grpc.ClientDuplexStream<unknown, unknown> } } }
}
const openclaudeProto = protoDescriptor.openclaude.v1
const rl = readline.createInterface({
@@ -35,22 +37,33 @@ async function main() {
grpc.credentials.createInsecure()
)
let call: grpc.ClientDuplexStream<any, any> | null = null
let call: grpc.ClientDuplexStream<unknown, unknown> | null = null
const startStream = () => {
call = client.Chat()
let textStreamed = false
call.on('data', async (serverMessage: any) => {
call.on('data', async (raw: unknown) => {
// gRPC streaming payloads are dynamically loaded from the .proto file at
// startup, so the runtime message shape has no static type. Narrow at
// the boundary and trust field access below.
const serverMessage = raw as {
text_chunk?: { text?: string }
tool_start?: { tool_name?: string; arguments_json?: string }
tool_result?: { tool_name?: string; output?: string }
action_required?: { question?: string; prompt_id?: string }
done?: { full_text?: string }
error?: { message?: string }
}
if (serverMessage.text_chunk) {
process.stdout.write(serverMessage.text_chunk.text)
process.stdout.write(serverMessage.text_chunk.text ?? '')
textStreamed = true
} else if (serverMessage.tool_start) {
console.log(`\n\x1b[36m[Tool Call]\x1b[0m \x1b[1m${serverMessage.tool_start.tool_name}\x1b[0m`)
console.log(`\x1b[90m${serverMessage.tool_start.arguments_json}\x1b[0m\n`)
} else if (serverMessage.tool_result) {
console.log(`\n\x1b[32m[Tool Result]\x1b[0m \x1b[1m${serverMessage.tool_result.tool_name}\x1b[0m`)
const out = serverMessage.tool_result.output
const out = serverMessage.tool_result.output ?? ''
if (out.length > 500) {
console.log(`\x1b[90m${out.substring(0, 500)}...\n(Output truncated, total length: ${out.length})\x1b[0m`)
} else {
@@ -59,8 +72,8 @@ async function main() {
} else if (serverMessage.action_required) {
const action = serverMessage.action_required
console.log(`\n\x1b[33m[Action Required]\x1b[0m`)
const reply = await askQuestion(`\x1b[1m${action.question}\x1b[0m (y/n) > `)
const reply = await askQuestion(`\x1b[1m${action.question ?? ''}\x1b[0m (y/n) > `)
call?.write({
input: {
prompt_id: action.prompt_id,
@@ -75,7 +88,7 @@ async function main() {
console.log('\n\x1b[32m[Generation Complete]\x1b[0m')
promptUser()
} else if (serverMessage.error) {
console.error(`\n\x1b[31m[Server Error]\x1b[0m ${serverMessage.error.message}`)
console.error(`\n\x1b[31m[Server Error]\x1b[0m ${serverMessage.error.message ?? ''}`)
promptUser()
}
})
+13 -10
View File
@@ -96,7 +96,7 @@ export function resolveCacheProbeRequestApiKey(
function getField(obj: unknown, path: string): unknown {
return path
.split('.')
.reduce((o: any, k: string) => (o != null ? o[k] : undefined), obj)
.reduce((o: unknown, k: string) => (o != null && typeof o === 'object' ? (o as Record<string, unknown>)[k] : undefined), obj)
}
interface ProbeResult {
@@ -123,7 +123,7 @@ async function sendProbe(
headers,
body: JSON.stringify(body),
})
} catch (err: any) {
} catch (err: unknown) {
return {
label,
status: 0,
@@ -131,7 +131,7 @@ async function sendProbe(
headers: {},
usage: null,
responseId: null,
error: err.message,
error: err instanceof Error ? err.message : String(err),
}
}
const elapsed = Date.now() - start
@@ -419,21 +419,24 @@ export const call: LocalCommandCall = async (args) => {
// --- Simulate what main's shim code does with this usage ---
// codexShim.ts makeUsage() — used for Responses API (GPT-5+/Codex)
function mainMakeUsage(u: any) {
function mainMakeUsage(u: unknown) {
const rec = (u ?? {}) as Record<string, unknown>
return {
input_tokens: u?.input_tokens ?? 0,
output_tokens: u?.output_tokens ?? 0,
input_tokens: typeof rec.input_tokens === 'number' ? rec.input_tokens : 0,
output_tokens: typeof rec.output_tokens === 'number' ? rec.output_tokens : 0,
cache_creation_input_tokens: 0,
cache_read_input_tokens: 0, // ← main hardcodes this to 0
}
}
// openaiShim.ts convertChunkUsage() — used for Chat Completions
function mainConvertChunkUsage(u: any) {
function mainConvertChunkUsage(u: unknown) {
const rec = (u ?? {}) as Record<string, unknown>
const details = (rec.prompt_tokens_details ?? {}) as Record<string, unknown>
return {
input_tokens: u?.prompt_tokens ?? 0,
output_tokens: u?.completion_tokens ?? 0,
input_tokens: typeof rec.prompt_tokens === 'number' ? rec.prompt_tokens : 0,
output_tokens: typeof rec.completion_tokens === 'number' ? rec.completion_tokens : 0,
cache_creation_input_tokens: 0,
cache_read_input_tokens: u?.prompt_tokens_details?.cached_tokens ?? 0,
cache_read_input_tokens: typeof details.cached_tokens === 'number' ? details.cached_tokens : 0,
}
}
@@ -5,6 +5,7 @@ import { logEvent } from 'src/services/analytics/index.js';
import { Box, Link, Text } from '../ink.js';
import type { ExternalClaudeMdInclude } from '../utils/claudemd.js';
import { saveCurrentProjectConfig } from '../utils/config.js';
import type { ProjectConfig } from '../utils/config.js';
import { getClaudeConfigHomeDir } from '../utils/envUtils.js';
import { getDisplayPath } from '../utils/file.js';
import { Select } from './CustomSelect/index.js';
@@ -15,16 +16,16 @@ type Props = {
externalIncludes?: ExternalClaudeMdInclude[];
scope?: 'User' | 'Project';
};
function acceptProject(current: any) {
function acceptProject(current: ProjectConfig) {
return { ...current, hasClaudeMdExternalIncludesApproved: true, hasClaudeMdExternalIncludesWarningShown: true };
}
function acceptUser(current: any) {
function acceptUser(current: ProjectConfig) {
return { ...current, hasClaudeMdExternalIncludesApprovedForUser: true, hasClaudeMdExternalIncludesWarningShownForUser: true };
}
function declineProject(current: any) {
function declineProject(current: ProjectConfig) {
return { ...current, hasClaudeMdExternalIncludesApproved: false, hasClaudeMdExternalIncludesWarningShown: true };
}
function declineUser(current: any) {
function declineUser(current: ProjectConfig) {
return { ...current, hasClaudeMdExternalIncludesApprovedForUser: false, hasClaudeMdExternalIncludesWarningShownForUser: true };
}
function getUserClaudeMdDisplayPath(): string {
+1 -1
View File
@@ -34,7 +34,7 @@ type Props = {
export function EffortPicker({ onSelect, onCancel }: Props) {
const model = useMainLoopModel()
const appStateEffort = useAppState((s: any) => s.effortValue)
const appStateEffort = useAppState((s) => s.effortValue)
const setAppState = useSetAppState()
const provider = getAPIProvider()
const usesOpenAIEffort = modelUsesOpenAIEffort(model)
+19 -16
View File
@@ -1718,18 +1718,21 @@ export function ProviderManager({ mode, onDone }: Props): React.ReactNode {
env: {
CLAUDE_CODE_USE_GITHUB: '1',
OPENAI_MODEL: GITHUB_PROVIDER_DEFAULT_MODEL,
OPENAI_API_KEYS: undefined as any,
OPENAI_API_KEY: undefined as any,
OPENAI_ORG: undefined as any,
OPENAI_PROJECT: undefined as any,
OPENAI_ORGANIZATION: undefined as any,
OPENAI_BASE_URL: undefined as any,
OPENAI_API_BASE: undefined as any,
CLAUDE_CODE_USE_OPENAI: undefined as any,
CLAUDE_CODE_USE_GEMINI: undefined as any,
CLAUDE_CODE_USE_BEDROCK: undefined as any,
CLAUDE_CODE_USE_VERTEX: undefined as any,
CLAUDE_CODE_USE_FOUNDRY: undefined as any,
// `undefined` instructs the merge to clear these keys from saved
// settings. EnvironmentVariablesSchema coerces to string, so we route
// through `unknown` instead of `any` for the literal assignment.
OPENAI_API_KEYS: undefined as unknown as string,
OPENAI_API_KEY: undefined as unknown as string,
OPENAI_ORG: undefined as unknown as string,
OPENAI_PROJECT: undefined as unknown as string,
OPENAI_ORGANIZATION: undefined as unknown as string,
OPENAI_BASE_URL: undefined as unknown as string,
OPENAI_API_BASE: undefined as unknown as string,
CLAUDE_CODE_USE_OPENAI: undefined as unknown as string,
CLAUDE_CODE_USE_GEMINI: undefined as unknown as string,
CLAUDE_CODE_USE_BEDROCK: undefined as unknown as string,
CLAUDE_CODE_USE_VERTEX: undefined as unknown as string,
CLAUDE_CODE_USE_FOUNDRY: undefined as unknown as string,
},
})
if (error) {
@@ -1767,10 +1770,10 @@ export function ProviderManager({ mode, onDone }: Props): React.ReactNode {
const { error } = updateSettingsForSource('userSettings', {
env: {
CLAUDE_CODE_USE_GITHUB: undefined as any,
OPENAI_MODEL: undefined as any,
OPENAI_BASE_URL: undefined as any,
OPENAI_API_BASE: undefined as any,
CLAUDE_CODE_USE_GITHUB: undefined as unknown as string,
OPENAI_MODEL: undefined as unknown as string,
OPENAI_BASE_URL: undefined as unknown as string,
OPENAI_API_BASE: undefined as unknown as string,
},
})
if (error) {
+3 -3
View File
@@ -3,9 +3,9 @@ import React, { createContext, type ReactNode, useCallback, useEffect, useMemo,
import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js';
import type { WizardContextValue, WizardProviderProps } from './types.js';
// Use any here for the context since it will be cast properly when used
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const WizardContext = createContext<WizardContextValue<any> | null>(null);
// Use the default generic (Record<string, unknown>) for the context value; consumers
// that need a tighter type can supply it via useContext<WizardContextValue<T>>.
export const WizardContext = createContext<WizardContextValue | null>(null);
export function WizardProvider<T extends Record<string, unknown> = Record<string, unknown>>(t0: WizardProviderProps<T>): React.ReactNode {
const $ = _c(38);
const {
+13 -14
View File
@@ -202,20 +202,19 @@ export async function startMCPServer(
if (typeof data === 'string') {
content = [{ type: 'text', text: data }]
} else if (Array.isArray(data)) {
content = data.map((block: any) => {
if (block.type === 'text') {
return { type: 'text', text: block.text || '' }
} else if (block.type === 'image' && block.source) {
return {
type: 'image',
data: block.source.data,
mimeType: block.source.media_type,
}
} else {
// eslint-disable-next-line custom-rules/no-top-level-side-effects, no-console
console.warn(`Unmapped content block type from tool ${name}: ${block.type || 'unknown'}`)
return { type: 'text', text: jsonStringify(block) }
content = data.flatMap((block: unknown) => {
// Boundary data — defensively skip primitives/null instead of crashing.
if (!block || typeof block !== 'object') return []
const b = block as { type?: unknown; text?: unknown; source?: { type?: unknown; media_type?: unknown; data?: unknown } }
if (b.type === 'text') {
return [{ type: 'text', text: String(b.text ?? '') } as CallToolResult['content'][number]]
}
if (b.type === 'image' && b.source && typeof b.source.data === 'string' && typeof b.source.media_type === 'string') {
return [{ type: 'image', data: b.source.data, mimeType: b.source.media_type } as CallToolResult['content'][number]]
}
// eslint-disable-next-line custom-rules/no-top-level-side-effects, no-console
console.warn(`Unmapped content block type from tool ${name}: ${String(b.type ?? 'unknown')}`)
return [{ type: 'text', text: jsonStringify(block) } as CallToolResult['content'][number]]
}) as CallToolResult['content']
} else {
content = [{ type: 'text', text: jsonStringify(data) }]
@@ -223,7 +222,7 @@ export async function startMCPServer(
return {
content,
isError: !!(finalResult as any).isError,
isError: !!(finalResult as { isError?: unknown }).isError,
}
} catch (error) {
logError(error)
+26 -6
View File
@@ -186,6 +186,26 @@ export type SessionMessage = {
// Re-export precise SDK message types from generated types
// These use camelCase field names and discriminated unions for full IntelliSense
// Note: ToolAnnotations and CallToolResult shapes mirror the MCP SDK but are
// declared locally to keep the public SDK declarations free of the optional
// `@modelcontextprotocol/sdk` peer dependency. They are stable SDK shapes.
type ToolAnnotations = {
title?: string
readOnlyHint?: boolean
destructiveHint?: boolean
idempotentHint?: boolean
openWorldHint?: boolean
}
type CallToolResult = {
content?: Array<
| { type: 'text'; text: string }
| { type: 'image'; data: string; mimeType: string }
| { type: 'resource'; resource: { uri: string; text?: string; mimeType?: string } }
| { type: string; [key: string]: unknown }
>
isError?: boolean
[key: string]: unknown
}
import type {
AccountInfo,
AgentInfo,
@@ -441,12 +461,12 @@ export interface SDKSession {
// MCP tool types
// ============================================================================
export interface SdkMcpToolDefinition<Schema = any> {
export interface SdkMcpToolDefinition<Schema = unknown> {
name: string
description: string
inputSchema: Schema
handler: (args: any, extra: unknown) => Promise<any>
annotations?: any
handler: (args: any, extra: unknown) => Promise<CallToolResult>
annotations?: ToolAnnotations
searchHint?: string
alwaysLoad?: boolean
}
@@ -525,13 +545,13 @@ export function unstable_v2_prompt(
// MCP tool functions
// ============================================================================
export function tool<Schema = any>(
export function tool<Schema = unknown>(
name: string,
description: string,
inputSchema: Schema,
handler: (args: any, extra: unknown) => Promise<any>,
handler: (args: any, extra: unknown) => Promise<CallToolResult>,
extras?: {
annotations?: any
annotations?: ToolAnnotations
searchHint?: string
alwaysLoad?: boolean
},
+1 -1
View File
@@ -168,7 +168,7 @@ export {
* )
* ```
*/
export function tool<Schema = any>(
export function tool<Schema = unknown>(
name: string,
description: string,
inputSchema: Schema,
+12 -12
View File
@@ -6,7 +6,7 @@
*/
import type { ContentBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
import { randomUUID } from 'crypto'
import { randomUUID, type UUID } from 'crypto'
import { dirname } from 'path'
import { QueryEngine } from '../../QueryEngine.js'
import {
@@ -824,11 +824,11 @@ class QueryImpl implements Query {
// Find the last assistant message UUID that has a file-history snapshot
const fileHistory = state.fileHistory
for (let i = messages.length - 1; i >= 0; i--) {
const msg = messages[i]
const messageId = (msg as any)?.uuid as string | undefined
const msg = messages[i] as { uuid?: string } | undefined
const messageId = msg?.uuid
if (!messageId) continue
if (fileHistoryCanRestore(fileHistory, messageId as any)) {
if (fileHistoryCanRestore(fileHistory, messageId as UUID)) {
// Synchronous check — return canRewind: true with the messageId.
// Use rewindFilesAsync() to actually perform the rewind.
return { canRewind: true }
@@ -850,11 +850,11 @@ class QueryImpl implements Query {
const fileHistory = state.fileHistory
let targetMessageId: string | undefined
for (let i = messages.length - 1; i >= 0; i--) {
const msg = messages[i]
const messageId = (msg as any)?.uuid as string | undefined
const msg = messages[i] as { uuid?: string } | undefined
const messageId = msg?.uuid
if (!messageId) continue
if (fileHistoryCanRestore(fileHistory, messageId as any)) {
if (fileHistoryCanRestore(fileHistory, messageId as UUID)) {
targetMessageId = messageId
break
}
@@ -865,7 +865,7 @@ class QueryImpl implements Query {
}
// Get diff stats before rewinding (async)
const diffStats = await fileHistoryGetDiffStats(fileHistory, targetMessageId as any)
const diffStats = await fileHistoryGetDiffStats(fileHistory, targetMessageId as UUID)
// Perform the actual rewind
try {
@@ -874,7 +874,7 @@ class QueryImpl implements Query {
...prev,
fileHistory: updater(prev.fileHistory),
})),
targetMessageId as any,
targetMessageId as UUID,
)
return {
@@ -910,7 +910,7 @@ class QueryImpl implements Query {
supportedAgents(): string[] {
const state = this.appStateStore.getState()
const agents = state.agentDefinitions?.activeAgents
return agents?.map((a: any) => a.agentType).filter(Boolean) ?? []
return agents?.map((a) => a.agentType).filter(Boolean) ?? []
}
mcpServerStatus(): McpServerStatus[] {
@@ -925,10 +925,10 @@ class QueryImpl implements Query {
base.serverInfo = client.serverInfo
}
if (client.type === 'failed') {
base.error = (client as any).error
base.error = client.error
}
if ('config' in client) {
const cfg = (client as any).config
const cfg = client.config
if (cfg?.scope) base.scope = cfg.scope
}
return base
+6 -5
View File
@@ -42,6 +42,7 @@ import type {
PermissionResult,
SDKResultMessage as GeneratedSDKResultMessage,
} from './coreTypes.generated.js'
import type { Message } from '../../types/message.js'
import type {
SDKMessage,
SDKPermissionTimeoutMessage,
@@ -177,7 +178,7 @@ export type SDKResultMessage = GeneratedSDKResultMessage
* These definitions can be passed to `createSdkMcpServer()` to register
* custom MCP tools.
*/
export interface SdkMcpToolDefinition<Schema = any> {
export interface SdkMcpToolDefinition<Schema = unknown> {
name: string
description: string
inputSchema: Schema
@@ -518,7 +519,7 @@ class SDKSessionImpl implements SDKSession {
function createEngineFromOptions(
options: SDKSessionOptions,
permissionTarget: PermissionTarget & { pushTimeout?: (msg: SDKPermissionTimeoutMessage) => void },
initialMessages?: any[],
initialMessages?: Message[],
sessionId?: string,
): { engine: QueryEngine; appStateStore: Store<AppState>; abortController: AbortController } {
const {
@@ -678,7 +679,7 @@ export async function unstable_v2_resumeSession(
// Load prior messages from JSONL with compact-aware chain building.
// Matches CLI's loadTranscriptFile → buildConversationChain → removeExtraFields.
const resolved = await resolveSessionFilePath(sessionId, options.cwd)
let initialMessages: any[]
let initialMessages: Message[] = []
if (resolved) {
const { size: fileSize } = await stat(resolved.filePath)
@@ -764,7 +765,7 @@ export async function unstable_v2_resumeSession(
}
if (leaf) {
const chain = buildChain(byUuid, leaf)
initialMessages = stripChainFields(chain)
initialMessages = stripChainFields(chain) as Message[]
} else {
initialMessages = []
}
@@ -779,7 +780,7 @@ export async function unstable_v2_resumeSession(
const { engine, appStateStore, abortController } = createEngineFromOptions(
options,
session,
initialMessages as any[],
initialMessages,
sessionId,
)
session.setEngine(engine)
+28 -5
View File
@@ -20,9 +20,32 @@ const packageDefinition = protoLoader.loadSync(PROTO_PATH, {
oneofs: true,
})
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition) as any
// proto-loader returns a deeply-nested untyped shape; we only need the AgentService
// definition from the OpenClaude v1 package, so narrow to that. The implementation
// map declares the method names; ServiceDefinition<T> requires T to be a record
// keyed by method name.
const protoDescriptor = grpc.loadPackageDefinition(packageDefinition) as unknown as {
openclaude: {
v1: {
AgentService: { service: grpc.ServiceDefinition<{ Chat: never }> }
}
}
}
const openclaudeProto = protoDescriptor.openclaude.v1
// Shape of a single client message on the duplex Chat stream. Matches the
// `oneof` payload in openclaude.proto (ChatRequest | ToolInput | CancelRequest).
type ClientMessage = {
request?: {
session_id?: unknown
message?: unknown
working_directory?: unknown
model?: unknown
} | null
input?: { prompt_id?: unknown; reply?: unknown } | null
cancel?: unknown
}
const MAX_SESSIONS = 1000
type TextDeltaEvent = {
@@ -108,7 +131,7 @@ export class GrpcServer {
)
}
private handleChat(call: grpc.ServerDuplexStream<any, any>) {
private handleChat(call: grpc.ServerDuplexStream<ClientMessage, unknown>) {
let engine: QueryEngine | null = null
let appState: AppState = getDefaultAppState()
const fileCache: FileStateCache = new FileStateCache(READ_FILE_STATE_CACHE_SIZE, 25 * 1024 * 1024)
@@ -117,7 +140,7 @@ export class GrpcServer {
const pendingRequests = new Map<string, (reply: string) => void>()
// Accumulated messages from previous turns for multi-turn context
let previousMessages: any[] = []
let previousMessages: Message[] = []
let sessionId = ''
let interrupted = false
@@ -293,11 +316,11 @@ export class GrpcServer {
}
call.end()
}
} catch (err: any) {
} catch (err) {
console.error('Error processing stream')
call.write({
error: {
message: err.message || "Internal server error",
message: err instanceof Error ? err.message : "Internal server error",
code: "INTERNAL"
}
})
+2 -3
View File
@@ -34,9 +34,8 @@ if (process.env.NODE_ENV === 'development') {
try {
// eslint-disable-next-line custom-rules/no-top-level-dynamic-import -- dev-only; NODE_ENV check is DCE'd in production
void import('./devtools.js')
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} catch (error: any) {
if (error.code === 'ERR_MODULE_NOT_FOUND') {
} catch (error: unknown) {
if ((error as { code?: unknown }).code === 'ERR_MODULE_NOT_FOUND') {
// biome-ignore lint/suspicious/noConsole: intentional warning
console.warn(
`
+1 -2
View File
@@ -263,8 +263,7 @@ function isBeingDebugged() {
// Check if inspector is available and active (indicates debugging)
try {
// Dynamic import would be better but is async - use global object instead
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const inspector = (global as any).require('inspector');
const inspector = (global as unknown as { require: (s: string) => { url: () => string } }).require('inspector');
const hasInspectorUrl = !!inspector.url();
return hasInspectorUrl || hasInspectArg || hasInspectEnv;
} catch {
+1 -1
View File
@@ -974,7 +974,7 @@ async function* queryLoop(
messagesForQuery = snipResult.messages
snipTokensFreed = snipResult.tokensFreed
if (snipResult.boundaryMessage) {
yield snipResult.boundaryMessage
yield snipResult.boundaryMessage as Message
}
queryCheckpoint('query_snip_end')
}
+7 -5
View File
@@ -122,19 +122,21 @@ function isPersistentRetryEnabled(): boolean {
: false
}
function isQuotaExhausted(error: any): boolean {
const msg = (error?.message || '').toLowerCase()
function isQuotaExhausted(error: unknown): boolean {
const errObj = error as { message?: unknown; status?: unknown } | null | undefined
const msg = String(errObj?.message ?? '').toLowerCase()
const status = errObj?.status
// OpenRouter (and other gateways) return 402 with instructions to adjust max_tokens.
// If the 402 is retryable via token adjustment, do not treat it as exhausted.
if (error?.status === 402 && error instanceof APIError && parseOpenRouterAffordableMaxTokensError(error) !== undefined) {
if (status === 402 && error instanceof APIError && parseOpenRouterAffordableMaxTokensError(error) !== undefined) {
return false
}
return (
error?.status === 402 ||
status === 402 ||
msg.includes('quota_exhausted') ||
((error?.status === 429 || error?.status === 403 || error?.status === 400) &&
((status === 429 || status === 403 || status === 400) &&
(msg.includes('limit: 0') ||
msg.includes('exceeded your current quota') ||
msg.includes('credit') ||
+55 -27
View File
@@ -1,5 +1,6 @@
import { randomUUID } from 'crypto'
import type { UUID } from 'crypto'
import type { Message } from '../../types/message.js'
import { deriveShortMessageId } from '../../utils/messages.js'
// Module-level registry of message UUIDs queued for removal. We resolve the
@@ -35,7 +36,23 @@ function normalizeSnipShortId(shortId: string): string {
// and were queued. Unresolvable short IDs (stale or hallucinated) are skipped,
// so callers can report the genuinely-queued count rather than the raw request
// length.
export function markForSnip(shortIds: string[], messages: any[]): UUID[] {
// Structural message shape for snip operations. Tests construct fake messages
// with `string` uuids; using `Message` directly would reject them since the
// production `uuid` field is a branded `${string}-${string}-...` template type.
type SnipMessage = {
uuid?: string
type?: string
message?: { content?: unknown }
content?: unknown
subtype?: string
attachment?: { type?: string }
snipMetadata?: { removedUuids?: UUID[] }
isMeta?: boolean
timestamp?: string
level?: string
}
export function markForSnip(shortIds: string[], messages: SnipMessage[]): UUID[] {
const shortIdToUuid = new Map<string, UUID>()
for (const msg of messages) {
if (msg?.uuid) {
@@ -73,14 +90,14 @@ const DEFAULT_NUDGE_INTERVAL_TOKENS = 10_000
/**
* Rough per-message token estimate: content length ÷ 4.
*/
function estimateTokens(msg: any): number {
function estimateTokens(msg: SnipMessage): number {
const content = msg?.message?.content ?? msg?.content ?? ''
const text = typeof content === 'string' ? content : JSON.stringify(content)
return Math.ceil(text.length / 4)
}
export function shouldNudgeForSnips(
messages: any[],
messages: SnipMessage[],
intervalTokens = DEFAULT_NUDGE_INTERVAL_TOKENS,
): boolean {
let accumulated = 0
@@ -98,19 +115,21 @@ export function shouldNudgeForSnips(
return false
}
export function snipCompactIfNeeded(
messages: any[],
): { messages: any[]; tokensFreed: number; boundaryMessage?: any } {
export function snipCompactIfNeeded<T extends SnipMessage>(
messages: T[],
): { messages: T[]; tokensFreed: number; boundaryMessage?: SnipMessage } {
if (pendingSnipUuids.size === 0) {
return { messages, tokensFreed: 0 }
}
// Match pending UUIDs against THIS conversation's messages. UUIDs that belong
// to another in-process session won't be present here, so they stay pending.
const uuidsToRemove = new Set<UUID>()
// Use plain `string` keys so the Set also accepts the test fixtures' `uuid: 'u4'`
// shapes; production callers pass branded UUIDs which coerce cleanly.
const uuidsToRemove = new Set<string | undefined>()
for (const msg of messages) {
const uuid = msg?.uuid as UUID | undefined
if (uuid && pendingSnipUuids.has(uuid)) uuidsToRemove.add(uuid)
const uuid = msg?.uuid
if (uuid && pendingSnipUuids.has(uuid as UUID)) uuidsToRemove.add(uuid as UUID)
}
if (uuidsToRemove.size === 0) {
@@ -118,7 +137,7 @@ export function snipCompactIfNeeded(
}
// Consume only the matched UUIDs; another session's pending removals survive.
for (const uuid of uuidsToRemove) pendingSnipUuids.delete(uuid)
for (const uuid of uuidsToRemove) pendingSnipUuids.delete(uuid as unknown as UUID)
// A tool interaction spans an assistant tool_use and the paired user
// tool_result. The model can snip from EITHER side, so we pair in both
@@ -164,7 +183,9 @@ export function snipCompactIfNeeded(
if (msg?.type !== 'assistant') continue
const blocks = msg?.message?.content
if (!Array.isArray(blocks)) continue
const toolUses = (blocks as any[]).filter(b => b?.type === 'tool_use')
const toolUses = (blocks as unknown[]).filter((b): b is { type: string; id?: string } =>
typeof b === 'object' && b !== null && (b as { type?: unknown }).type === 'tool_use',
)
if (toolUses.length === 0) continue
// Only the paired-drop branch needs the whole turn to be tool blocks: an
// explicitly-snipped message (uuidsToRemove) is the model's deliberate choice
@@ -174,14 +195,14 @@ export function snipCompactIfNeeded(
const droppable =
uuidsToRemove.has(msg?.uuid) ||
(isPureToolUseTurn &&
toolUses.every((t: any) => snippedResultToolUseIds.has(t?.id)))
toolUses.every((t) => typeof t.id === 'string' && snippedResultToolUseIds.has(t.id)))
if (droppable) {
for (const t of toolUses) if (t?.id) safeToolUseIds.add(t.id as string)
for (const t of toolUses) if (t?.id) safeToolUseIds.add(t.id)
}
}
let tokensFreed = 0
const surviving: any[] = []
const surviving: T[] = []
// UUIDs actually removed from the live context: explicitly-snipped messages
// that were cleanly removable, plus the paired half of each snipped tool
// interaction (paired tool-result users, or paired assistant tool-uses). They
@@ -200,10 +221,13 @@ export function snipCompactIfNeeded(
// tool_use, which the next API-prep pass repairs with a synthetic
// placeholder, so the snip would not actually take effect. Keep it.
if (msg?.type === 'user' && Array.isArray(msg?.message?.content)) {
const results = (msg.message.content as any[]).filter(b => b?.type === 'tool_result')
const userBlocks = msg.message.content as unknown[]
const results = userBlocks.filter((b): b is { type: string; tool_use_id?: string } =>
typeof b === 'object' && b !== null && (b as { type?: unknown }).type === 'tool_result',
)
if (
results.length > 0 &&
!results.every((r: any) => safeToolUseIds.has(r?.tool_use_id))
!results.every((r) => typeof r.tool_use_id === 'string' && safeToolUseIds.has(r.tool_use_id))
) {
surviving.push(msg)
continue
@@ -215,12 +239,14 @@ export function snipCompactIfNeeded(
}
// Drop user messages whose content is entirely tool results for snipped tool calls
if (msg?.type === 'user' && Array.isArray(msg?.message?.content)) {
const blocks = msg.message.content as any[]
const results = blocks.filter(b => b?.type === 'tool_result')
const userBlocks = msg.message.content as unknown[]
const results = userBlocks.filter((b): b is { type: string; tool_use_id?: string } =>
typeof b === 'object' && b !== null && (b as { type?: unknown }).type === 'tool_result',
)
if (
results.length > 0 &&
results.length === blocks.length &&
results.every((r: any) => snippedToolUseIds.has(r?.tool_use_id))
results.length === userBlocks.length &&
results.every((r) => typeof r.tool_use_id === 'string' && snippedToolUseIds.has(r.tool_use_id))
) {
tokensFreed += estimateTokens(msg)
if (msg?.uuid) removedUuids.add(msg.uuid as UUID)
@@ -231,12 +257,14 @@ export function snipCompactIfNeeded(
// side. Mirrors the user-message .every() guard: if any tool_use in this
// turn still has a surviving result, keep the message to avoid orphaning it.
if (msg?.type === 'assistant' && Array.isArray(msg?.message?.content)) {
const blocks = msg.message.content as any[]
const toolUses = blocks.filter(b => b?.type === 'tool_use')
const assistantBlocks = msg.message.content as unknown[]
const toolUses = assistantBlocks.filter((b): b is { type: string; id?: string } =>
typeof b === 'object' && b !== null && (b as { type?: unknown }).type === 'tool_use',
)
if (
toolUses.length > 0 &&
toolUses.length === blocks.length &&
toolUses.every((t: any) => snippedResultToolUseIds.has(t?.id))
toolUses.length === assistantBlocks.length &&
toolUses.every((t) => typeof t.id === 'string' && snippedResultToolUseIds.has(t.id))
) {
tokensFreed += estimateTokens(msg)
if (msg?.uuid) removedUuids.add(msg.uuid as UUID)
@@ -252,9 +280,9 @@ export function snipCompactIfNeeded(
return { messages, tokensFreed: 0 }
}
const boundaryMessage = {
const boundaryMessage: SnipMessage = {
type: 'system' as const,
subtype: 'snip_boundary',
subtype: 'snip_boundary' as const,
content: 'Conversation history snipped',
isMeta: false as const,
timestamp: new Date().toISOString(),
@@ -272,7 +300,7 @@ export function snipCompactIfNeeded(
}
export function isSnipMarkerMessage(message: unknown): boolean {
return (message as any)?.subtype === 'snip_boundary'
return (message as { subtype?: unknown } | null | undefined)?.subtype === 'snip_boundary'
}
/** Exposed for test isolation only — do not call in production code. */
+7 -5
View File
@@ -1,5 +1,5 @@
export function isSnipBoundaryMessage(message: unknown): boolean {
return Boolean((message as any)?.snipMetadata)
return Boolean((message as { snipMetadata?: unknown } | null | undefined)?.snipMetadata)
}
/**
@@ -10,13 +10,15 @@ export function isSnipBoundaryMessage(message: unknown): boolean {
export function projectSnippedView<T>(messages: T[]): T[] {
const removedUuids = new Set<string>()
for (const msg of messages) {
const uuids = (msg as any)?.snipMetadata?.removedUuids
const uuids = (msg as { snipMetadata?: { removedUuids?: unknown } } | null | undefined)?.snipMetadata?.removedUuids
if (!Array.isArray(uuids)) continue
for (const uuid of uuids) removedUuids.add(uuid as string)
for (const uuid of uuids) {
if (typeof uuid === 'string') removedUuids.add(uuid)
}
}
if (removedUuids.size === 0) return messages
return messages.filter(msg => {
const uuid = (msg as any)?.uuid
return !uuid || !removedUuids.has(uuid as string)
const uuid = (msg as { uuid?: string } | null | undefined)?.uuid
return !uuid || !removedUuids.has(uuid)
})
}
+12 -14
View File
@@ -254,20 +254,16 @@ function addCodexSource(
})
}
function getCodexSources(item: Record<string, any>): unknown[] {
if (Array.isArray(item.action?.sources)) {
return item.action.sources
}
if (Array.isArray(item.sources)) {
return item.sources
}
if (Array.isArray(item.result?.sources)) {
return item.result.sources
}
function getCodexSources(item: Record<string, unknown>): unknown[] {
const action = (item.action ?? {}) as Record<string, unknown>
const result = (item.result ?? {}) as Record<string, unknown>
if (Array.isArray(action.sources)) return action.sources
if (Array.isArray(item.sources)) return item.sources as unknown[]
if (Array.isArray(result.sources)) return result.sources
return []
}
function extractCodexWebSearchFailure(item: Record<string, any>): string | undefined {
function extractCodexWebSearchFailure(item: Record<string, unknown>): string | undefined {
// Codex web_search_call items can carry a status field. When the tool
// call fails (rate limit, upstream error, model-side guardrail), the
// parser should surface a meaningful error rather than the generic
@@ -275,10 +271,12 @@ function extractCodexWebSearchFailure(item: Record<string, any>): string | undef
// { type: 'web_search_call', status: 'failed', error: { message?: string } }
// { type: 'web_search_call', status: 'failed', action: { error?: { message?: string } } }
if (item?.status !== 'failed') return undefined
const error = (item.error ?? {}) as Record<string, unknown>
const action = (item.action ?? {}) as Record<string, unknown>
const actionError = (action.error ?? {}) as Record<string, unknown>
const reason =
(typeof item.error?.message === 'string' && item.error.message) ||
(typeof item.action?.error?.message === 'string' &&
item.action.error.message) ||
(typeof error.message === 'string' && error.message) ||
(typeof actionError.message === 'string' && actionError.message) ||
(typeof item.error === 'string' && item.error) ||
undefined
return reason ? `Web search failed: ${reason}` : 'Web search failed.'
+13 -8
View File
@@ -22,21 +22,26 @@ export const bingProvider: SearchProvider = {
url.searchParams.set('q', input.query)
url.searchParams.set('count', '15')
const data = await fetchJsonWithWebSearchTimeout(
const data = (await fetchJsonWithWebSearchTimeout(
url.toString(),
{
headers: { 'Ocp-Apim-Subscription-Key': process.env.BING_API_KEY! },
},
signal,
{ providerName: 'Bing' },
)
)) as { webPages?: { value?: unknown } } | undefined
const hits = (data.webPages?.value ?? []).map((r: any) => ({
title: r.name ?? '',
url: r.url ?? '',
description: r.snippet,
source: r.displayUrl,
}))
const rawResults = data?.webPages?.value
const results = Array.isArray(rawResults) ? rawResults : []
const hits = (results as unknown[]).map((r) => {
const rec = (r ?? {}) as Record<string, unknown>
return {
title: typeof rec.name === 'string' ? rec.name : '',
url: typeof rec.url === 'string' ? rec.url : '',
description: typeof rec.snippet === 'string' ? rec.snippet : undefined,
source: typeof rec.displayUrl === 'string' ? rec.displayUrl : undefined,
}
})
return {
hits: applyDomainFilters(hits, input),
+14 -8
View File
@@ -25,7 +25,7 @@ export const braveProvider: SearchProvider = {
url.searchParams.set('q', input.query)
url.searchParams.set('count', '15')
const data = await fetchJsonWithWebSearchTimeout(
const data = (await fetchJsonWithWebSearchTimeout(
url.toString(),
{
headers: {
@@ -35,14 +35,20 @@ export const braveProvider: SearchProvider = {
},
signal,
{ providerName: 'Brave' },
)
)) as { web?: { results?: unknown } } | undefined
const hits = (data.web?.results ?? []).map((r: any) => ({
title: r.title ?? '',
url: r.url ?? '',
description: r.description,
source: r.url ? safeHostname(r.url) : undefined,
}))
const rawResults = data?.web?.results
const results = Array.isArray(rawResults) ? rawResults : []
const hits = (results as unknown[]).map((r) => {
const rec = (r ?? {}) as Record<string, unknown>
const url = typeof rec.url === 'string' ? rec.url : ''
return {
title: typeof rec.title === 'string' ? rec.title : '',
url,
description: typeof rec.description === 'string' ? rec.description : undefined,
source: url ? safeHostname(url) : undefined,
}
})
return {
hits: applyDomainFilters(hits, input),
+71 -42
View File
@@ -47,7 +47,7 @@ interface ProviderPreset {
authHeader?: string
authScheme?: string
jsonPath?: string
responseAdapter?: (data: any) => SearchHit[]
responseAdapter?: (data: unknown) => SearchHit[]
/**
* When set, the API key (WEB_KEY) is sent as this query parameter instead
* of in an HTTP header. Auth headers are suppressed automatically unless
@@ -70,13 +70,24 @@ const BUILT_IN_PROVIDERS: Record<string, ProviderPreset> = {
urlTemplate: 'https://localhost:8080/search',
queryParam: 'q',
jsonPath: 'results',
responseAdapter(data: any) {
return (data.results ?? []).map((r: any) => ({
title: r.title ?? r.url,
url: r.url,
description: r.content,
source: r.engine ?? r.source,
}))
responseAdapter(data: unknown) {
const rec = (data ?? {}) as Record<string, unknown>
const rawResults = Array.isArray(rec.results) ? rec.results : []
return (rawResults as unknown[]).map((r) => {
const item = (r ?? {}) as Record<string, unknown>
const url = typeof item.url === 'string' ? item.url : ''
return {
title: typeof item.title === 'string' ? item.title : url,
url,
description: typeof item.content === 'string' ? item.content : undefined,
source:
typeof item.engine === 'string'
? item.engine
: typeof item.source === 'string'
? item.source
: undefined,
}
})
},
},
google: {
@@ -91,13 +102,18 @@ const BUILT_IN_PROVIDERS: Record<string, ProviderPreset> = {
queryParam: 'q',
authQueryParam: 'key',
envQueryParams: { cx: 'GOOGLE_CSE_ID' },
responseAdapter(data: any) {
return (data.items ?? []).map((r: any) => ({
title: r.title ?? '',
url: r.link ?? '',
description: r.snippet,
source: r.displayLink,
}))
responseAdapter(data: unknown) {
const rec = (data ?? {}) as Record<string, unknown>
const rawItems = Array.isArray(rec.items) ? rec.items : []
return (rawItems as unknown[]).map((r) => {
const item = (r ?? {}) as Record<string, unknown>
return {
title: typeof item.title === 'string' ? item.title : '',
url: typeof item.link === 'string' ? item.link : '',
description: typeof item.snippet === 'string' ? item.snippet : undefined,
source: typeof item.displayLink === 'string' ? item.displayLink : undefined,
}
})
},
},
brave: {
@@ -107,13 +123,20 @@ const BUILT_IN_PROVIDERS: Record<string, ProviderPreset> = {
// Brave wants the bare token in X-Subscription-Token, NOT "Bearer <token>".
// Empty scheme overrides the default 'Bearer' applied by buildAuthHeadersForPreset.
authScheme: '',
responseAdapter(data: any) {
return (data.web?.results ?? []).map((r: any) => ({
title: r.title ?? '',
url: r.url ?? '',
description: r.description,
source: safeHostname(r.url),
}))
responseAdapter(data: unknown) {
const rec = (data ?? {}) as Record<string, unknown>
const web = (rec.web ?? {}) as Record<string, unknown>
const rawResults = Array.isArray(web.results) ? web.results : []
return (rawResults as unknown[]).map((r) => {
const item = (r ?? {}) as Record<string, unknown>
const url = typeof item.url === 'string' ? item.url : ''
return {
title: typeof item.title === 'string' ? item.title : '',
url,
description: typeof item.description === 'string' ? item.description : undefined,
source: url ? safeHostname(url) : undefined,
}
})
},
},
serpapi: {
@@ -121,13 +144,18 @@ const BUILT_IN_PROVIDERS: Record<string, ProviderPreset> = {
queryParam: 'q',
authHeader: 'Authorization',
authScheme: 'Bearer',
responseAdapter(data: any) {
return (data.organic_results ?? []).map((r: any) => ({
title: r.title ?? '',
url: r.link ?? '',
description: r.snippet,
source: r.displayed_link,
}))
responseAdapter(data: unknown) {
const rec = (data ?? {}) as Record<string, unknown>
const rawResults = Array.isArray(rec.organic_results) ? rec.organic_results : []
return (rawResults as unknown[]).map((r) => {
const item = (r ?? {}) as Record<string, unknown>
return {
title: typeof item.title === 'string' ? item.title : '',
url: typeof item.link === 'string' ? item.link : '',
description: typeof item.snippet === 'string' ? item.snippet : undefined,
source: typeof item.displayed_link === 'string' ? item.displayed_link : undefined,
}
})
},
},
}
@@ -416,7 +444,7 @@ function resolveConfig(): {
queryParam: string
method: string
jsonPath?: string
responseAdapter?: (data: any) => SearchHit[]
responseAdapter?: (data: unknown) => SearchHit[]
preset?: ProviderPreset
} {
const providerName = process.env.WEB_PROVIDER
@@ -553,18 +581,18 @@ function buildRequest(query: string) {
// Response parsing — flexible, handles many shapes
// ---------------------------------------------------------------------------
function walkJsonPath(obj: any, path: string): any {
let current = obj
function walkJsonPath(obj: unknown, path: string): unknown {
let current: unknown = obj
for (const seg of path.split('.')) {
if (current == null || typeof current !== 'object') return undefined
current = current[seg]
current = (current as Record<string, unknown>)[seg]
}
return current
}
function extractFromNode(node: any): SearchHit[] {
function extractFromNode(node: unknown): SearchHit[] {
if (!node) return []
if (Array.isArray(node)) return node.map(normalizeHit).filter(Boolean) as SearchHit[]
if (Array.isArray(node)) return (node as unknown[]).map(normalizeHit).filter(Boolean) as SearchHit[]
if (typeof node === 'object') {
const all: SearchHit[] = []
for (const sub of Object.values(node)) all.push(...extractFromNode(sub))
@@ -574,19 +602,20 @@ function extractFromNode(node: any): SearchHit[] {
return []
}
export function extractHits(raw: any, jsonPath?: string): SearchHit[] {
export function extractHits(raw: unknown, jsonPath?: string): SearchHit[] {
if (jsonPath) return extractFromNode(walkJsonPath(raw, jsonPath))
if (Array.isArray(raw)) return raw.map(normalizeHit).filter(Boolean) as SearchHit[]
if (Array.isArray(raw)) return (raw as unknown[]).map(normalizeHit).filter(Boolean) as SearchHit[]
if (!raw || typeof raw !== 'object') return []
const arrayKeys = ['results', 'items', 'data', 'web', 'organic_results', 'hits', 'entries']
const obj = raw as Record<string, unknown>
for (const key of arrayKeys) {
const val = raw[key]
if (Array.isArray(val)) return val.map(normalizeHit).filter(Boolean) as SearchHit[]
const val = obj[key]
if (Array.isArray(val)) return (val as unknown[]).map(normalizeHit).filter(Boolean) as SearchHit[]
if (val && typeof val === 'object' && !Array.isArray(val)) {
const all: SearchHit[] = []
for (const sub of Object.values(val)) {
if (Array.isArray(sub)) all.push(...(sub.map(normalizeHit).filter(Boolean) as SearchHit[]))
for (const sub of Object.values(val as Record<string, unknown>)) {
if (Array.isArray(sub)) all.push(...((sub as unknown[]).map(normalizeHit).filter(Boolean) as SearchHit[]))
}
if (all.length > 0) return all
}
@@ -599,7 +628,7 @@ export function extractHits(raw: any, jsonPath?: string): SearchHit[] {
// Fetch with one retry + timeout
// ---------------------------------------------------------------------------
async function fetchWithRetry(url: string, init: RequestInit, signal?: AbortSignal): Promise<any> {
async function fetchWithRetry(url: string, init: RequestInit, signal?: AbortSignal): Promise<unknown> {
const timeoutSec = readPositiveEnvNumber(process.env.WEB_CUSTOM_TIMEOUT_SEC, DEFAULT_TIMEOUT_SECONDS)
const timeoutMs = timeoutSec * 1000
let lastErr: Error | undefined
+25 -13
View File
@@ -24,12 +24,14 @@ import { applyDomainFilters, safeHostname, type ProviderOutput } from './types.j
import { fetchJsonWithWebSearchTimeout } from './timeout.js'
/** Join up to 3 highlight excerpts with an ellipsis separator. */
function describeFromHighlights(r: any): string | undefined {
const highlights = Array.isArray(r?.highlights) ? r.highlights : null
function describeFromHighlights(r: unknown): string | undefined {
if (!r || typeof r !== 'object') return undefined
const rec = r as Record<string, unknown>
const highlights = Array.isArray(rec.highlights) ? rec.highlights : null
if (highlights && highlights.length > 0) {
return highlights.slice(0, 3).join(' … ')
return (highlights as unknown[]).slice(0, 3).filter((s): s is string => typeof s === 'string').join(' … ')
}
if (typeof r?.text === 'string' && r.text) return r.text
if (typeof rec.text === 'string' && rec.text) return rec.text
return undefined
}
@@ -43,7 +45,7 @@ export const exaProvider: SearchProvider = {
async search(input: SearchInput, signal?: AbortSignal): Promise<ProviderOutput> {
const start = performance.now()
const body: Record<string, any> = {
const body: Record<string, unknown> = {
query: input.query,
numResults: 15,
type: 'auto',
@@ -53,7 +55,7 @@ export const exaProvider: SearchProvider = {
if (input.allowed_domains?.length) body.includeDomains = input.allowed_domains
if (input.blocked_domains?.length) body.excludeDomains = input.blocked_domains
const data = await fetchJsonWithWebSearchTimeout(
const data = (await fetchJsonWithWebSearchTimeout(
'https://api.exa.ai/search',
{
method: 'POST',
@@ -65,14 +67,24 @@ export const exaProvider: SearchProvider = {
},
signal,
{ providerName: 'Exa' },
)
)) as { results?: unknown }
const hits = (data.results ?? []).map((r: any) => ({
title: r.title ?? '',
url: r.url ?? '',
description: describeFromHighlights(r),
source: r.url ? safeHostname(r.url) : undefined,
}))
const results = Array.isArray(data.results) ? data.results : []
const hits = (results as unknown[]).map((r): {
title: string
url: string
description: string | undefined
source: string | undefined
} => {
const rec = (r ?? {}) as Record<string, unknown>
const url = typeof rec.url === 'string' ? rec.url : ''
return {
title: typeof rec.title === 'string' ? rec.title : '',
url,
description: describeFromHighlights(r),
source: url ? safeHostname(url) : undefined,
}
})
return {
// Exa handles domain filtering server-side via includeDomains/excludeDomains
+21 -8
View File
@@ -22,7 +22,7 @@ export const jinaProvider: SearchProvider = {
url.searchParams.set('q', input.query)
url.searchParams.set('count', '10')
const data = await fetchJsonWithWebSearchTimeout(
const data = (await fetchJsonWithWebSearchTimeout(
url.toString(),
{
headers: {
@@ -32,14 +32,27 @@ export const jinaProvider: SearchProvider = {
},
signal,
{ providerName: 'Jina' },
)
)) as { data?: unknown; results?: unknown } | undefined
const hits = (data.data ?? data.results ?? []).map((r: any) => ({
title: r.title ?? '',
url: r.url ?? '',
description: r.description ?? r.snippet ?? r.content,
source: r.url ? safeHostname(r.url) : undefined,
}))
const rawResults = Array.isArray(data?.data) ? data.data : (Array.isArray(data?.results) ? data.results : [])
const hits = (rawResults as unknown[]).map((r) => {
const rec = (r ?? {}) as Record<string, unknown>
const url = typeof rec.url === 'string' ? rec.url : ''
const desc =
typeof rec.description === 'string'
? rec.description
: typeof rec.snippet === 'string'
? rec.snippet
: typeof rec.content === 'string'
? rec.content
: undefined
return {
title: typeof rec.title === 'string' ? rec.title : '',
url,
description: desc,
source: url ? safeHostname(url) : undefined,
}
})
return {
hits: applyDomainFilters(hits, input),
+21 -8
View File
@@ -18,7 +18,7 @@ export const linkupProvider: SearchProvider = {
async search(input: SearchInput, signal?: AbortSignal): Promise<ProviderOutput> {
const start = performance.now()
const data = await fetchJsonWithWebSearchTimeout(
const data = (await fetchJsonWithWebSearchTimeout(
'https://api.linkup.so/v1/search',
{
method: 'POST',
@@ -34,14 +34,27 @@ export const linkupProvider: SearchProvider = {
},
signal,
{ providerName: 'Linkup' },
)
)) as { results?: unknown } | undefined
const hits = (data.results ?? []).map((r: any) => ({
title: r.name ?? r.title ?? '',
url: r.url ?? '',
description: r.snippet ?? r.description ?? r.content,
source: r.url ? safeHostname(r.url) : undefined,
}))
const rawResults = Array.isArray(data?.results) ? data.results : []
const hits = (rawResults as unknown[]).map((r) => {
const rec = (r ?? {}) as Record<string, unknown>
const url = typeof rec.url === 'string' ? rec.url : ''
const desc =
typeof rec.snippet === 'string'
? rec.snippet
: typeof rec.description === 'string'
? rec.description
: typeof rec.content === 'string'
? rec.content
: undefined
return {
title: typeof rec.name === 'string' ? rec.name : (typeof rec.title === 'string' ? rec.title : ''),
url,
description: desc,
source: url ? safeHostname(url) : undefined,
}
})
return {
hits: applyDomainFilters(hits, input),
+23 -9
View File
@@ -30,23 +30,37 @@ export const mojeekProvider: SearchProvider = {
headers['Authorization'] = `Bearer ${process.env.MOJEEK_API_KEY}`
}
const data = await fetchJsonWithWebSearchTimeout(
const data = (await fetchJsonWithWebSearchTimeout(
url.toString(),
{
headers,
},
signal,
{ providerName: 'Mojeek' },
)
)) as { response?: { results?: unknown }; results?: unknown } | undefined
const rawResults = data?.response?.results ?? data?.results ?? []
const nestedResults = data?.response?.results
const rawResults = Array.isArray(nestedResults)
? nestedResults
: Array.isArray(data?.results)
? data.results
: []
const hits = rawResults.map((r: any) => ({
title: r.title ?? '',
url: r.url ?? '',
description: r.snippet ?? r.desc,
source: r.url ? safeHostname(r.url) : undefined,
}))
const hits = (rawResults as unknown[]).map((r) => {
const rec = (r ?? {}) as Record<string, unknown>
const url = typeof rec.url === 'string' ? rec.url : ''
return {
title: typeof rec.title === 'string' ? rec.title : '',
url,
description:
typeof rec.snippet === 'string'
? rec.snippet
: typeof rec.desc === 'string'
? rec.desc
: undefined,
source: url ? safeHostname(url) : undefined,
}
})
return {
hits: applyDomainFilters(hits, input),
+18 -8
View File
@@ -18,7 +18,7 @@ export const tavilyProvider: SearchProvider = {
async search(input: SearchInput, signal?: AbortSignal): Promise<ProviderOutput> {
const start = performance.now()
const data = await fetchJsonWithWebSearchTimeout(
const data = (await fetchJsonWithWebSearchTimeout(
'https://api.tavily.com/search',
{
method: 'POST',
@@ -34,14 +34,24 @@ export const tavilyProvider: SearchProvider = {
},
signal,
{ providerName: 'Tavily' },
)
)) as { results?: unknown } | undefined
const hits = (data.results ?? []).map((r: any) => ({
title: r.title ?? '',
url: r.url ?? '',
description: r.content ?? r.snippet,
source: r.url ? safeHostname(r.url) : undefined,
}))
const rawResults = Array.isArray(data?.results) ? data.results : []
const hits = (rawResults as unknown[]).map((r) => {
const rec = (r ?? {}) as Record<string, unknown>
const url = typeof rec.url === 'string' ? rec.url : ''
return {
title: typeof rec.title === 'string' ? rec.title : '',
url,
description:
typeof rec.content === 'string'
? rec.content
: typeof rec.snippet === 'string'
? rec.snippet
: undefined,
source: url ? safeHostname(url) : undefined,
}
})
return {
hits: applyDomainFilters(hits, input),
+1 -1
View File
@@ -133,7 +133,7 @@ export async function fetchJsonWithWebSearchTimeout(
init: Parameters<typeof fetch>[1],
signal: AbortSignal | undefined,
options: WebSearchTimeoutOptions = {},
): Promise<any> {
): Promise<unknown> {
const providerName = options.providerName ?? 'Web search provider'
return withWebSearchTimeout(
+6 -3
View File
@@ -53,15 +53,18 @@ const SOURCE_KEYS = [
'source', 'domain', 'displayLink', 'displayed_link', 'engine',
] as const
function firstMatch(obj: any, keys: readonly string[]): string | undefined {
function firstMatch(obj: unknown, keys: readonly string[]): string | undefined {
if (!obj || typeof obj !== 'object') return undefined
const record = obj as Record<string, unknown>
for (const k of keys) {
if (typeof obj?.[k] === 'string' && obj[k]) return obj[k]
const v = record[k]
if (typeof v === 'string' && v) return v
}
return undefined
}
/** Extract a SearchHit from any object shape using well-known field aliases. */
export function normalizeHit(raw: any): SearchHit | null {
export function normalizeHit(raw: unknown): SearchHit | null {
if (!raw || typeof raw !== 'object') return null
const title = firstMatch(raw, TITLE_KEYS)
const url = firstMatch(raw, URL_KEYS)
+27 -9
View File
@@ -22,24 +22,42 @@ export const youProvider: SearchProvider = {
url.searchParams.set('query', input.query)
url.searchParams.set('num_web_results', '10')
const data = await fetchJsonWithWebSearchTimeout(
const data = (await fetchJsonWithWebSearchTimeout(
url.toString(),
{
headers: { 'X-API-Key': process.env.YOU_API_KEY! },
},
signal,
{ providerName: 'You.com' },
)
)) as { results?: { web?: unknown } | unknown } | undefined
const webResults = data?.results?.web ?? data?.results ?? []
const resultsField = data?.results
const nestedWeb =
resultsField && typeof resultsField === 'object' && 'web' in resultsField
? (resultsField as { web?: unknown }).web
: undefined
const rawResults = Array.isArray(nestedWeb)
? nestedWeb
: Array.isArray(resultsField)
? resultsField
: []
const hits = webResults.map((r: any) => {
const snippet = Array.isArray(r.snippets) ? r.snippets[0] : r.snippet
const hits = (rawResults as unknown[]).map((r) => {
const rec = (r ?? {}) as Record<string, unknown>
const url = typeof rec.url === 'string' ? rec.url : ''
const snippets = Array.isArray(rec.snippets) ? rec.snippets : []
const snippet = snippets.length > 0 && typeof snippets[0] === 'string' ? snippets[0] : undefined
return {
title: r.title ?? '',
url: r.url ?? '',
description: snippet ?? r.description,
source: r.url ? safeHostname(r.url) : undefined,
title: typeof rec.title === 'string' ? rec.title : '',
url,
description:
snippet ??
(typeof rec.snippet === 'string'
? rec.snippet
: typeof rec.description === 'string'
? rec.description
: undefined),
source: url ? safeHostname(url) : undefined,
}
})
+7 -2
View File
@@ -275,8 +275,13 @@ function extractTextFromContent(content: unknown): string {
if (typeof content === 'string') return content
if (Array.isArray(content)) {
return content
.filter((block: any) => block.type === 'text' && typeof block.text === 'string')
.map((block: any) => block.text)
.filter((block: unknown): block is { type: string; text: string } =>
typeof block === 'object' &&
block !== null &&
(block as { type?: unknown }).type === 'text' &&
typeof (block as { text?: unknown }).text === 'string',
)
.map((block) => block.text)
.join('\n')
}
return ''
+108 -33
View File
@@ -141,10 +141,43 @@ interface LegacySource {
path: string
kind: 'json' | 'sqlite'
mtimeMs: number
data: any
data: LegacyData
artifactBytes: Map<string, Buffer>
}
// Legacy knowledge-graph shapes (JSON stores written by older OpenClaude
// versions and the SQLite store read at line ~400). All field types are
// `unknown` because the on-disk format predates our schema; helpers in this
// file sanitize as they read.
type LegacyEntity = {
id?: unknown
name?: unknown
type?: unknown
attributes?: Record<string, unknown>
}
type LegacyRelation = {
sourceId?: unknown
targetId?: unknown
type?: unknown
}
type LegacySummary = {
id?: unknown
content?: unknown
keywords?: unknown
timestamp?: unknown
}
// Some older JSON dumps stored entities as an array; the SQLite reader
// produces a record keyed by id. Both shapes are normalized to the
// `Record<string, LegacyEntity>` form before `mergeLegacySources` consumes them.
type LegacyEntityBag = Record<string, LegacyEntity> | LegacyEntity[]
type LegacyData = {
entities: LegacyEntityBag
relations: LegacyRelation[]
summaries: LegacySummary[]
rules: string[]
_droppedEntityAliases?: Map<string, string>
}
function getLegacySourceMtime(path: string, kind: LegacySource['kind']): number {
let mtimeMs = statSync(path).mtimeMs
if (kind === 'sqlite') {
@@ -219,12 +252,34 @@ function sqliteDataArtifactsMatch(
})
}
function normalizeLegacyData(value: any): any {
function normalizeLegacyData(value: unknown): LegacyData {
const v = value as Partial<LegacyData> | null | undefined
const rawEntities = v?.entities
let entities: LegacyEntityBag = {}
if (Array.isArray(rawEntities)) {
// Array form: mergeLegacySources falls back to `String(id ?? entryKey)`,
// so key the array form by the index to preserve the same fallback
// contract. An entry with an explicit id overrides any previous entry
// with the same id (last-wins) so concurrent stores converge to the
// newest copy; entryKey collisions across arrays are not possible
// because the outer `mergeLegacySources` loop never sees two arrays at
// once.
const out: Record<string, LegacyEntity> = {}
for (let i = 0; i < rawEntities.length; i++) {
const e = rawEntities[i]
if (e && typeof e === 'object') {
out[String(i)] = e as LegacyEntity
}
}
entities = out
} else if (rawEntities && typeof rawEntities === 'object') {
entities = rawEntities as Record<string, LegacyEntity>
}
return {
entities: value?.entities && typeof value.entities === 'object' ? value.entities : {},
relations: Array.isArray(value?.relations) ? value.relations : [],
summaries: Array.isArray(value?.summaries) ? value.summaries : [],
rules: Array.isArray(value?.rules) ? value.rules : [],
entities,
relations: Array.isArray(v?.relations) ? (v.relations as LegacyRelation[]) : [],
summaries: Array.isArray(v?.summaries) ? (v.summaries as LegacySummary[]) : [],
rules: Array.isArray(v?.rules) ? (v.rules as string[]) : [],
}
}
@@ -241,7 +296,7 @@ function isLegacyDataShape(value: unknown): boolean {
}
/** Merge every recoverable legacy location, preferring the newest copy on conflicts. */
function mergeLegacySources(sources: LegacySource[]): any {
function mergeLegacySources(sources: LegacySource[]): LegacyData {
const merged = normalizeLegacyData(null)
const droppedAliases = new Map<string, string>()
const entityNames = new Map<string, string>()
@@ -251,7 +306,7 @@ function mergeLegacySources(sources: LegacySource[]): any {
for (const source of [...sources].sort((a, b) => b.mtimeMs - a.mtimeMs)) {
const data = normalizeLegacyData(source.data)
for (const [entryKey, rawEntity] of Object.entries(data.entities) as [string, any][]) {
for (const [entryKey, rawEntity] of Object.entries(data.entities)) {
if (!rawEntity || typeof rawEntity !== 'object') continue
const id = String(rawEntity.id ?? entryKey)
const nameKey = String(rawEntity.name ?? '').trim().toLowerCase()
@@ -394,18 +449,23 @@ function migrateLegacyKnowledgeGraph(): void {
}
type SqliteReadResult =
| { ok: true; data: any }
| { ok: true; data: LegacyData }
| { ok: false; reason: 'not_found' | 'unavailable' | 'error' }
interface SqliteDb {
close?: () => void
}
type SqliteRow = Record<string, unknown>
function readLegacySqliteStore(dbPath: string): SqliteReadResult {
if (!existsSync(dbPath)) return { ok: false, reason: 'not_found' }
let openDatabase: () => { db: any; queryAll: (sql: string) => any[] }
let openDatabase: () => { db: SqliteDb; queryAll: (sql: string) => SqliteRow[] }
try {
const Database = _require('bun:sqlite').Database
openDatabase = () => {
const db = new Database(dbPath, { readonly: true })
return { db, queryAll: sql => db.query(sql).all() as any[] }
const db: { query: (sql: string) => { all: () => SqliteRow[] }; close?: () => void } = new Database(dbPath, { readonly: true })
return { db, queryAll: sql => db.query(sql).all() }
}
} catch {
try {
@@ -414,8 +474,8 @@ function readLegacySqliteStore(dbPath: string): SqliteReadResult {
// OpenClaude install can still migrate after the user changes runtimes.
const DatabaseSync = _require('node:sqlite').DatabaseSync
openDatabase = () => {
const db = new DatabaseSync(dbPath, { readOnly: true })
return { db, queryAll: sql => db.prepare(sql).all() as any[] }
const db: { prepare: (sql: string) => { all: () => SqliteRow[] }; close?: () => void } = new DatabaseSync(dbPath, { readOnly: true })
return { db, queryAll: sql => db.prepare(sql).all() }
}
} catch {
console.error(
@@ -425,43 +485,58 @@ function readLegacySqliteStore(dbPath: string): SqliteReadResult {
}
}
let db: any
let db: SqliteDb | null = null
try {
const opened = openDatabase()
db = opened.db
const queryAll = opened.queryAll
const data: any = { entities: {}, relations: [], summaries: [], rules: [] }
const data: LegacyData = { entities: {}, relations: [], summaries: [], rules: [] }
const entityRows = queryAll('SELECT id, type, name, attributes FROM entities')
for (const row of entityRows) {
data.entities[row.id] = {
const id = String(row.id ?? '')
const rawAttrs = row.attributes
data.entities[id] = {
id: row.id,
type: row.type ?? '',
name: row.name ?? '',
attributes: row.attributes ? JSON.parse(row.attributes) : {},
attributes:
typeof rawAttrs === 'string' && rawAttrs.length > 0
? (JSON.parse(rawAttrs) as Record<string, unknown>)
: {},
}
}
data.relations = queryAll('SELECT source_id, target_id, type FROM relations').map(
(r: any) => ({ sourceId: r.source_id, targetId: r.target_id, type: r.type }),
)
const summaryRows = queryAll('SELECT id, content, keywords, timestamp FROM summaries')
data.summaries = summaryRows.map((r: any) => ({
id: r.id,
content: r.content ?? '',
keywords: r.keywords ? JSON.parse(r.keywords) : [],
timestamp: r.timestamp ?? 0,
data.relations = queryAll('SELECT source_id, target_id, type FROM relations').map(r => ({
sourceId: r.source_id,
targetId: r.target_id,
type: r.type,
}))
data.rules = queryAll('SELECT content FROM rules').map((r: any) => r.content)
const summaryRows = queryAll('SELECT id, content, keywords, timestamp FROM summaries')
data.summaries = summaryRows.map(r => {
const rawKeywords = r.keywords
return {
id: r.id,
content: r.content ?? '',
keywords:
typeof rawKeywords === 'string' && rawKeywords.length > 0
? (JSON.parse(rawKeywords) as unknown)
: [],
timestamp: r.timestamp ?? 0,
}
})
data.rules = queryAll('SELECT content FROM rules')
.map(r => r.content)
.filter((c): c is string => typeof c === 'string')
return { ok: true, data }
} catch (e) {
console.error('[knowledgeGraph] Failed to read SQLite store:', e)
return { ok: false, reason: 'error' }
} finally {
try { db?.close() } catch { /* ignore close failures after read */ }
try { db?.close?.() } catch { /* ignore close failures after read */ }
}
}
@@ -547,7 +622,7 @@ function archiveLegacySources(sources: LegacySource[]): boolean {
return true
}
function doMigration(data: any, sources: LegacySource[], projectKey: string): void {
function doMigration(data: LegacyData, sources: LegacySource[], projectKey: string): void {
// Archive and byte-verify every discovered source before writing facts. If
// any root/cwd store cannot be preserved, leave all live stores in place and
// retry later rather than completing a partial migration.
@@ -590,7 +665,7 @@ function doMigration(data: any, sources: LegacySource[], projectKey: string): vo
// into the YAML title and body (P1). The old fact extractor stored raw env
// values in both attributes and names.
const legacyEntities = Object.entries(data.entities ?? {})
for (const [legacyId, entity] of legacyEntities as [string, any][]) {
for (const [legacyId, entity] of legacyEntities) {
const safeName = safeEntityName(entity)
if (!safeName) {
continue
@@ -681,7 +756,7 @@ ${safeRule}
}
// Preserve legacy relations as a single relation-set fact (remapped using legacyToNewId, H4)
const relations: Relation[] = (data.relations ?? []).flatMap((r: any) => {
const relations: Relation[] = (data.relations ?? []).flatMap((r: LegacyRelation) => {
const rawSourceId = String(r.sourceId ?? '')
const rawTargetId = String(r.targetId ?? '')
const sourceId = legacyToNewId.get(rawSourceId) || sanitizeLegacyReference(rawSourceId)
+8 -6
View File
@@ -1745,25 +1745,27 @@ export function normalizeMessagesForAPI(
// When tool search is enabled, preserve all fields including 'caller'
if (toolSearchEnabled) {
const { extra_content, ...restBlock } = block as any
const blockWithExtra = block as unknown as { extra_content?: unknown } & Record<string, unknown>
const { extra_content, ...restBlock } = blockWithExtra
return {
...restBlock,
name: canonicalName,
input: normalizedInput,
...(extra_content ? { extra_content } : {})
}
...(extra_content ? { extra_content } : {}),
} as BetaContentBlock
}
// When tool search is NOT enabled, explicitly construct tool_use
// block with only standard API fields to avoid sending fields like
// 'caller' that may be stored in sessions from tool search runs
return {
const blockExtra = (block as unknown as { extra_content?: unknown }).extra_content
return {
type: 'tool_use' as const,
id: block.id,
name: canonicalName,
input: normalizedInput,
...((block as any).extra_content ? { extra_content: (block as any).extra_content } : {})
}
...(blockExtra ? { extra_content: blockExtra } : {}),
} as BetaContentBlock
}
return block
}),
+2 -2
View File
@@ -15,7 +15,7 @@
const runtimeImport = new Function(
'specifier',
'return import(specifier)',
) as (specifier: string) => Promise<any>
) as (specifier: string) => Promise<unknown>
export class OptionalRuntimeModuleUnavailableError extends Error {
constructor(
@@ -37,7 +37,7 @@ export function isOptionalRuntimeModuleUnavailableError(
}
/** Raw runtime import — rejects with the underlying error if the module is missing. */
export function importRuntimeModule(specifier: string): Promise<any> {
export function importRuntimeModule(specifier: string): Promise<unknown> {
return runtimeImport(specifier)
}
+3 -3
View File
@@ -575,11 +575,11 @@ export const SettingsSchema = lazySchema(() =>
'and feed errors back for self-repair.',
),
worktree: z
.preprocess((val: any) => {
.preprocess((val: unknown) => {
if (val && typeof val === 'object') {
const copy = { ...val }
const copy: Record<string, unknown> = { ...(val as Record<string, unknown>) }
if ('enableGitLongPaths' in val && !('autoConfigureLongPaths' in val)) {
copy.autoConfigureLongPaths = val.enableGitLongPaths
copy.autoConfigureLongPaths = (val as Record<string, unknown>).enableGitLongPaths
}
return copy
}
+1 -1
View File
@@ -47,7 +47,7 @@ export function assertObject(value: unknown, field: string): asserts value is Re
/**
* Assert that a value is a function.
*/
export function assertFunction(value: unknown, field: string): asserts value is (...args: any[]) => any {
export function assertFunction(value: unknown, field: string): asserts value is (...args: unknown[]) => unknown {
if (typeof value !== 'function') {
throw new TypeError(`missing or invalid '${field}' (expected function)`)
}