@rynfar/meridian 1.72.0 → 1.74.0

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.
Files changed (177) hide show
  1. package/README.md +119 -107
  2. package/assets/banner.svg +18 -43
  3. package/assets/how-it-works.svg +40 -86
  4. package/assets/providers/README.md +10 -0
  5. package/assets/providers/antigravity.png +0 -0
  6. package/assets/providers/claude.png +0 -0
  7. package/dist/antigravity-clients/opencode.js +173 -0
  8. package/dist/antigravity-clients/pi.js +21 -0
  9. package/dist/antigravitySetup-35pd5xqn.js +191 -0
  10. package/dist/cli-1jstkdj9.js +30 -0
  11. package/dist/{cli-9e5cxp89.js → cli-2h3w5n6g.js} +163 -65
  12. package/dist/cli-3zwfvexc.js +56 -0
  13. package/dist/{cli-hxxy0m1z.js → cli-82e2m44k.js} +2 -1
  14. package/dist/cli-8sw5wr9j.js +136 -0
  15. package/dist/{cli-khhjyk04.js → cli-chsp880k.js} +214 -21
  16. package/dist/cli-dh0a1bw4.js +619 -0
  17. package/dist/cli-eehk3052.js +57 -0
  18. package/dist/{cli-ab7zkp1a.js → cli-fveyj34s.js} +32261 -18952
  19. package/dist/cli-grfp0s1j.js +330 -0
  20. package/dist/cli-qpfky8wy.js +133 -0
  21. package/dist/{cli-hj61zpt7.js → cli-wbsmhxcx.js} +26 -2
  22. package/dist/cli.js +111 -38
  23. package/dist/cliDashboard-c0dvkj4y.js +325 -0
  24. package/dist/configDir.d.ts +30 -0
  25. package/dist/configDir.d.ts.map +1 -0
  26. package/dist/organizationName-90f0atkn.js +29 -0
  27. package/dist/{pluginPage-f5gdmbxc.js → pluginPage-pp72d2j8.js} +1 -1
  28. package/dist/{profileCli-1eecbmgk.js → profileCli-1bfgc05h.js} +13 -3
  29. package/dist/{profilePage-mamej7kp.js → profilePage-vs319rvf.js} +252 -60
  30. package/dist/profileRename-4a4km6g8.js +22 -0
  31. package/dist/{profiles-wch9h234.js → profiles-0mp32ek9.js} +16 -3
  32. package/dist/proxy/adapterInstances.d.ts +1 -1
  33. package/dist/proxy/adapterInstances.d.ts.map +1 -1
  34. package/dist/proxy/adapters/claudecode.d.ts.map +1 -1
  35. package/dist/proxy/adapters/droid.d.ts.map +1 -1
  36. package/dist/proxy/auth.d.ts +2 -0
  37. package/dist/proxy/auth.d.ts.map +1 -1
  38. package/dist/proxy/authDiscovery.d.ts +52 -0
  39. package/dist/proxy/authDiscovery.d.ts.map +1 -0
  40. package/dist/proxy/backends/antigravity.d.ts +8 -0
  41. package/dist/proxy/backends/antigravity.d.ts.map +1 -0
  42. package/dist/proxy/backends/antigravityAttachments.d.ts +16 -0
  43. package/dist/proxy/backends/antigravityAttachments.d.ts.map +1 -0
  44. package/dist/proxy/backends/antigravityGrammar.d.ts +12 -0
  45. package/dist/proxy/backends/antigravityGrammar.d.ts.map +1 -0
  46. package/dist/proxy/backends/antigravityJobs.d.ts +19 -0
  47. package/dist/proxy/backends/antigravityJobs.d.ts.map +1 -0
  48. package/dist/proxy/backends/antigravityMedia.d.ts +9 -0
  49. package/dist/proxy/backends/antigravityMedia.d.ts.map +1 -0
  50. package/dist/proxy/backends/antigravityNative.d.ts +8 -0
  51. package/dist/proxy/backends/antigravityNative.d.ts.map +1 -0
  52. package/dist/proxy/backends/antigravityOpenai.d.ts +9 -0
  53. package/dist/proxy/backends/antigravityOpenai.d.ts.map +1 -0
  54. package/dist/proxy/backends/antigravityOpenaiMedia.d.ts +9 -0
  55. package/dist/proxy/backends/antigravityOpenaiMedia.d.ts.map +1 -0
  56. package/dist/proxy/backends/antigravityPlugins.d.ts +41 -0
  57. package/dist/proxy/backends/antigravityPlugins.d.ts.map +1 -0
  58. package/dist/proxy/backends/antigravityProbe.d.ts +16 -0
  59. package/dist/proxy/backends/antigravityProbe.d.ts.map +1 -0
  60. package/dist/proxy/backends/antigravityProcess.d.ts +4 -0
  61. package/dist/proxy/backends/antigravityProcess.d.ts.map +1 -0
  62. package/dist/proxy/backends/antigravityProtocol.d.ts +582 -0
  63. package/dist/proxy/backends/antigravityProtocol.d.ts.map +1 -0
  64. package/dist/proxy/backends/antigravityReplay.d.ts +41 -0
  65. package/dist/proxy/backends/antigravityReplay.d.ts.map +1 -0
  66. package/dist/proxy/backends/antigravityResponses.d.ts +35 -0
  67. package/dist/proxy/backends/antigravityResponses.d.ts.map +1 -0
  68. package/dist/proxy/backends/antigravityRuntime.d.ts +189 -0
  69. package/dist/proxy/backends/antigravityRuntime.d.ts.map +1 -0
  70. package/dist/proxy/backends/antigravitySchema.d.ts +10 -0
  71. package/dist/proxy/backends/antigravitySchema.d.ts.map +1 -0
  72. package/dist/proxy/backends/antigravitySessions.d.ts +28 -0
  73. package/dist/proxy/backends/antigravitySessions.d.ts.map +1 -0
  74. package/dist/proxy/backends/antigravityState.d.ts +22 -0
  75. package/dist/proxy/backends/antigravityState.d.ts.map +1 -0
  76. package/dist/proxy/backends/antigravityStops.d.ts +10 -0
  77. package/dist/proxy/backends/antigravityStops.d.ts.map +1 -0
  78. package/dist/proxy/backends/antigravityTokens.d.ts +14 -0
  79. package/dist/proxy/backends/antigravityTokens.d.ts.map +1 -0
  80. package/dist/proxy/backends/antigravityUrl.d.ts +7 -0
  81. package/dist/proxy/backends/antigravityUrl.d.ts.map +1 -0
  82. package/dist/proxy/backends/providerStatus.d.ts +15 -0
  83. package/dist/proxy/backends/providerStatus.d.ts.map +1 -0
  84. package/dist/proxy/credentialsMode.d.ts +77 -0
  85. package/dist/proxy/credentialsMode.d.ts.map +1 -0
  86. package/dist/proxy/design.d.ts.map +1 -1
  87. package/dist/proxy/followActive.d.ts +262 -0
  88. package/dist/proxy/followActive.d.ts.map +1 -0
  89. package/dist/proxy/limitDetection.d.ts +110 -0
  90. package/dist/proxy/limitDetection.d.ts.map +1 -0
  91. package/dist/proxy/models.d.ts.map +1 -1
  92. package/dist/proxy/oauthPlan.d.ts +63 -0
  93. package/dist/proxy/oauthPlan.d.ts.map +1 -0
  94. package/dist/proxy/oauthUsage.d.ts +65 -2
  95. package/dist/proxy/oauthUsage.d.ts.map +1 -1
  96. package/dist/proxy/openai.d.ts +6 -2
  97. package/dist/proxy/openai.d.ts.map +1 -1
  98. package/dist/proxy/organizationName.d.ts +71 -0
  99. package/dist/proxy/organizationName.d.ts.map +1 -0
  100. package/dist/proxy/passthroughEarlyStop.d.ts +10 -0
  101. package/dist/proxy/passthroughEarlyStop.d.ts.map +1 -1
  102. package/dist/proxy/planAllowance.d.ts +75 -0
  103. package/dist/proxy/planAllowance.d.ts.map +1 -0
  104. package/dist/proxy/probes.d.ts +85 -0
  105. package/dist/proxy/probes.d.ts.map +1 -0
  106. package/dist/proxy/profileCli.d.ts +53 -1
  107. package/dist/proxy/profileCli.d.ts.map +1 -1
  108. package/dist/proxy/profileHealth.d.ts +127 -0
  109. package/dist/proxy/profileHealth.d.ts.map +1 -0
  110. package/dist/proxy/profileRename.d.ts +83 -0
  111. package/dist/proxy/profileRename.d.ts.map +1 -0
  112. package/dist/proxy/profiles.d.ts +72 -6
  113. package/dist/proxy/profiles.d.ts.map +1 -1
  114. package/dist/proxy/query.d.ts +7 -0
  115. package/dist/proxy/query.d.ts.map +1 -1
  116. package/dist/proxy/routing.d.ts +59 -1
  117. package/dist/proxy/routing.d.ts.map +1 -1
  118. package/dist/proxy/sdkFeatures.d.ts +1 -1
  119. package/dist/proxy/sdkFeatures.d.ts.map +1 -1
  120. package/dist/proxy/server.d.ts.map +1 -1
  121. package/dist/proxy/session/fingerprint.d.ts +5 -0
  122. package/dist/proxy/session/fingerprint.d.ts.map +1 -1
  123. package/dist/proxy/session/lineage.d.ts.map +1 -1
  124. package/dist/proxy/setup.d.ts.map +1 -1
  125. package/dist/proxy/supervision.d.ts +41 -0
  126. package/dist/proxy/supervision.d.ts.map +1 -0
  127. package/dist/proxy/tokenRefresh.d.ts +38 -4
  128. package/dist/proxy/tokenRefresh.d.ts.map +1 -1
  129. package/dist/proxy/types.d.ts +32 -0
  130. package/dist/proxy/types.d.ts.map +1 -1
  131. package/dist/server.js +11 -7
  132. package/dist/settings.d.ts +83 -0
  133. package/dist/settings.d.ts.map +1 -0
  134. package/dist/{setup-ndmjpy23.js → setup-7fr49z92.js} +1 -1
  135. package/dist/statusProbe-9x9q261e.js +151 -0
  136. package/dist/telemetry/dashboard.d.ts.map +1 -1
  137. package/dist/telemetry/index.d.ts +20 -2
  138. package/dist/telemetry/index.d.ts.map +1 -1
  139. package/dist/telemetry/landing.d.ts +1 -1
  140. package/dist/telemetry/landing.d.ts.map +1 -1
  141. package/dist/telemetry/logStore.d.ts +11 -0
  142. package/dist/telemetry/logStore.d.ts.map +1 -1
  143. package/dist/telemetry/pricingStore.d.ts +2 -2
  144. package/dist/telemetry/pricingStore.d.ts.map +1 -1
  145. package/dist/telemetry/profileBar.d.ts +3 -3
  146. package/dist/telemetry/profileBar.d.ts.map +1 -1
  147. package/dist/telemetry/profileFacts.d.ts +17 -0
  148. package/dist/telemetry/profileFacts.d.ts.map +1 -0
  149. package/dist/telemetry/profileOrder.d.ts +68 -0
  150. package/dist/telemetry/profileOrder.d.ts.map +1 -0
  151. package/dist/telemetry/profilePage.d.ts.map +1 -1
  152. package/dist/telemetry/profileSort.d.ts +35 -0
  153. package/dist/telemetry/profileSort.d.ts.map +1 -0
  154. package/dist/telemetry/profileSpent.d.ts +68 -0
  155. package/dist/telemetry/profileSpent.d.ts.map +1 -0
  156. package/dist/telemetry/providerPage.d.ts +2 -0
  157. package/dist/telemetry/providerPage.d.ts.map +1 -0
  158. package/dist/telemetry/providerSetup.d.ts +5 -0
  159. package/dist/telemetry/providerSetup.d.ts.map +1 -0
  160. package/dist/telemetry/providerView.d.ts +45 -0
  161. package/dist/telemetry/providerView.d.ts.map +1 -0
  162. package/dist/telemetry/routeChain.d.ts +32 -0
  163. package/dist/telemetry/routeChain.d.ts.map +1 -0
  164. package/dist/telemetry/routes.d.ts.map +1 -1
  165. package/dist/telemetry/settingsPage.d.ts +1 -1
  166. package/dist/telemetry/settingsPage.d.ts.map +1 -1
  167. package/dist/telemetry/sqlite.d.ts.map +1 -1
  168. package/dist/telemetry/store.d.ts +6 -1
  169. package/dist/telemetry/store.d.ts.map +1 -1
  170. package/dist/telemetry/types.d.ts +132 -2
  171. package/dist/telemetry/types.d.ts.map +1 -1
  172. package/dist/{tokenRefresh-kzz08kea.js → tokenRefresh-eg9h83wd.js} +6 -1
  173. package/package.json +11 -4
  174. package/dist/cli-5jxyma6z.js +0 -277
  175. package/dist/cli-vj9cv18n.js +0 -38
  176. package/dist/proxy/settings.d.ts +0 -30
  177. package/dist/proxy/settings.d.ts.map +0 -1
