@narumitw/pi-usage 0.60.4 → 0.60.6

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/dist/index.ts CHANGED
@@ -120,6 +120,9 @@ var UsageCache = class {
120
120
  }
121
121
  this.entries.set(key, { createdAt: now, report: report2 });
122
122
  }
123
+ delete(providerId, fingerprint) {
124
+ this.entries.delete(cacheKey(providerId, fingerprint));
125
+ }
123
126
  clearProvider(providerId) {
124
127
  for (const key of this.entries.keys()) {
125
128
  if (key.startsWith(`${providerId}:`)) this.entries.delete(key);
@@ -239,8 +242,6 @@ function redactUsageError(value, secrets = []) {
239
242
  function errorMessage(error) {
240
243
  return sanitizeDisplayText(error instanceof Error ? error.message : String(error), 600);
241
244
  }
242
- var UsageUnsupportedError = class extends Error {
243
- };
244
245
  function abortError() {
245
246
  return Object.assign(new Error("Usage query aborted."), { name: "AbortError" });
246
247
  }
@@ -1732,22 +1733,98 @@ function isRecord2(value) {
1732
1733
  return typeof value === "object" && value !== null && !Array.isArray(value);
1733
1734
  }
1734
1735
 
1736
+ // src/providers/zai-errors.ts
1737
+ var ERROR_MESSAGES = {
1738
+ "1000": "Authentication failed. Check your API key.",
1739
+ "1001": "Authentication header missing. Check your API key.",
1740
+ "1003": "Authentication token expired. Obtain a new token.",
1741
+ "1005": "Two-factor authentication required.",
1742
+ "1113": "Insufficient balance or no resource package. Recharge your account.",
1743
+ "1200": "API call error. Try again later.",
1744
+ "1210": "Invalid API parameter. Check the API documentation.",
1745
+ "1211": "Unknown model. Check the model ID.",
1746
+ "1212": "This model does not support the requested method.",
1747
+ "1213": "A required parameter is missing.",
1748
+ "1214": "Invalid parameter. Check the API documentation.",
1749
+ "1215": "Conflicting parameters. Check the API documentation.",
1750
+ "1220": "Access denied. Check your permissions.",
1751
+ "1221": "This API has been taken offline.",
1752
+ "1222": "This API does not exist.",
1753
+ "1230": "API processing error. Try again later.",
1754
+ "1234": "Network error. Try again later.",
1755
+ "1261": "Prompt too long. Reduce the input length.",
1756
+ "1301": "Content rejected by the safety policy.",
1757
+ "1302": "Request rate limit reached. Try again later.",
1758
+ "1305": "Service overloaded. Try again later.",
1759
+ "1308": "Usage limit reached. Wait for the quota reset.",
1760
+ "1309": "GLM Coding Plan expired. Renew your subscription.",
1761
+ "1310": "Weekly or monthly limit exhausted. Wait for the quota reset.",
1762
+ "1311": "Your subscription does not include this model.",
1763
+ "1313": "Request frequency restricted by the Fair Usage Policy. Contact support.",
1764
+ "1314": "Enterprise package expired. Contact your administrator.",
1765
+ "1315": "This API key requires an enterprise coding package scenario. Replace the key.",
1766
+ "1316": "5-hour limit reached; insufficient balance for extra usage. Wait for reset.",
1767
+ "1317": "7-day limit reached; insufficient balance for extra usage. Wait for reset.",
1768
+ "1318": "5-hour limit reached; extra usage blocked by monthly spend limit.",
1769
+ "1319": "7-day limit reached; extra usage blocked by monthly spend limit.",
1770
+ "1320": "5-hour limit reached; extra usage blocked by monthly spend limit.",
1771
+ "1321": "7-day limit reached; extra usage blocked by monthly spend limit."
1772
+ };
1773
+ var HTTP_MESSAGES = {
1774
+ 400: "Invalid request. Check the API documentation.",
1775
+ 401: "Authentication failed. Check your API key.",
1776
+ 403: "Access denied. Check your permissions.",
1777
+ 429: "Request or usage limit reached. Try again later.",
1778
+ 500: "Internal error. Try again later."
1779
+ };
1780
+ function zaiPayloadError(payload) {
1781
+ const object = asObject11(payload);
1782
+ if (!object) return void 0;
1783
+ const nested = asObject11(object.error);
1784
+ const rawCode = nested?.code === void 0 ? object.code : nested.code;
1785
+ const code = errorCode(rawCode);
1786
+ if (object.error === void 0 && object.success !== false && (rawCode === void 0 || code === "0" || code === "200")) {
1787
+ return void 0;
1788
+ }
1789
+ return code && code !== "0" && code !== "200" ? `Z.AI ${code}: ${ERROR_MESSAGES[code] ?? "API request failed."}` : "Z.AI: API request failed.";
1790
+ }
1791
+ function zaiResponseError(status, text) {
1792
+ let payload;
1793
+ try {
1794
+ payload = JSON.parse(text);
1795
+ } catch {
1796
+ if (status >= 200 && status < 300) return "Z.AI: Invalid JSON response.";
1797
+ }
1798
+ const error = zaiPayloadError(payload);
1799
+ if (error) return error;
1800
+ if (status < 200 || status >= 300) {
1801
+ return `Z.AI HTTP ${status}: ${HTTP_MESSAGES[status] ?? "API request failed."}`;
1802
+ }
1803
+ return void 0;
1804
+ }
1805
+ function errorCode(value) {
1806
+ if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 9999) {
1807
+ return String(value);
1808
+ }
1809
+ return typeof value === "string" && /^(?:0|[1-9]\d{0,3})$/u.test(value) ? value : void 0;
1810
+ }
1811
+ function asObject11(value) {
1812
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
1813
+ }
1814
+
1735
1815
  // src/providers/zai.ts
1736
1816
  var FIVE_HOUR_WINDOW_MINUTES2 = 300;
1737
1817
  var WEEKLY_WINDOW_MINUTES2 = 10080;
1738
1818
  function normalizeZaiQuotaPayload(providerId, providerName, payload, capturedAt, plan) {
1739
- const data = asObject11(payload.data);
1740
- if (!data) {
1741
- const reason = asString5(payload.msg);
1742
- throw new UsageUnsupportedError(
1743
- `No GLM Coding Plan on this Z.AI credential; API usage is not metered.${reason ? ` Z.AI reported: ${reason}` : ""}`
1744
- );
1745
- }
1819
+ const error = zaiPayloadError(payload);
1820
+ if (error) throw new Error(error);
1821
+ const data = asObject12(payload.data);
1822
+ if (!data) throw new Error("Z.AI quota response data was not an object.");
1746
1823
  const limits = Array.isArray(data.limits) ? data.limits : [];
1747
1824
  const buckets = [];
1748
1825
  const metrics = [];
1749
1826
  for (const raw of limits) {
1750
- const limit = asObject11(raw);
1827
+ const limit = asObject12(raw);
1751
1828
  if (!limit) continue;
1752
1829
  const type = asString5(limit.type);
1753
1830
  const unit = asNonnegativeNumber4(limit.unit);
@@ -1796,14 +1873,11 @@ function normalizeZaiQuotaPayload(providerId, providerName, payload, capturedAt,
1796
1873
  };
1797
1874
  }
1798
1875
  function normalizeZaiSubscriptionPayload(payload) {
1799
- if (payload.success === false) return void 0;
1800
- if (typeof payload.code === "number" && payload.code !== 0 && payload.code !== 200) {
1801
- return void 0;
1802
- }
1876
+ if (zaiPayloadError(payload)) return void 0;
1803
1877
  if (!Array.isArray(payload.data)) return void 0;
1804
1878
  const candidates = [];
1805
1879
  for (const raw of payload.data) {
1806
- const entry = asObject11(raw);
1880
+ const entry = asObject12(raw);
1807
1881
  if (!entry) continue;
1808
1882
  const name = asString5(entry.productName);
1809
1883
  if (!name) continue;
@@ -1881,7 +1955,7 @@ function addCountBucket(buckets, limit, id, label, windowMinutes) {
1881
1955
  function addUsageDetailMetrics(metrics, value) {
1882
1956
  if (!Array.isArray(value)) return;
1883
1957
  for (const raw of value) {
1884
- const detail = asObject11(raw);
1958
+ const detail = asObject12(raw);
1885
1959
  if (!detail) continue;
1886
1960
  const label = asString5(detail.modelCode);
1887
1961
  const usage = asNonnegativeNumber4(detail.usage);
@@ -1889,7 +1963,7 @@ function addUsageDetailMetrics(metrics, value) {
1889
1963
  metrics.push({ id: `mcp-${kebabCase(label)}`, label, value: usage, unit: "count" });
1890
1964
  }
1891
1965
  }
1892
- function asObject11(value) {
1966
+ function asObject12(value) {
1893
1967
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
1894
1968
  return value;
1895
1969
  }
@@ -2227,6 +2301,7 @@ var SUPPORTED_ADAPTERS = [
2227
2301
  {
2228
2302
  id: "zai",
2229
2303
  displayName: "Z.AI",
2304
+ invalidateCacheOnFailure: true,
2230
2305
  semantics: { kind: "consumer-subscription", label: "GLM Coding Plan usage" },
2231
2306
  async query(auth, signal, timeoutMs, guard) {
2232
2307
  return queryZaiUsage("zai", "Z.AI", auth, signal, timeoutMs, guard);
@@ -2235,6 +2310,7 @@ var SUPPORTED_ADAPTERS = [
2235
2310
  {
2236
2311
  id: "zai-coding-cn",
2237
2312
  displayName: "Z.AI Coding CN",
2313
+ invalidateCacheOnFailure: true,
2238
2314
  semantics: { kind: "consumer-subscription", label: "GLM Coding Plan usage" },
2239
2315
  async query(auth, signal, timeoutMs, guard) {
2240
2316
  return queryZaiUsage("zai-coding-cn", "Z.AI Coding CN", auth, signal, timeoutMs, guard);
@@ -2455,8 +2531,7 @@ async function queryProviderUsage(adapter, auth, signal, timeoutMs, guard, targe
2455
2531
  );
2456
2532
  } catch (error) {
2457
2533
  if (isStaleExtensionContextError(error) || isAbortError(error)) throw error;
2458
- const message = redactUsageError(errorMessage(error), auth.secrets);
2459
- throw error instanceof UsageUnsupportedError ? new UsageUnsupportedError(message) : new Error(message);
2534
+ throw new Error(redactUsageError(errorMessage(error), auth.secrets));
2460
2535
  }
2461
2536
  }
2462
2537
  function providerIsConfigured(ctx, providerId) {
@@ -2537,6 +2612,8 @@ async function fetchProviderJson(url, auth, signal, timeoutMs, description, requ
2537
2612
  );
2538
2613
  if (controller.signal.aborted)
2539
2614
  throw Object.assign(new Error("Usage query aborted."), { name: "AbortError" });
2615
+ const responseError = request.responseError?.(response.status, text);
2616
+ if (responseError) throw new Error(responseError);
2540
2617
  if (!response.ok) {
2541
2618
  throw new Error(
2542
2619
  `${description} returned ${response.status} ${response.statusText}: ${redactUsageError(text, auth.secrets)}`
@@ -2613,7 +2690,7 @@ function resolveXaiUsageAuth(auth, model, salt, candidates) {
2613
2690
  const matches = [];
2614
2691
  for (const candidate of candidates) {
2615
2692
  try {
2616
- const credential = asObject12(candidate);
2693
+ const credential = asObject13(candidate);
2617
2694
  if (credential?.type !== "oauth") continue;
2618
2695
  sawOAuth = true;
2619
2696
  if (credential.access !== resolvedAccess) continue;
@@ -2667,7 +2744,7 @@ function resolveGitHubCopilotUsageAuth(auth, model, salt, candidates, standalone
2667
2744
  const matches = /* @__PURE__ */ new Map();
2668
2745
  for (const candidate of candidates) {
2669
2746
  try {
2670
- const credential = asObject12(candidate);
2747
+ const credential = asObject13(candidate);
2671
2748
  if (credential?.type !== "oauth") continue;
2672
2749
  sawOAuth = true;
2673
2750
  const storedAccess2 = typeof credential.access === "string" && credential.access ? credential.access : void 0;
@@ -2729,7 +2806,7 @@ function bearerToken(authorization) {
2729
2806
  const match = /^Bearer\s+(.+)$/iu.exec(authorization ?? "");
2730
2807
  return match?.[1];
2731
2808
  }
2732
- function asObject12(value) {
2809
+ function asObject13(value) {
2733
2810
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
2734
2811
  return value;
2735
2812
  }
@@ -2858,7 +2935,8 @@ async function queryZaiUsage(providerId, providerName, auth, signal, timeoutMs,
2858
2935
  zaiMonitorAuth(auth),
2859
2936
  signal,
2860
2937
  remainingTimeout2(timeoutMs, startedAt, `fetching ${providerName} quota`),
2861
- `${providerName} quota endpoint`
2938
+ `${providerName} quota endpoint`,
2939
+ { responseError: zaiResponseError }
2862
2940
  );
2863
2941
  await guard();
2864
2942
  const planTimeoutMs = timeoutMs - (Date.now() - startedAt);
@@ -2873,7 +2951,8 @@ async function fetchZaiPlan(providerName, auth, signal, timeoutMs) {
2873
2951
  zaiMonitorAuth(auth),
2874
2952
  signal,
2875
2953
  timeoutMs,
2876
- `${providerName} plan endpoint`
2954
+ `${providerName} plan endpoint`,
2955
+ { responseError: zaiResponseError }
2877
2956
  );
2878
2957
  return normalizeZaiSubscriptionPayload(payload);
2879
2958
  } catch (error) {
@@ -3023,7 +3102,7 @@ function normalizeCodexResetCreditsPayload(payload) {
3023
3102
  if (rawCredits !== void 0 && !Array.isArray(rawCredits)) {
3024
3103
  throw new Error("Codex reset credits response returned invalid credits.");
3025
3104
  }
3026
- const options = (rawCredits ?? []).map(asObject13).filter((credit) => Boolean(credit)).filter((credit) => credit.status === "available" && credit.reset_type === "codex_rate_limits").map(normalizeResetOption).sort(
3105
+ const options = (rawCredits ?? []).map(asObject14).filter((credit) => Boolean(credit)).filter((credit) => credit.status === "available" && credit.reset_type === "codex_rate_limits").map(normalizeResetOption).sort(
3027
3106
  (left, right) => (left.expiresAt ?? Number.MAX_SAFE_INTEGER) - (right.expiresAt ?? Number.MAX_SAFE_INTEGER)
3028
3107
  ).slice(0, Math.min(availableCount, MAX_RESET_OPTIONS));
3029
3108
  if (availableCount > 0 && options.length === 0) {
@@ -3038,7 +3117,7 @@ function selectCodexResetCredential(candidates, resolvedAccess, resolvedAccountI
3038
3117
  const matches = /* @__PURE__ */ new Map();
3039
3118
  for (const candidate of candidates) {
3040
3119
  try {
3041
- const credential = asObject13(candidate);
3120
+ const credential = asObject14(candidate);
3042
3121
  if (credential?.type !== "oauth") continue;
3043
3122
  sawOAuth = true;
3044
3123
  const storedAccess = asNonemptyString(credential.access);
@@ -3077,7 +3156,7 @@ function codexAccountIdFromAccessToken(access) {
3077
3156
  const parts = access.split(".");
3078
3157
  if (parts.length !== 3 || !parts[1]) return void 0;
3079
3158
  const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
3080
- const claims = asObject13(asObject13(payload)?.["https://api.openai.com/auth"]);
3159
+ const claims = asObject14(asObject14(payload)?.["https://api.openai.com/auth"]);
3081
3160
  return validHeaderValue(claims?.chatgpt_account_id);
3082
3161
  } catch {
3083
3162
  return void 0;
@@ -3109,7 +3188,7 @@ function normalizeResetOption(credit) {
3109
3188
  function isCodexResetOutcomeCode(value) {
3110
3189
  return value === "reset" || value === "nothing_to_reset" || value === "no_credit" || value === "already_redeemed";
3111
3190
  }
3112
- function asObject13(value) {
3191
+ function asObject14(value) {
3113
3192
  if (!value || typeof value !== "object" || Array.isArray(value)) return void 0;
3114
3193
  return value;
3115
3194
  }
@@ -4568,7 +4647,7 @@ function usageExtension(pi, dependencies = {}) {
4568
4647
  providerId: adapter.id,
4569
4648
  providerName,
4570
4649
  displayState,
4571
- status: previousDiscoveryFailure.status,
4650
+ status: "query-failed",
4572
4651
  message: previousDiscoveryFailure.message
4573
4652
  },
4574
4653
  fingerprint: auth.fingerprint,
@@ -4627,7 +4706,7 @@ function usageExtension(pi, dependencies = {}) {
4627
4706
  providerId: adapter.id,
4628
4707
  providerName,
4629
4708
  displayState,
4630
- status: previousFailure.status,
4709
+ status: "query-failed",
4631
4710
  message: previousFailure.message
4632
4711
  },
4633
4712
  fingerprint: auth.fingerprint,
@@ -4680,16 +4759,16 @@ function usageExtension(pi, dependencies = {}) {
4680
4759
  );
4681
4760
  }
4682
4761
  const message = errorMessage(error);
4683
- const status = error instanceof UsageUnsupportedError ? "unsupported" : "query-failed";
4684
4762
  const now = Date.now();
4685
4763
  for (const [key, failure] of failureBackoff) {
4686
4764
  if (failure.until <= now) failureBackoff.delete(key);
4687
4765
  }
4688
4766
  if (queryId === void 0 || latestQueries.get(failureKey) === queryId) {
4767
+ if (adapter.invalidateCacheOnFailure) cache.delete(adapter.id, queryFingerprint);
4689
4768
  setBoundedMap(
4690
4769
  failureBackoff,
4691
4770
  failureKey,
4692
- { until: now + FAILURE_BACKOFF_MS, message, status },
4771
+ { until: now + FAILURE_BACKOFF_MS, message },
4693
4772
  MAX_ACCOUNT_STATES
4694
4773
  );
4695
4774
  }
@@ -4698,7 +4777,7 @@ function usageExtension(pi, dependencies = {}) {
4698
4777
  providerId: adapter.id,
4699
4778
  providerName,
4700
4779
  displayState,
4701
- status,
4780
+ status: "query-failed",
4702
4781
  message
4703
4782
  },
4704
4783
  fingerprint: auth.fingerprint,