@juspay/neurolink 12.9.1 → 12.9.3

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.
@@ -18,6 +18,8 @@ import { clearAccountCooldown, loadAccountCooldowns, saveAccountCooldown, } from
18
18
  import { anthropicAccountKeysEqual, ENV_ANTHROPIC_ACCOUNT_KEY, isAccountAllowed, LEGACY_ANTHROPIC_ACCOUNT_KEY, normalizeAnthropicAccountKey, shouldLoadFallbackCredential, } from "../../proxy/accountSelection.js";
19
19
  import { getUnifiedRateLimitStatus, isQuotaOverageAvailable, loadAccountQuotas, mergeQuotaSnapshot, modelFamilyToken, parseQuotaHeaders, saveAccountQuota, } from "../../proxy/accountQuota.js";
20
20
  import { fetchAccountUsage, listAnthropicAccountsForUsage, usageToQuota, } from "../../proxy/accountUsage.js";
21
+ import { tokenStore } from "../../auth/tokenStore.js";
22
+ import { fetchCodexAccountUsage, listCodexAccountsForUsage, resolveProxyStatusAccountIdentity, } from "../../proxy/codexAccountUsage.js";
21
23
  import { AccountQuotaRefreshCoordinator } from "../../proxy/accountQuotaRefreshCoordinator.js";
22
24
  import { ProviderTransportCoordinator } from "../../proxy/providerTransportCoordinator.js";
23
25
  import { MAX_COOLDOWN_MS_BY_REASON } from "../../proxy/routingEvidence.js";
@@ -780,6 +782,45 @@ async function refreshAccountQuotaInBackground(account, trigger) {
780
782
  }
781
783
  await applyAccountUsageResult(account, refresh.result, refresh.startedAt);
782
784
  }
785
+ /**
786
+ * The logins the account-exposing routes enumerate, per engine, plus every
787
+ * key the token store holds at all (disabled ones included). Overridable by
788
+ * the suite because the token store is a singleton bound to the real home at
789
+ * import — a case cannot point it elsewhere, and reading the operator's own
790
+ * logins inside a fixture test is exactly how a phantom would hide.
791
+ */
792
+ let accountDirectoryOverride = null;
793
+ async function listRoutableAccountsByEngine(allowlist) {
794
+ if (accountDirectoryOverride) {
795
+ return {
796
+ anthropic: accountDirectoryOverride.anthropic,
797
+ codex: accountDirectoryOverride.codex,
798
+ };
799
+ }
800
+ // The allowlist is an Anthropic routing concept, keyed by anthropic:
801
+ // prefixes; applying it to Codex keys would exclude every Codex login.
802
+ const [anthropic, codex] = await Promise.all([
803
+ listAnthropicAccountsForUsage(allowlist),
804
+ listCodexAccountsForUsage(),
805
+ ]);
806
+ return { anthropic, codex };
807
+ }
808
+ /**
809
+ * Every login the token store knows, routable or not. This is what separates
810
+ * a DISABLED login (still here, shown as unrouted) from a REMOVED one (gone,
811
+ * and not shown): usage counters and quota snapshots outlive a logout, and
812
+ * without this check a deleted login renders as an unrouted account forever.
813
+ */
814
+ async function listKnownAccountKeys() {
815
+ if (accountDirectoryOverride) {
816
+ return accountDirectoryOverride.knownKeys;
817
+ }
818
+ const [anthropic, codex] = await Promise.all([
819
+ tokenStore.listByPrefix("anthropic:"),
820
+ tokenStore.listByPrefix("codex:"),
821
+ ]);
822
+ return new Set([...anthropic.map(normalizeAnthropicAccountKey), ...codex]);
823
+ }
783
824
  /**
784
825
  * Fetch fresh limits from Anthropic's usage endpoint for every eligible OAuth
785
826
  * account and write them through the exact same chain the passive header
@@ -789,24 +830,38 @@ async function refreshAccountQuotaInBackground(account, trigger) {
789
830
  */