@@ -1,3 +1,8 @@
1
+ import {
2
+ envBool,
3
+ init_env
4
+ } from "./cli-1jstkdj9.js";
5
+
1
6
  // src/proxy/tokenRefresh.ts
2
7
  import { execFile as execFileCb } from "node:child_process";
3
8
  import { createHash } from "node:crypto";
@@ -68,6 +73,141 @@ var claudeLog = (event, extra) => {
68
73
  console.debug(`[opencode-claude-code-provider] ${JSON.stringify(payload)}`);
69
74
  };
70
75
 
76
+ // src/proxy/credentialsMode.ts
77
+ init_env();
78
+ function isCredentialsReadOnly() {
79
+ return envBool("CREDENTIALS_READONLY");
80
+ }
81
+ function refuseCredentialWrite(operation, target) {
82
+ console.error(`[PROXY] REFUSED credential write (${operation} → ${target}): ` + `MERIDIAN_CREDENTIALS_READONLY is set, so this instance must not modify credentials. ` + `If this is unexpected, the calling code path is missing a read-only guard.`);
83
+ return false;
84
+ }
85
+ var bannerLogged = false;
86
+ function logCredentialsModeBanner() {
87
+ if (bannerLogged || !isCredentialsReadOnly())
88
+ return;
89
+ bannerLogged = true;
90
+ console.warn(`[PROXY] CREDENTIALS READ-ONLY MODE (MERIDIAN_CREDENTIALS_READONLY): ` + `this instance will not refresh or write OAuth credentials. ` + `Token rotation is another instance's job; credentials are re-read from disk.`);
91
+ }
92
+
93
+ // src/proxy/authDiscovery.ts
94
+ var SAFE_AUTH_STRING_KEYS = new Set([
95
+ "token_type",
96
+ "expires_at",
97
+ "expires_in",
98
+ "refresh_token_expires_at",
99
+ "refresh_token_expires_in",
100
+ "scope",
101
+ "scopes",
102
+ "organization_type",
103
+ "rate_limit_tier",
104
+ "billing_type",
105
+ "subscriptionType",
106
+ "rateLimitTier",
107
+ "seat_tier",
108
+ "seatTier"
109
+ ]);
110
+ function describeValue(key, value) {
111
+ if (value === null || value === undefined)
112
+ return value;
113
+ if (typeof value === "string") {
114
+ return SAFE_AUTH_STRING_KEYS.has(key) ? value : `[string len=${value.length}]`;
115
+ }
116
+ if (typeof value === "number" || typeof value === "boolean")
117
+ return value;
118
+ if (Array.isArray(value))
119
+ return value.map((item) => describeValue(key, item));
120
+ if (typeof value === "object") {
121
+ const out = {};
122
+ for (const [k, v] of Object.entries(value)) {
123
+ out[k] = describeValue(k, v);
124
+ }
125
+ return out;
126
+ }
127
+ return `[${typeof value}]`;
128
+ }
129
+ function describeAuthFields(payload) {
130
+ return describeValue("", payload);
131
+ }
132
+ function authFieldPaths(payload, prefix = "") {
133
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
134
+ return [];
135
+ const paths = [];
136
+ for (const [key, value] of Object.entries(payload)) {
137
+ const path = prefix ? `${prefix}.${key}` : key;
138
+ paths.push(path);
139
+ paths.push(...authFieldPaths(value, path));
140
+ }
141
+ return paths;
142
+ }
143
+
144
+ // src/proxy/oauthPlan.ts
145
+ var OAUTH_PROFILE_URL = "https://api.anthropic.com/api/oauth/profile";
146
+ function subscriptionTypeFromOrganizationType(organizationType) {
147
+ switch (organizationType) {
148
+ case "claude_max":
149
+ return "max";
150
+ case "claude_pro":
151
+ return "pro";
152
+ case "claude_team":
153
+ return "team";
154
+ case "claude_enterprise":
155
+ return "enterprise";
156
+ default:
157
+ return;
158
+ }
159
+ }
160
+ function extractPlanFields(profile) {
161
+ const subscriptionType = subscriptionTypeFromOrganizationType(profile?.organization?.organization_type);
162
+ const rateLimitTier = profile?.organization?.rate_limit_tier;
163
+ const seatTier = profile?.organization?.seat_tier;
164
+ return {
165
+ ...subscriptionType ? { subscriptionType } : {},
166
+ ...rateLimitTier ? { rateLimitTier } : {},
167
+ ...seatTier ? { seatTier } : {}
168
+ };
169
+ }
170
+ function planFieldsMissing(fields) {
171
+ return !fields?.subscriptionType || !fields?.rateLimitTier && !fields?.seatTier;
172
+ }
173
+ async function fetchOAuthPlanFields(accessToken) {
174
+ let response;
175
+ try {
176
+ response = await fetch(OAUTH_PROFILE_URL, {
177
+ headers: {
178
+ Authorization: `Bearer ${accessToken}`,
179
+ "Content-Type": "application/json",
180
+ "Cache-Control": "no-cache"
181
+ },
182
+ signal: AbortSignal.timeout(1e4)
183
+ });
184
+ } catch (err) {
185
+ claudeLog("auth.profile_request_failed", { error: String(err) });
186
+ console.warn(`[meridian] Could not read the account plan: ${err instanceof Error ? err.message : err}`);
187
+ return {};
188
+ }
189
+ if (!response.ok) {
190
+ claudeLog("auth.profile_bad_response", { status: response.status });
191
+ console.warn(`[meridian] Could not read the account plan (${response.status}).`);
192
+ return {};
193
+ }
194
+ let profile;
195
+ try {
196
+ profile = await response.json();
197
+ } catch (err) {
198
+ claudeLog("auth.profile_parse_failed", { error: String(err) });
199
+ console.warn(`[meridian] Account plan response was not valid JSON: ${err instanceof Error ? err.message : err}`);
200
+ return {};
201
+ }
202
+ const plan = extractPlanFields(profile);
203
+ claudeLog("auth.profile_discovered", {
204
+ fields: authFieldPaths(profile),
205
+ payload: describeAuthFields(profile),
206
+ plan
207
+ });
208
+ return plan;
209
+ }
210
+
71
211
  // src/proxy/tokenRefresh.ts
