@askalf/dario 3.31.13 → 3.31.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -1
- package/dist/accounts.d.ts +34 -0
- package/dist/accounts.js +68 -4
- package/dist/cc-authorize-probe.js +5 -1
- package/dist/cli.js +17 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/oauth.js +6 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -202,6 +202,8 @@ dario accounts list
|
|
|
202
202
|
dario proxy
|
|
203
203
|
```
|
|
204
204
|
|
|
205
|
+
If you already have a single-account `dario login` set up and run `dario accounts add <alias>` for the first time, dario **back-fills** your existing login credentials into the pool under the reserved alias `login` before running OAuth for the new alias. Net effect: your first `accounts add` gives you two pool accounts (login + new alias), pool mode activates immediately. Back-fill is one-shot, idempotent, and never touches your existing `credentials.json` — if you later `dario accounts remove` below the 2+ threshold, single-account mode reads it unchanged. Skipped if you explicitly pick `login` as the new alias — your intent wins.
|
|
206
|
+
|
|
205
207
|
Each request picks the account with the highest headroom:
|
|
206
208
|
|
|
207
209
|
```
|
|
@@ -340,7 +342,7 @@ A version marker (`<!-- dario-sub-agent-version: X -->`) embedded in the markdow
|
|
|
340
342
|
| `dario status` | Show Claude backend OAuth token health and expiry |
|
|
341
343
|
| `dario refresh` | Force an immediate Claude token refresh |
|
|
342
344
|
| `dario logout` | Delete stored Claude credentials |
|
|
343
|
-
| `dario accounts list` / `add <alias>` / `remove <alias>` | Multi-account pool management |
|
|
345
|
+
| `dario accounts list` / `add <alias>` / `remove <alias>` | Multi-account pool management. `add <alias>` on a fresh pool auto back-fills your existing `dario login` credentials as `login`, so your first `add` trips the 2+ pool threshold on its own — see [Multi-account pool mode](#multi-account-pool-mode). |
|
|
344
346
|
| `dario backend list` / `add <name> --key=<key> [--base-url=<url>]` / `remove <name>` | OpenAI-compat backend management |
|
|
345
347
|
| `dario shim -- <cmd> [args...]` | Run a child process with the in-process fetch patch (see [Shim mode](#shim-mode)) |
|
|
346
348
|
| `dario subagent install` / `remove` / `status` | CC sub-agent lifecycle (v3.26 — see [sub-agent hook](#claude-code-sub-agent-hook-v326)) |
|
|
@@ -762,6 +764,17 @@ Yes — anything that speaks the OpenAI Chat Completions API. Groq, OpenRouter,
|
|
|
762
764
|
**Something's wrong. Where do I start?**
|
|
763
765
|
`dario doctor`. One command, one aggregated report — dario version, Node, platform, runtime/TLS classification, CC binary compat, template source + age + drift, OAuth status, pool state, backends, sub-agent install state, home dir. Exit code 1 if any check fails. Paste the output when you file an issue. (If you're inside Claude Code, `dario subagent install` once and then ask CC to "use the dario sub-agent to run doctor" — same output, no context switch.)
|
|
764
766
|
|
|
767
|
+
**OpenClaw returns 401 after I set `DARIO_API_KEY` (or upgrade past v3.30.6).**
|
|
768
|
+
If you run `dario proxy --host=0.0.0.0` (non-loopback), dario requires `DARIO_API_KEY` to be set so it's not an open subscription relay. OpenClaw 2026.2.17+ prefers `~/.openclaw/agents/main/agent/auth-profiles.json` over `openclaw.json`'s `apiKey` field or the `ANTHROPIC_API_KEY` env var — so if you have a stale Anthropic token in `auth-profiles.json` from an earlier setup, OpenClaw sends *that* token instead of `dario`, and dario rejects the request with `Authorization present but value mismatch` (visible under `dario proxy -v`, added in v3.31.2).
|
|
769
|
+
|
|
770
|
+
Three fixes, in order of simplicity:
|
|
771
|
+
|
|
772
|
+
1. **Use loopback.** `dario proxy --host=127.0.0.1` — auth only enforced on non-loopback binds, no `DARIO_API_KEY` required, no OpenClaw changes. Best if you don't actually need LAN reach to dario.
|
|
773
|
+
2. **Delete the Anthropic auth profile.** Remove the `"anthropic:default"` entry from `~/.openclaw/agents/main/agent/auth-profiles.json`. OpenClaw then falls back through the config chain and picks up `ANTHROPIC_API_KEY=dario` from the env. Confirmed working by [@tetsuco in #97](https://github.com/askalf/dario/issues/97).
|
|
774
|
+
3. **Overwrite the auth profile.** `openclaw models auth paste-token --provider anthropic` and paste `dario`. Replaces whatever key was in there — keep a backup if you use it elsewhere.
|
|
775
|
+
|
|
776
|
+
Diagnose with `dario proxy -v` — the reject log (v3.31.2+) reports header-name only (never the value, since it may be a real credential you mistyped) and tells you which of the three configs is actually being hit.
|
|
777
|
+
|
|
765
778
|
**What happens when Anthropic rotates the OAuth config?**
|
|
766
779
|
Dario auto-detects OAuth config from the installed Claude Code binary. When CC ships a new version with rotated values, dario picks them up on the next run. Cache at `~/.dario/cc-oauth-cache-v6.json`, keyed by the CC binary fingerprint. The cache path version bumps each time the canonical OAuth config shape changes so stale caches regenerate automatically on upgrade — v3 → v4 in v3.19.4 (scope-list flip CC v2.1.104 → v2.1.107), v4 → v5 in v3.31.3 (authorize URL `claude.com/cai/` → `claude.ai/` host normalization), v5 → v6 in v3.31.4 (6-scope restore after CC v2.1.116).
|
|
767
780
|
|
|
@@ -781,6 +794,9 @@ Env vars win over the file. Set `DARIO_OAUTH_DISABLE_OVERRIDE=1` to force pure a
|
|
|
781
794
|
**What happens when Anthropic changes the CC request template?**
|
|
782
795
|
Dario extracts the live request template from your installed Claude Code binary on startup — the system prompt, tool schemas, user-agent, beta flags, header insertion order, static header values, and top-level request-body key order — and uses those to replay requests instead of a version pinned into dario itself. When CC ships a new version with a tweaked template, the next `dario proxy` run picks it up automatically. Drift detection forces a refresh when the installed CC version changes under dario, and the nightly `cc-drift-watch` workflow catches upstream rotations (client_id, URLs, tool set, version) the day they ship on npm.
|
|
783
796
|
|
|
797
|
+
**Why does `dario accounts list` show an account called `login` I never added?**
|
|
798
|
+
That's your existing `dario login` credentials, back-filled into the pool automatically on your first `dario accounts add <alias>`. Pool mode activates at 2+ accounts in `~/.dario/accounts/`, and the single-account `credentials.json` store lives outside that directory — so without the back-fill, one `accounts add` would leave you at 1 pool entry and your login account orphaned. The `login` alias is reserved for this path. Safe to `dario accounts remove login` if you don't want it pooled; the original `credentials.json` is untouched by the back-fill, so single-account mode resumes reading it after removal drops you below the 2+ threshold. See [Multi-account pool mode](#multi-account-pool-mode) for the full picture.
|
|
799
|
+
|
|
784
800
|
**First time setup on a fresh Claude account.**
|
|
785
801
|
If dario is the first thing you run against a brand-new Claude account, prime the account with a few real Claude Code commands first:
|
|
786
802
|
```bash
|
package/dist/accounts.d.ts
CHANGED
|
@@ -23,3 +23,37 @@ export declare function _accountRefreshesInFlightSizeForTest(): number;
|
|
|
23
23
|
*/
|
|
24
24
|
export declare function addAccountViaOAuth(alias: string): Promise<AccountCredentials>;
|
|
25
25
|
export declare function getAccountsDir(): string;
|
|
26
|
+
/**
|
|
27
|
+
* Alias reserved for credentials auto-migrated from the single-account
|
|
28
|
+
* `dario login` store. Named `login` so it's semantically obvious where
|
|
29
|
+
* the entry came from and unlikely to collide with user-chosen aliases
|
|
30
|
+
* like `work`, `personal`, etc. If a user specifically requests `login`
|
|
31
|
+
* as the alias for `dario accounts add`, the caller falls back to
|
|
32
|
+
* `default` so the migration doesn't step on the user's intent.
|
|
33
|
+
*/
|
|
34
|
+
export declare const MIGRATED_LOGIN_ALIAS = "login";
|
|
35
|
+
/**
|
|
36
|
+
* Promote the user's existing single-account `dario login` credentials
|
|
37
|
+
* (`~/.dario/credentials.json`, `~/.claude/.credentials.json`, or OS
|
|
38
|
+
* keychain — whichever `loadCredentials` finds) into the pool under a
|
|
39
|
+
* reserved alias.
|
|
40
|
+
*
|
|
41
|
+
* Why: the pool activation threshold is 2+ accounts in `~/.dario/accounts/`.
|
|
42
|
+
* A user with one `dario login` account + one `dario accounts add bar`
|
|
43
|
+
* ends up with only one account in `accounts/` (bar), pool mode never
|
|
44
|
+
* trips, and the login account is effectively orphaned while pool is off.
|
|
45
|
+
* Calling this on the first `dario accounts add` back-fills the login
|
|
46
|
+
* account into the pool so the second `add` crosses the threshold.
|
|
47
|
+
*
|
|
48
|
+
* Idempotent: no-op if `accounts/` already has any entry, no-op if no
|
|
49
|
+
* credentials are reachable anywhere. Returns the alias written to, or
|
|
50
|
+
* `null` when nothing happened.
|
|
51
|
+
*
|
|
52
|
+
* The source `credentials.json` (if present) is left untouched — single-
|
|
53
|
+
* account mode still reads it if the user later `accounts remove`s down
|
|
54
|
+
* below the pool threshold. Migration is copy-only, never destructive.
|
|
55
|
+
*
|
|
56
|
+
* @param preferredAlias caller may request a specific alias. If it's
|
|
57
|
+
* already the reserved `login` (or collides), falls back to `default`.
|
|
58
|
+
*/
|
|
59
|
+
export declare function ensureLoginCredentialsInPool(alias?: string): Promise<string | null>;
|
package/dist/accounts.js
CHANGED
|
@@ -2,10 +2,15 @@
|
|
|
2
2
|
* Multi-account credential storage.
|
|
3
3
|
*
|
|
4
4
|
* Accounts live at `~/.dario/accounts/<alias>.json`. Single-account dario
|
|
5
|
-
*
|
|
6
|
-
* When `~/.dario/accounts/` contains 2+ files the proxy
|
|
7
|
-
* (see pool.ts). Each account has its own independent
|
|
8
|
-
* can refresh without affecting the others.
|
|
5
|
+
* uses `~/.dario/credentials.json` (plus the CC file + OS keychain fallback
|
|
6
|
+
* paths in oauth.ts). When `~/.dario/accounts/` contains 2+ files the proxy
|
|
7
|
+
* activates pool mode (see pool.ts). Each account has its own independent
|
|
8
|
+
* OAuth lifecycle and can refresh without affecting the others.
|
|
9
|
+
*
|
|
10
|
+
* `ensureLoginCredentialsInPool` (below) bridges the two stores on the
|
|
11
|
+
* first `dario accounts add` — it promotes the user's existing login
|
|
12
|
+
* credentials into the pool under a reserved alias so that adding a
|
|
13
|
+
* second account actually trips the 2+ threshold and activates pooling.
|
|
9
14
|
*
|
|
10
15
|
* OAuth config (client_id, scopes, authorize URL, token URL) comes from
|
|
11
16
|
* dario's cc-oauth-detect scanner — the same source the single-account
|
|
@@ -17,6 +22,7 @@ import { homedir } from 'node:os';
|
|
|
17
22
|
import { randomUUID, randomBytes, createHash } from 'node:crypto';
|
|
18
23
|
import { createServer } from 'node:http';
|
|
19
24
|
import { detectCCOAuthConfig } from './cc-oauth-detect.js';
|
|
25
|
+
import { loadCredentials } from './oauth.js';
|
|
20
26
|
const DARIO_DIR = join(homedir(), '.dario');
|
|
21
27
|
const ACCOUNTS_DIR = join(DARIO_DIR, 'accounts');
|
|
22
28
|
/**
|
|
@@ -308,3 +314,61 @@ export async function addAccountViaOAuth(alias) {
|
|
|
308
314
|
export function getAccountsDir() {
|
|
309
315
|
return ACCOUNTS_DIR;
|
|
310
316
|
}
|
|
317
|
+
/**
|
|
318
|
+
* Alias reserved for credentials auto-migrated from the single-account
|
|
319
|
+
* `dario login` store. Named `login` so it's semantically obvious where
|
|
320
|
+
* the entry came from and unlikely to collide with user-chosen aliases
|
|
321
|
+
* like `work`, `personal`, etc. If a user specifically requests `login`
|
|
322
|
+
* as the alias for `dario accounts add`, the caller falls back to
|
|
323
|
+
* `default` so the migration doesn't step on the user's intent.
|
|
324
|
+
*/
|
|
325
|
+
export const MIGRATED_LOGIN_ALIAS = 'login';
|
|
326
|
+
/**
|
|
327
|
+
* Promote the user's existing single-account `dario login` credentials
|
|
328
|
+
* (`~/.dario/credentials.json`, `~/.claude/.credentials.json`, or OS
|
|
329
|
+
* keychain — whichever `loadCredentials` finds) into the pool under a
|
|
330
|
+
* reserved alias.
|
|
331
|
+
*
|
|
332
|
+
* Why: the pool activation threshold is 2+ accounts in `~/.dario/accounts/`.
|
|
333
|
+
* A user with one `dario login` account + one `dario accounts add bar`
|
|
334
|
+
* ends up with only one account in `accounts/` (bar), pool mode never
|
|
335
|
+
* trips, and the login account is effectively orphaned while pool is off.
|
|
336
|
+
* Calling this on the first `dario accounts add` back-fills the login
|
|
337
|
+
* account into the pool so the second `add` crosses the threshold.
|
|
338
|
+
*
|
|
339
|
+
* Idempotent: no-op if `accounts/` already has any entry, no-op if no
|
|
340
|
+
* credentials are reachable anywhere. Returns the alias written to, or
|
|
341
|
+
* `null` when nothing happened.
|
|
342
|
+
*
|
|
343
|
+
* The source `credentials.json` (if present) is left untouched — single-
|
|
344
|
+
* account mode still reads it if the user later `accounts remove`s down
|
|
345
|
+
* below the pool threshold. Migration is copy-only, never destructive.
|
|
346
|
+
*
|
|
347
|
+
* @param preferredAlias caller may request a specific alias. If it's
|
|
348
|
+
* already the reserved `login` (or collides), falls back to `default`.
|
|
349
|
+
*/
|
|
350
|
+
export async function ensureLoginCredentialsInPool(alias = MIGRATED_LOGIN_ALIAS) {
|
|
351
|
+
if (!safeAliasPath(alias))
|
|
352
|
+
return null;
|
|
353
|
+
const existing = await listAccountAliases();
|
|
354
|
+
if (existing.length > 0)
|
|
355
|
+
return null;
|
|
356
|
+
const creds = await loadCredentials();
|
|
357
|
+
const tok = creds?.claudeAiOauth;
|
|
358
|
+
if (!tok?.accessToken || !tok?.refreshToken)
|
|
359
|
+
return null;
|
|
360
|
+
const identity = (await detectClaudeIdentity()) ?? {
|
|
361
|
+
deviceId: randomUUID(),
|
|
362
|
+
accountUuid: randomUUID(),
|
|
363
|
+
};
|
|
364
|
+
await saveAccount({
|
|
365
|
+
alias,
|
|
366
|
+
accessToken: tok.accessToken,
|
|
367
|
+
refreshToken: tok.refreshToken,
|
|
368
|
+
expiresAt: tok.expiresAt,
|
|
369
|
+
scopes: tok.scopes ?? [],
|
|
370
|
+
deviceId: identity.deviceId,
|
|
371
|
+
accountUuid: identity.accountUuid,
|
|
372
|
+
});
|
|
373
|
+
return alias;
|
|
374
|
+
}
|
|
@@ -131,7 +131,11 @@ export function buildProbeAuthorizeUrl(cfg) {
|
|
|
131
131
|
scope: cfg.scopes,
|
|
132
132
|
code_challenge: pkceChallenge(),
|
|
133
133
|
code_challenge_method: 'S256',
|
|
134
|
-
|
|
134
|
+
// 32 bytes — match what CC v2.1.116+ actually sends. See dario#71.
|
|
135
|
+
// Shorter states produce "Invalid request format" from Anthropic's
|
|
136
|
+
// authorize endpoint, which the probe classifier would otherwise mis-
|
|
137
|
+
// attribute to drift when it's actually our own request shape.
|
|
138
|
+
state: base64url(randomBytes(32)),
|
|
135
139
|
});
|
|
136
140
|
return `${cfg.authorizeUrl}?${params.toString()}`;
|
|
137
141
|
}
|
package/dist/cli.js
CHANGED
|
@@ -38,7 +38,7 @@ import { homedir } from 'node:os';
|
|
|
38
38
|
import { startAutoOAuthFlow, startManualOAuthFlow, detectHeadlessEnvironment, getStatus, refreshTokens, loadCredentials } from './oauth.js';
|
|
39
39
|
import { startProxy, sanitizeError } from './proxy.js';
|
|
40
40
|
import { VALID_EFFORT_VALUES } from './cc-template.js';
|
|
41
|
-
import { listAccountAliases, loadAllAccounts, addAccountViaOAuth, removeAccount } from './accounts.js';
|
|
41
|
+
import { listAccountAliases, loadAllAccounts, addAccountViaOAuth, removeAccount, ensureLoginCredentialsInPool, MIGRATED_LOGIN_ALIAS } from './accounts.js';
|
|
42
42
|
import { listBackends, saveBackend, removeBackend } from './openai-backend.js';
|
|
43
43
|
const args = process.argv.slice(2);
|
|
44
44
|
const command = args[0] ?? 'proxy';
|
|
@@ -463,6 +463,22 @@ async function accounts() {
|
|
|
463
463
|
console.error(`[dario] Account "${alias}" already exists. Remove it first with \`dario accounts remove ${alias}\`.`);
|
|
464
464
|
process.exit(1);
|
|
465
465
|
}
|
|
466
|
+
// If the user has `dario login` credentials on disk or in the keychain
|
|
467
|
+
// and the pool is empty, migrate those credentials into the pool first.
|
|
468
|
+
// Otherwise the new account lives alone in accounts/, pool mode never
|
|
469
|
+
// trips the 2+ threshold, and the login account is orphaned from the
|
|
470
|
+
// pool until the user figures out they have to re-`accounts add` it.
|
|
471
|
+
// Skip silently when the user explicitly picks the reserved alias —
|
|
472
|
+
// their intent wins, they can run `accounts add` again for the login
|
|
473
|
+
// migration under a different alias.
|
|
474
|
+
if (existing.length === 0 && alias !== MIGRATED_LOGIN_ALIAS) {
|
|
475
|
+
const migrated = await ensureLoginCredentialsInPool();
|
|
476
|
+
if (migrated) {
|
|
477
|
+
console.log('');
|
|
478
|
+
console.log(` Migrated your existing \`dario login\` account into the pool as "${migrated}".`);
|
|
479
|
+
console.log(` (Pool mode activates on 2+ accounts — this back-fill plus "${alias}" crosses that.)`);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
466
482
|
console.log('');
|
|
467
483
|
console.log(` Adding account "${alias}" to the pool...`);
|
|
468
484
|
console.log('');
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export type { OAuthTokens, CredentialsFile } from './oauth.js';
|
|
|
9
9
|
export { startProxy, sanitizeError } from './proxy.js';
|
|
10
10
|
export { AccountPool, parseRateLimits } from './pool.js';
|
|
11
11
|
export type { PoolAccount, PoolStatus, RateLimitSnapshot, AccountIdentity } from './pool.js';
|
|
12
|
-
export { listAccountAliases, loadAccount, loadAllAccounts, saveAccount, removeAccount, refreshAccountToken, addAccountViaOAuth, getAccountsDir, } from './accounts.js';
|
|
12
|
+
export { listAccountAliases, loadAccount, loadAllAccounts, saveAccount, removeAccount, refreshAccountToken, addAccountViaOAuth, ensureLoginCredentialsInPool, MIGRATED_LOGIN_ALIAS, getAccountsDir, } from './accounts.js';
|
|
13
13
|
export type { AccountCredentials } from './accounts.js';
|
|
14
14
|
export { Analytics } from './analytics.js';
|
|
15
15
|
export type { RequestRecord, AnalyticsSummary } from './analytics.js';
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ export { startProxy, sanitizeError } from './proxy.js';
|
|
|
10
10
|
// contains 2+ accounts; see README for the progression from single-account
|
|
11
11
|
// mode to pool mode).
|
|
12
12
|
export { AccountPool, parseRateLimits } from './pool.js';
|
|
13
|
-
export { listAccountAliases, loadAccount, loadAllAccounts, saveAccount, removeAccount, refreshAccountToken, addAccountViaOAuth, getAccountsDir, } from './accounts.js';
|
|
13
|
+
export { listAccountAliases, loadAccount, loadAllAccounts, saveAccount, removeAccount, refreshAccountToken, addAccountViaOAuth, ensureLoginCredentialsInPool, MIGRATED_LOGIN_ALIAS, getAccountsDir, } from './accounts.js';
|
|
14
14
|
export { Analytics } from './analytics.js';
|
|
15
15
|
// Multi-provider backends (v3.6.0+). Secondary OpenAI-compat providers
|
|
16
16
|
// (OpenAI, OpenRouter, Groq, local LiteLLM, etc.) configured via
|
package/dist/oauth.js
CHANGED
|
@@ -198,7 +198,10 @@ async function saveCredentials(creds) {
|
|
|
198
198
|
export async function startAutoOAuthFlow() {
|
|
199
199
|
const { createServer } = await import('node:http');
|
|
200
200
|
const { codeVerifier, codeChallenge } = generatePKCE();
|
|
201
|
-
|
|
201
|
+
// 32 random bytes → 43-char base64url state. See dario#71 — Anthropic's
|
|
202
|
+
// authorize endpoint rejects shorter states with "Invalid request format";
|
|
203
|
+
// CC v2.1.116+ ships 32. Keep in lockstep with CC's entropy-per-state.
|
|
204
|
+
const state = base64url(randomBytes(32));
|
|
202
205
|
return new Promise((resolve, reject) => {
|
|
203
206
|
const server = createServer((req, res) => {
|
|
204
207
|
const url = new URL(req.url || '', `http://${req.headers.host || 'localhost'}`);
|
|
@@ -387,7 +390,8 @@ export function detectHeadlessEnvironment() {
|
|
|
387
390
|
*/
|
|
388
391
|
export async function startManualOAuthFlow() {
|
|
389
392
|
const { codeVerifier, codeChallenge } = generatePKCE();
|
|
390
|
-
|
|
393
|
+
// 32 bytes — same reason as startAutoOAuthFlow. See dario#71.
|
|
394
|
+
const state = base64url(randomBytes(32));
|
|
391
395
|
const cfg = await getOAuthConfig();
|
|
392
396
|
const authUrl = buildManualAuthorizeUrl(cfg, codeChallenge, state);
|
|
393
397
|
console.log('');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askalf/dario",
|
|
3
|
-
"version": "3.31.
|
|
3
|
+
"version": "3.31.14",
|
|
4
4
|
"description": "A local LLM router. One endpoint, every provider — Claude subscriptions, OpenAI, OpenRouter, Groq, local LiteLLM, any OpenAI-compat endpoint — your tools don't need to change.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"node": ">=18.0.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
|
-
"@types/node": "^
|
|
72
|
+
"@types/node": "^25.6.0",
|
|
73
73
|
"tsx": "^4.19.0",
|
|
74
74
|
"typescript": "^5.7.0"
|
|
75
75
|
}
|