790
831
  async function refreshAccountLimits(options = {}) {
791
832
  const fetchedAt = Date.now();
792
- const allAccounts = await listAnthropicAccountsForUsage(options.accountAllowlist);
833
+ const directory = await listRoutableAccountsByEngine(options.accountAllowlist);
834
+ const allAccounts = [
835
+ ...directory.anthropic.map((account) => ({
836
+ account,
837
+ provider: "anthropic",
838
+ })),
839
+ ...directory.codex.map((account) => ({
840
+ account,
841
+ provider: "codex",
842
+ })),
843
+ ];
793
844
  const accounts = options.accountFilter
794
- ? allAccounts.filter((account) => account.label === options.accountFilter ||
845
+ ? allAccounts.filter(({ account }) => account.label === options.accountFilter ||
795
846
  account.key === options.accountFilter)
796
847
  : allAccounts;
797
848
  const persisted = await loadAccountQuotas().catch(() => ({}));
798
- const buildResult = (account, status, quota, error) => {
849
+ const buildResult = (account, provider, status, quota, error) => {
799
850
  const state = accountRuntimeState.get(account.key);
851
+ // The quota store keys Anthropic snapshots by bare label for historical
852
+ // reasons (see CLAUDE.md) and Codex snapshots by full key. A Codex login
853
+ // must never fall back to the bare label: with one email on both engines
854
+ // that label holds the ANTHROPIC account's windows.
855
+ const persistedQuota = provider === "anthropic"
856
+ ? (persisted[account.key] ?? persisted[account.label] ?? null)
857
+ : (persisted[account.key] ?? null);
800
858
  const result = {
801
859
  account: account.label,
802
860
  key: account.key,
861
+ provider,
803
862
  type: account.type,
804
863
  status,
805
- quota: quota ??
806
- state?.quota ??
807
- persisted[account.key] ??
808
- persisted[account.label] ??
809
- null,
864
+ quota: quota ?? state?.quota ?? persistedQuota,
810
865
  };
811
866
  if (error !== undefined) {
812
867
  result.error = error;
@@ -823,7 +878,7 @@ async function refreshAccountLimits(options = {}) {
823
878
  return {
824
879
  fetchedAt,
825
880
  snapshot: true,
826
- results: accounts.map((account) => buildResult(account, "snapshot", null)),
881
+ results: accounts.map(({ account, provider }) => buildResult(account, provider, "snapshot", null)),
827
882
  refreshMetrics: accountQuotaRefreshCoordinator.getMetrics(),
828
883
  };
829
884
  }
@@ -835,23 +890,41 @@ async function refreshAccountLimits(options = {}) {
835
890
  if (index >= accounts.length) {
836
891
  return;
837
892
  }
838
- const account = accounts[index];
893
+ const { account, provider } = accounts[index];
839
894
  if (account.type !== "oauth") {
840
- results[index] = buildResult(account, "skipped_api_key", null);
895
+ results[index] = buildResult(account, provider, "skipped_api_key", null);
841
896
  continue;
842
897
  }
843
898
  const lastFetch = lastUsageFetchAt.get(account.key) ?? 0;
844
899
  if (Date.now() - lastFetch < MIN_USAGE_REFETCH_INTERVAL_MS) {
845
- results[index] = buildResult(account, "throttled", null);
900
+ results[index] = buildResult(account, provider, "throttled", null);
846
901
  continue;
847
902
  }
848
903
  lastUsageFetchAt.set(account.key, Date.now());
904
+ if (provider === "codex") {
905
+ // Codex has its own usage endpoint and no overage/cooldown
906
+ // reconciliation to run; the snapshot is written under the full key,
907
+ // which is the only key the Codex engine ever reads it back by.
908
+ try {
909
+ const fetched = await fetchCodexAccountUsage(account);
910
+ if (fetched.ok === false) {
911
+ results[index] = buildResult(account, provider, "error", null, `codex usage fetch failed: ${fetched.reason}`);
912
+ continue;
913
+ }
914
+ await saveAccountQuota(account.key, fetched.quota);
915
+ results[index] = buildResult(account, provider, "refreshed", fetched.quota);
916
+ }
917
+ catch (err) {
918
+ results[index] = buildResult(account, provider, "error", null, err instanceof Error ? err.message : String(err));
919
+ }
920
+ continue;
921
+ }
849
922
  // Isolate failures per account: an unexpected rejection must not abort
850
923
  // the Promise.all sweep and turn the whole /limits response into a 502.
851
924
  try {
852
925
  const refresh = await accountQuotaRefreshCoordinator.run(account, `manual:${account.key}`, fetchValidatedAccountUsage, { force: true });
853
926
  if (refresh.kind !== "completed") {
854
- results[index] = buildResult(account, "throttled", null);
927
+ results[index] = buildResult(account, provider, "throttled", null);
855
928
  continue;
856
929
  }
857
930
  const fetchResult = refresh.result;
@@ -859,18 +932,18 @@ async function refreshAccountLimits(options = {}) {
859
932
  // file without strictNullChecks, where negated boolean-discriminant
860
933
  // narrowing does not apply.
861
934
  if (fetchResult.ok === false) {
862
- results[index] = buildResult(account, "error", null, fetchResult.error);
935
+ results[index] = buildResult(account, provider, "error", null, fetchResult.error);
863
936
  continue;
864
937
  }
865
938
  const quota = await applyAccountUsageResult(account, fetchResult, refresh.startedAt, persisted[account.key] ?? persisted[account.label] ?? null);
866
939
  if (!quota) {
867
- results[index] = buildResult(account, "error", null, "usage payload had no recognizable limit windows");
940
+ results[index] = buildResult(account, provider, "error", null, "usage payload had no recognizable limit windows");
868
941
  continue;
869
942
  }
870
- results[index] = buildResult(account, "refreshed", quota);
943
+ results[index] = buildResult(account, provider, "refreshed", quota);
871
944
  }
872
945
  catch (err) {
873
- results[index] = buildResult(account, "error", null, err instanceof Error ? err.message : String(err));
946
+ results[index] = buildResult(account, provider, "error", null, err instanceof Error ? err.message : String(err));
874
947
  }
875
948
  }
876
949
  };
@@ -6769,11 +6842,6 @@ function buildEarlyClaudeRequestError(args) {
6769
6842
  * millisecond fields tens of thousands of years into the future, so only the
6770
6843
  * seconds fields are converted, and 0 becomes null rather than epoch zero.
6771
6844
  */
6772
- /**
6773
- * Account types that represent a real credential rather than proxy plumbing.
6774
- * Mirrors the Anthropic pool's own account types.
6775
- */
6776
- const REAL_ACCOUNT_TYPES = new Set(["oauth", "api_key"]);
6777
6845
  function toMillis(value) {
6778
6846
  return typeof value === "number" && Number.isFinite(value) && value > 0
6779
6847
  ? Math.round(value * 1000)
@@ -7498,9 +7566,31 @@ export function createClaudeProxyRoutes(modelRouter, basePath = "", accountStrat
7498
7566
  // Usage is the optional half; quota and status must still render.
7499
7567
  usageError = error instanceof Error ? error.message : String(error);
7500
7568
  }
7501
- const statsByLabel = new Map();
7502
- for (const entry of Object.values(statsAccounts)) {
7503
- statsByLabel.set(entry.label, entry);
7569
+ // Joined by provider-qualified KEY, never by label. One email can
7570
+ // be logged in to both engines; joined by label, the Codex login was
7571
+ // swallowed by the Anthropic row (and its counters could land on
7572
+ // that row, last write winning). Legacy stats entries that predate
7573
+ // the key carry only a label and a type, which is enough to derive
7574
+ // it; a keyed entry for the same login wins over a legacy one.
7575
+ const statsByKey = new Map();
7576
+ for (const [mapKey, entry] of Object.entries(statsAccounts)) {
7577
+ const identity = resolveProxyStatusAccountIdentity(entry.label, entry.type, entry.key ?? mapKey);
7578
+ const identityKey = identity.key ?? mapKey;
7579
+ const existing = statsByKey.get(identityKey);
7580
+ if (!existing || (entry.key && !existing.key)) {
7581
+ statsByKey.set(identityKey, { ...entry, identityKey });
7582
+ }
7583
+ }
7584
+ // Which logins exist at all, disabled ones included. A stats entry
7585
+ // with no login behind it is a REMOVED account, not an unrouted one.
7586
+ // If the store cannot be read, err towards showing rows: hiding a
7587
+ // real login is the worse mistake, and the old behaviour.
7588
+ let knownKeys;
7589
+ try {
7590
+ knownKeys = await listKnownAccountKeys();
7591
+ }
7592
+ catch {
7593
+ knownKeys = null;
7504
7594
  }
7505
7595
  const rows = [];
7506
7596
  const claimed = new Set();
@@ -7509,13 +7599,14 @@ export function createClaudeProxyRoutes(modelRouter, basePath = "", accountStrat
7509
7599
  // today, which is exactly when an operator most wants to see it.
7510
7600
  for (const result of limits.results) {
7511
7601
  const label = result.account;
7512
- claimed.add(label);
7513
- const stat = statsByLabel.get(label);
7602
+ claimed.add(result.key);
7603
+ const stat = statsByKey.get(result.key);
7514
7604
  const quota = normalizeQuotaForAccounts(result.quota);
7515
7605
  const cooling = isCooling(result.key ?? null);
7516
7606
  rows.push({
7517
7607
  label,
7518
7608
  key: result.key ?? null,
7609
+ provider: result.provider,
7519
7610
  kind: "account",
7520
7611
  type: result.type ?? stat?.type ?? "oauth",
7521
7612
  // result.status describes how the quota was obtained
@@ -7549,39 +7640,53 @@ export function createClaudeProxyRoutes(modelRouter, basePath = "", accountStrat
7549
7640
  weeklyHealth: quotaHealth(quota.weeklyStatus),
7550
7641
  }
7551
7642
  : null,
7552
- usage: usageByAccount.get(label) ?? null,
7643
+ usage: usageByAccount.get(result.key) ?? null,
7553
7644
  });
7554
7645
  }
7555
7646
  // Plumbing rows are still reported, but tagged, so a consumer can
7556
7647
  // show or hide them rather than rendering them as credentials.
7557
7648
  //
7558
- // A real login can land here too: listAnthropicAccountsForUsage
7559
- // skips accounts the token store has disabled or the allowlist
7560
- // excludes, so an account with real usage history but no current
7561
- // route is absent from limits.results. Tagging that as plumbing hid
7562
- // the one account an operator is looking for when they ask why
7563
- // traffic stopped the docs tell consumers to filter internal rows
7564
- // out. It stays kind "account", with a status saying why it has no
7565
- // quota block.
7566
- for (const entry of Object.values(statsAccounts)) {
7567
- if (claimed.has(entry.label)) {
7649
+ // A real login can land here too: the listers skip accounts the
7650
+ // token store has disabled or the allowlist excludes, so a login
7651
+ // with real usage history but no current route is absent from
7652
+ // limits.results. Tagging that as plumbing hid the one account an
7653
+ // operator is looking for when they ask why traffic stopped. It
7654
+ // stays kind "account", with a status saying why it has no quota
7655
+ // block but only while the login still EXISTS. A stats entry whose
7656
+ // login has been removed from the store is history, not a
7657
+ // credential, and rendering it as "unrouted" put a phantom account
7658
+ // on every dashboard for as long as the counters file lived.
7659
+ for (const entry of statsByKey.values()) {
7660
+ const identity = resolveProxyStatusAccountIdentity(entry.label, entry.type, entry.identityKey);
7661
+ if (identity.key !== null && claimed.has(identity.key)) {
7662
+ continue;
7663
+ }
7664
+ const isLogin = identity.provider !== "other";
7665
+ if (isLogin &&
7666
+ identity.key !== null &&
7667
+ knownKeys !== null &&
7668
+ !knownKeys.has(identity.key)) {
7568
7669
  continue;
7569
7670
  }
7570
- const isRealAccount = REAL_ACCOUNT_TYPES.has(entry.type);
7571
7671
  rows.push({
7572
7672
  label: entry.label,
7573
- key: null,
7574
- kind: isRealAccount
7673
+ key: isLogin ? identity.key : null,
7674
+ ...(isLogin ? { provider: identity.provider } : {}),
7675
+ kind: isLogin
7575
7676
  ? "account"
7576
7677
  : entry.type === "translation"
7577
7678
  ? "translation"
7578
7679
  : "internal",
7579
- type: entry.type,
7580
- status: isRealAccount ? "unrouted" : null,
7680
+ // The row's type is the credential kind; the engine is
7681
+ // `provider`. Stats record Codex logins as "codex-oauth", which
7682
+ // consumers that only know the credential kinds read as
7683
+ // plumbing, so it is reported as the OAuth login it is.
7684
+ type: identity.provider === "codex" ? "oauth" : entry.type,
7685
+ status: isLogin ? "unrouted" : null,
7581
7686
  cooling: false,
7582
7687
  allowed: null,
7583
7688
  expired: null,
7584
- isPrimary: isPrimaryAccount(entry.label),
7689
+ isPrimary: isLogin ? isPrimaryAccount(identity.key) : false,
7585
7690
  requests: entry.successCount + entry.errorCount,
7586
7691
  errors: entry.errorCount,
7587
7692
  rateLimits: entry.rateLimitCount,
@@ -7593,7 +7698,9 @@ export function createClaudeProxyRoutes(modelRouter, basePath = "", accountStrat
7593
7698
  // and cost in the ledger — hardcoding null here discarded
7594
7699
  // exactly the usage an operator is looking for when they ask
7595
7700
  // why traffic stopped.
7596
- usage: usageByAccount.get(entry.label) ?? null,
7701
+ usage: identity.key !== null
7702
+ ? (usageByAccount.get(identity.key) ?? null)
7703
+ : null,
7597
7704
  });
7598
7705
  }
7599
7706
  const response = {
@@ -8001,6 +8108,9 @@ export const __testHooks = {
8001
8108
  limitsRefreshInFlight = null;
8002
8109
  accountQuotaRefreshCoordinator.clear();
8003
8110
  },
8111
+ setAccountDirectoryForTests: (override) => {
8112
+ accountDirectoryOverride = override;
8113
+ },
8004
8114
  isRetryableNetworkError,
8005
8115
  isPermanentRefreshFailure,
8006
8116
  getStreamFailureDetails,
@@ -44,6 +44,12 @@ export type CatalogErrorRuleJson = {
44
44
  };
45
45
  export type CatalogQuirks = {
46
46
  timeoutErrorClass?: "provider";
47
+ /** Vendor speaks OpenAI for chat but restricts how message content is
48
+ * encoded. "string": `messages[].content` must be a plain string —
49
+ * the content-parts array and the `null` OpenAI uses on an assistant
50
+ * message with tool_calls are both rejected with HTTP 400. Normalized by
51
+ * ConfiguredOpenAICompatProvider so tool round-trips work. */
52
+ messageContentFormat?: "string";
47
53
  registryDefaultIgnoresModelEnvVar?: boolean;
48
54
  };
49
55
  export type CatalogBillingPolicy = "free-tier" | "free-with-card" | "no-free-tier";
@@ -693,6 +693,9 @@ export type OpenAICompatCatalogEntry = {
693
693
  * the classifier's default (six of the seven catalog entries).
694
694
  */
695
695
  timeoutErrorClass?: new (message: string, provider?: string) => ProviderError;
696
+ /** See CatalogQuirks.messageContentFormat — a vendor that accepts
697
+ * `messages[].content` only as a plain string. */
698
+ messageContentFormat?: "string";
696
699
  };
697
700
  /** The subset of OpenAICompatCatalogEntry that resolveOpenAICompatConfig()
698
701
  * needs — lets call sites pass a minimal object without the full catalog
@@ -1209,8 +1209,14 @@ export type ProxyQuotaRefreshRunResult = {
1209
1209
  export type ProxyLimitsAccountResult = {
1210
1210
  /** Account label (quota-store key). */
1211
1211
  account: string;
1212
- /** Token-store key ("anthropic:<label>"). */
1212
+ /** Token-store key ("anthropic:<label>" or "codex:<label>"). */
1213
1213
  key: string;
1214
+ /**
1215
+ * Which pool engine owns this login. Two logins can share a label — an
1216
+ * operator may use one email for both — so the key, not the label, is the
1217
+ * identity, and this names the engine without parsing the key's prefix.
1218
+ */
1219
+ provider: ProxyAccountProvider;
1214
1220
  type: ProxyAccountType;
1215
1221
  status: "refreshed" | "throttled" | "skipped_api_key" | "snapshot" | "error";
1216
1222
  /** Fresh quota on "refreshed"; last known snapshot otherwise (may be null). */
@@ -1219,6 +1225,20 @@ export type ProxyLimitsAccountResult = {
1219
1225
  coolingUntil?: number;
1220
1226
  coolingReason?: AccountCoolingReason;
1221
1227
  };
1228
+ /** The pool engine a login belongs to, as named on limits and accounts rows. */
1229
+ export type ProxyAccountProvider = "anthropic" | "codex";
1230
+ /**
1231
+ * Test-only replacement for the token store behind the account-exposing
1232
+ * routes. The token store is a module singleton bound to the real home at
1233
+ * import, so a suite cannot redirect it; this lets a case state which logins
1234
+ * exist (`knownKeys`, including disabled ones) and which are routable per
1235
+ * engine, exactly as the real listers would answer.
1236
+ */
1237
+ export type ProxyAccountDirectoryOverride = {
1238
+ knownKeys: Set<string>;
1239
+ anthropic: ProxyPassthroughAccount[];
1240
+ codex: ProxyPassthroughAccount[];
1241
+ };
1222
1242
  /** Response body of the proxy's GET /limits endpoint. */
1223
1243
  export type ProxyLimitsRefreshResponse = {
1224
1244
  fetchedAt: number;
@@ -141,10 +141,21 @@ export type CliClientUsageTotals = {
141
141
  };
142
142
  /** One row of GET /accounts. */
143
143
  export type CliAccountsRow = {
144
- /** Bare label, e.g. "someone@example.com". The join key across all sources. */
144
+ /**
145
+ * Bare label, e.g. "someone@example.com". Display only: two rows can share
146
+ * it when one email is logged in to both engines. `key` is the identity.
147
+ */
145
148
  label: string;
146
- /** Full pool key, e.g. "anthropic:someone@example.com". */
149
+ /**
150
+ * Full pool key, e.g. "anthropic:someone@example.com" or
151
+ * "codex:someone@example.com". Null only for plumbing rows.
152
+ */
147
153
  key: string | null;
154
+ /**
155
+ * Which pool engine owns this login. Absent on plumbing rows. Consumers
156
+ * that key a list by row must key by `key`, not `label` — see above.
157
+ */
158
+ provider?: "anthropic" | "codex";
148
159
  /**
149
160
  * What this row actually is. Only "account" rows are real logins; the proxy
150
161
  * also tracks internal and translation pseudo-accounts, which have no quota
@@ -183,6 +194,13 @@ export type CliAccountsResponse = {
183
194
  /** One request as recorded in the proxy request log, reduced to what costing needs. */
184
195
  export type ProxyLedgerEntry = {
185
196
  account: string;
197
+ /**
198
+ * Provider-qualified identity, "anthropic:<label>" or "codex:<label>".
199
+ * Read from the log row when present; derived from `accountType` for rows
200
+ * written before the pool logged it. This, not `account`, is the join key:
201
+ * one email can be logged in to both engines.
202
+ */
203
+ accountKey: string;
186
204
  /** Derived calling CLI; see CliAccountUsageTotals.byClient. */
187
205
  clientApp: string;
188
206
  accountType: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "12.9.1",
3
+ "version": "12.9.3",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "TypeScript AI SDK with 24+ LLM providers behind one consistent API. MCP-native (connect any MCP server), voice TTS/STT/realtime, RAG, agents, memory, context compaction. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · DeepSeek · NVIDIA NIM and more.",
6
6
  "author": {
@@ -41,6 +41,7 @@
41
41
  "prepack": "svelte-kit sync && svelte-package && pnpm run build:react-hooks && pnpm run build:cli && pnpm run build:browser && publint",
42
42
  "build:react-hooks": "pnpm exec tsc --jsx react-jsx --module nodenext --moduleResolution nodenext --target esnext --esModuleInterop --skipLibCheck --outDir dist --declaration false src/lib/client/reactHooks.tsx",
43
43
  "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && tsc --noEmit --strict",
44
+ "check:models": "tsx tools/check-model-liveness.ts",
44
45
  "check:ci-scripts": "svelte-kit sync && tsc -p tsconfig.ci-scripts.json",
45
46
  "check:test-parse": "node scripts/parse-check-tests.mjs",
46
47
  "check:tools-tests": "svelte-kit sync && tsc -p tsconfig.tools-tests.json",