mirror of
https://github.com/Gitlawb/openclaude.git
synced 2026-11-06 12:50:54 -04:00
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:
co-authored by
Gravirei
Claude Opus 4.6
parent
35d7edc2f6
commit
6d09260291
@@ -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=="],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"total": 779,
|
||||
"generatedAt": "2026-09-06T02:24:17.929Z"
|
||||
}
|
||||
@@ -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()
|
||||
@@ -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
@@ -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()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,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
@@ -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)
|
||||
|
||||
Vendored
+26
-6
@@ -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
|
||||
},
|
||||
|
||||
@@ -168,7 +168,7 @@ export {
|
||||
* )
|
||||
* ```
|
||||
*/
|
||||
export function tool<Schema = any>(
|
||||
export function tool<Schema = unknown>(
|
||||
name: string,
|
||||
description: string,
|
||||
inputSchema: Schema,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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"
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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
@@ -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
@@ -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')
|
||||
}
|
||||
|
||||
@@ -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') ||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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.'
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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,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),
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}),
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user