72
212
  var execFile = promisify(execFileCb);
73
213
  var OAUTH_TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
@@ -85,6 +225,17 @@ function configDirToKeychainService(claudeConfigDir) {
85
225
  function configDirToCredentialsFile(claudeConfigDir) {
86
226
  return join(resolve(claudeConfigDir), ".credentials.json");
87
227
  }
228
+ function guardCredentialWrites(store) {
229
+ return {
230
+ ...store,
231
+ async write(credentials) {
232
+ if (isCredentialsReadOnly()) {
233
+ return refuseCredentialWrite("credential-store", store.refreshKey ?? "unknown-store");
234
+ }
235
+ return store.write(credentials);
236
+ }
237
+ };
238
+ }
88
239
  function serializeCredentials(credentials) {
89
240
  return JSON.stringify(credentials);
90
241
  }
@@ -101,7 +252,7 @@ function parseKeychainValue(raw) {
101
252
  }
102
253
  var keychainWasHexByService = new Map;
103
254
  function buildMacosStore(serviceName) {
104
- return {
255
+ return guardCredentialWrites({
105
256
  refreshKey: `keychain:${serviceName}`,
106
257
  async read() {
107
258
  try {
@@ -128,12 +279,12 @@ function buildMacosStore(serviceName) {
128
279
  return false;
129
280
  }
130
281
  }
131
- };
282
+ });
132
283
  }
133
284
  var macosStore = buildMacosStore(KEYCHAIN_SERVICE);
134
285
  function buildFileStore(filePath) {
135
286
  const absPath = resolve(filePath);
136
- return {
287
+ return guardCredentialWrites({
137
288
  refreshKey: `file:${absPath}`,
138
289
  async read() {
139
290
  try {
@@ -155,7 +306,7 @@ function buildFileStore(filePath) {
155
306
  return false;
156
307
  }
157
308
  }
158
- };
309
+ });
159
310
  }
160
311
  var fileStore = buildFileStore(CREDENTIALS_FILE);
161
312
  function createPlatformCredentialStore(opts) {
@@ -170,10 +321,25 @@ function createPlatformCredentialStore(opts) {
170
321
  function credentialsFilePathForProfile(claudeConfigDir) {
171
322
  return claudeConfigDir ? configDirToCredentialsFile(claudeConfigDir) : CREDENTIALS_FILE;
172
323
  }
324
+ async function readStoredCredentialPresence(store) {
325
+ let credentials;
326
+ try {
327
+ credentials = await store.read();
328
+ } catch {
329
+ return "unknown";
330
+ }
331
+ if (!credentials)
332
+ return "unknown";
333
+ const accessToken = credentials.claudeAiOauth?.accessToken;
334
+ return typeof accessToken === "string" && accessToken.length > 0 ? "present" : "absent";
335
+ }
173
336
  var inflightRefreshByKey = new Map;
174
337
  var inflightRefreshByStore = new WeakMap;
175
338
  async function refreshOAuthToken(store) {
176
339
  const s = store ?? createPlatformCredentialStore();
340
+ if (isCredentialsReadOnly()) {
341
+ return refuseCredentialWrite("oauth-refresh", s.refreshKey ?? "unknown-store");
342
+ }
177
343
  const refreshKey = s.refreshKey;
178
344
  if (refreshKey) {
179
345
  const inflight2 = inflightRefreshByKey.get(refreshKey);
@@ -244,10 +410,18 @@ async function doRefresh(store) {
244
410
  expiresAt,
245
411
  ...refreshTokenExpiresAt ? { refreshTokenExpiresAt } : {}
246
412
  };
413
+ const backfilled = planFieldsMissing(credentials.claudeAiOauth) ? await fetchOAuthPlanFields(tokenData.access_token) : {};
414
+ if (backfilled.subscriptionType || backfilled.rateLimitTier) {
415
+ credentials.claudeAiOauth = { ...backfilled, ...credentials.claudeAiOauth };
416
+ }
247
417
  const written = await store.write(credentials);
248
418
  if (!written)
249
419
  return false;
250
- claudeLog("token_refresh.success", { expiresAt, refreshTokenExpiresAt });
420
+ claudeLog("token_refresh.success", {
421
+ expiresAt,
422
+ refreshTokenExpiresAt,
423
+ backfilledPlan: Object.keys(backfilled)
424
+ });
251
425
  return true;
252
426
  }
253
427
  async function ensureFreshToken(store, bufferMs = 5 * 60 * 1000) {
@@ -258,6 +432,8 @@ async function ensureFreshToken(store, bufferMs = 5 * 60 * 1000) {
258
432
  return false;
259
433
  if (expiresAt - Date.now() > bufferMs)
260
434
  return true;
435
+ if (isCredentialsReadOnly())
436
+ return false;
261
437
  return refreshOAuthToken(s);
262
438
  }
263
439
  var DEFAULT_RENEWAL_WARN_DAYS = 3;
@@ -265,20 +441,20 @@ function resolveRenewalWarnDays(raw) {
265
441
  const parsed = raw ? Number(raw) : NaN;
266
442
  return Number.isFinite(parsed) && parsed >= 0 ? parsed : DEFAULT_RENEWAL_WARN_DAYS;
267
443
  }
268
- var RENEWAL_EXPIRY_TTL_MS = 5 * 60000;
269
- var renewalExpiryCache = new Map;
270
- var renewalExpiryInflight = new Map;
444
+ var CREDENTIAL_FACTS_TTL_MS = 5 * 60000;
445
+ var credentialFactsCache = new Map;
446
+ var credentialFactsInflight = new Map;
271
447
  function resetAuthRenewalCache() {
272
- renewalExpiryCache.clear();
273
- renewalExpiryInflight.clear();
448
+ credentialFactsCache.clear();
449
+ credentialFactsInflight.clear();
274
450
  }
275
- async function readRefreshTokenExpiry(s) {
451
+ async function readCredentialFacts(s) {
276
452
  const key = s.refreshKey;
277
453
  if (key) {
278
- const cached = renewalExpiryCache.get(key);
279
- if (cached && Date.now() - cached.at < RENEWAL_EXPIRY_TTL_MS)
454
+ const cached = credentialFactsCache.get(key);
455
+ if (cached && Date.now() - cached.at < CREDENTIAL_FACTS_TTL_MS)
280
456
  return cached.value;
281
- const inflight = renewalExpiryInflight.get(key);
457
+ const inflight = credentialFactsInflight.get(key);
282
458
  if (inflight)
283
459
  return inflight;
284
460
  }
@@ -287,25 +463,40 @@ async function readRefreshTokenExpiry(s) {
287
463
  try {
288
464
  credentials = await s.read();
289
465
  } catch {
290
- return;
466
+ return {};
291
467
  }
292
- const value = credentials?.claudeAiOauth?.refreshTokenExpiresAt;
468
+ const oauth = credentials?.claudeAiOauth;
469
+ const value = {
470
+ refreshTokenExpiresAt: oauth?.refreshTokenExpiresAt,
471
+ subscriptionType: oauth?.subscriptionType,
472
+ rateLimitTier: oauth?.rateLimitTier,
473
+ seatTier: oauth?.seatTier
474
+ };
293
475
  if (key)
294
- renewalExpiryCache.set(key, { value, at: Date.now() });
476
+ credentialFactsCache.set(key, { value, at: Date.now() });
295
477
  return value;
296
478
  })();
297
479
  if (!key)
298
480
  return read;
299
- renewalExpiryInflight.set(key, read);
481
+ credentialFactsInflight.set(key, read);
300
482
  try {
301
483
  return await read;
302
484
  } finally {
303
- renewalExpiryInflight.delete(key);
485
+ credentialFactsInflight.delete(key);
304
486
  }
305
487
  }
488
+ async function getStoredPlanFields(store) {
489
+ const s = store ?? createPlatformCredentialStore();
490
+ const { subscriptionType, rateLimitTier, seatTier } = await readCredentialFacts(s);
491
+ return {
492
+ ...subscriptionType ? { subscriptionType } : {},
493
+ ...rateLimitTier ? { rateLimitTier } : {},
494
+ ...seatTier ? { seatTier } : {}
495
+ };
496
+ }
306
497
  async function getAuthRenewalStatus(store, warnDays = DEFAULT_RENEWAL_WARN_DAYS) {
307
498
  const s = store ?? createPlatformCredentialStore();
308
- const refreshTokenExpiresAt = await readRefreshTokenExpiry(s);
499
+ const { refreshTokenExpiresAt } = await readCredentialFacts(s);
309
500
  if (!refreshTokenExpiresAt)
310
501
  return { renewalRequiredSoon: false };
311
502
  const msRemaining = refreshTokenExpiresAt - Date.now();
@@ -320,6 +511,8 @@ var scheduledRefreshTimer = null;
320
511
  var scheduledRefreshActive = false;
321
512
  var scheduledRefreshGeneration = 0;
322
513
  function startBackgroundRefresh(store, bufferMs = 5 * 60 * 1000, failureRetryMs = 5 * 60 * 1000) {
514
+ if (isCredentialsReadOnly())
515
+ return;
323
516
  if (scheduledRefreshActive)
324
517
  return;
325
518
  scheduledRefreshActive = true;
@@ -374,4 +567,4 @@ function resetInflightRefresh() {
374
567
  inflightRefreshByKey.clear();
375
568
  }
376
569
 
377
- export { withClaudeLogContext, claudeLog, configDirToKeychainService, configDirToCredentialsFile, serializeCredentials, createPlatformCredentialStore, credentialsFilePathForProfile, refreshOAuthToken, ensureFreshToken, DEFAULT_RENEWAL_WARN_DAYS, resolveRenewalWarnDays, resetAuthRenewalCache, getAuthRenewalStatus, startBackgroundRefresh, stopBackgroundRefresh, isBackgroundRefreshActive, resetInflightRefresh };
570
+ export { withClaudeLogContext, claudeLog, isCredentialsReadOnly, refuseCredentialWrite, logCredentialsModeBanner, describeAuthFields, authFieldPaths, fetchOAuthPlanFields, configDirToKeychainService, configDirToCredentialsFile, serializeCredentials, createPlatformCredentialStore, credentialsFilePathForProfile, readStoredCredentialPresence, refreshOAuthToken, ensureFreshToken, DEFAULT_RENEWAL_WARN_DAYS, resolveRenewalWarnDays, resetAuthRenewalCache, getStoredPlanFields, getAuthRenewalStatus, startBackgroundRefresh, stopBackgroundRefresh, isBackgroundRefreshActive, resetInflightRefresh };