@omnicross/subscriptions 0.1.0 → 0.1.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.
package/dist/index.cjs CHANGED
@@ -1,68 +1,281 @@
1
- "use strict";
2
- var __create = Object.create;
3
- var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
- var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7; var _class8; var _class9;
2
+
3
+
4
+
5
+ var _chunkTPW5Q25Ycjs = require('./chunk-TPW5Q25Y.cjs');
6
+
7
+ // src/scheduler/SubscriptionAccountSelector.ts
8
+ var SESSION_AFFINITY_TTL_MS = 36e5;
9
+ var LAST_USED_PERSIST_THROTTLE_MS = 6e4;
10
+ var DEFAULT_ACCOUNT_PRIORITY = 50;
11
+ function scopedKey(providerId, tail) {
12
+ return `${providerId}\0${tail}`;
13
+ }
14
+ function parseIso(iso) {
15
+ if (!iso) return 0;
16
+ const ms = Date.parse(iso);
17
+ return Number.isNaN(ms) ? 0 : ms;
18
+ }
19
+ var SubscriptionAccountSelector = (_class = class {constructor() { _class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this); }
20
+ /** `providerId\0accountId → live lastUsedAt ms` (authoritative tie-break). */
21
+ __init() {this.lastUsedOverlay = /* @__PURE__ */ new Map()}
22
+ /** `providerId\0sessionKey → { accountId, expiresAt }`. */
23
+ __init2() {this.affinity = /* @__PURE__ */ new Map()}
24
+ /** `providerId\0accountId → last durable-persist ms` (throttle state). */
25
+ __init3() {this.lastPersist = /* @__PURE__ */ new Map()}
26
+ /**
27
+ * Choose the account to serve this request, or `null` when there are ≤ 1
28
+ * schedulable accounts (the zero-regression signal — caller uses the active
29
+ * account). Updates the live `lastUsedAt` overlay for the chosen account and,
30
+ * when a `sessionKey` is given, records/extends the affinity mapping.
31
+ */
32
+ select(input) {
33
+ const now = _nullishCoalesce(input.now, () => ( Date.now()));
34
+ const schedulable = input.accounts.filter((a) => a.schedulable !== false);
35
+ if (schedulable.length <= 1) return null;
36
+ if (input.sessionKey) {
37
+ const aKey = scopedKey(input.providerId, input.sessionKey);
38
+ const hit = this.affinity.get(aKey);
39
+ if (hit && hit.expiresAt > now && schedulable.some((a) => a.id === hit.accountId)) {
40
+ hit.expiresAt = now + SESSION_AFFINITY_TTL_MS;
41
+ this.markUsed(input.providerId, hit.accountId, now);
42
+ return { accountId: hit.accountId, isActive: hit.accountId === input.activeAccountId };
43
+ }
44
+ }
45
+ const chosen = this.pickOrdered(input.providerId, schedulable);
46
+ this.markUsed(input.providerId, chosen.id, now);
47
+ if (input.sessionKey) {
48
+ this.affinity.set(scopedKey(input.providerId, input.sessionKey), {
49
+ accountId: chosen.id,
50
+ expiresAt: now + SESSION_AFFINITY_TTL_MS
51
+ });
52
+ }
53
+ return { accountId: chosen.id, isActive: chosen.id === input.activeAccountId };
17
54
  }
18
- return to;
19
- };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
55
+ /**
56
+ * Drop every session-affinity mapping bound to this account (subscription-account-
57
+ * health, task 4.1). Called when a selected account's by-id token turns out
58
+ * null/invalid or an affinity-bound account becomes unhealthy, so the next
59
+ * selection for those sessions picks a fresh account instead of re-sticking to
60
+ * the bad one. O(affinity entries) the map is tiny (one entry per live
61
+ * conversation).
62
+ */
63
+ evictAffinity(providerId, accountId) {
64
+ const prefix = `${providerId} `;
65
+ for (const [key, entry] of this.affinity) {
66
+ if (entry.accountId === accountId && key.startsWith(prefix)) {
67
+ this.affinity.delete(key);
68
+ }
69
+ }
70
+ }
71
+ /**
72
+ * Whether a best-effort `lastUsedAt` durable persist is DUE for this account
73
+ * (≥ `LAST_USED_PERSIST_THROTTLE_MS` since the last one). Records the persist
74
+ * time when it returns `true`, so the strategy calls `touchAccountLastUsed`
75
+ * sparingly and the request hot path does not rewrite the store every request.
76
+ */
77
+ duePersist(providerId, accountId, now = Date.now()) {
78
+ const key = scopedKey(providerId, accountId);
79
+ const last = _nullishCoalesce(this.lastPersist.get(key), () => ( 0));
80
+ if (now - last < LAST_USED_PERSIST_THROTTLE_MS) return false;
81
+ this.lastPersist.set(key, now);
82
+ return true;
83
+ }
84
+ /** Sort by `priority` asc → effective `lastUsedAt` asc → `createdAt` asc → `[0]`. */
85
+ pickOrdered(providerId, accounts) {
86
+ const ranked = accounts.map((account) => ({
87
+ account,
88
+ priority: _nullishCoalesce(account.priority, () => ( DEFAULT_ACCOUNT_PRIORITY)),
89
+ lastUsed: this.effectiveLastUsed(providerId, account),
90
+ created: parseIso(account.createdAt)
91
+ }));
92
+ ranked.sort(
93
+ (x, y) => x.priority - y.priority || x.lastUsed - y.lastUsed || x.created - y.created
94
+ );
95
+ return ranked[0].account;
96
+ }
97
+ /** The live tie-break value: the in-memory overlay when set, else the persisted
98
+ * `lastUsedAt` (0 when absent). */
99
+ effectiveLastUsed(providerId, account) {
100
+ const overlay = this.lastUsedOverlay.get(scopedKey(providerId, account.id));
101
+ return _nullishCoalesce(overlay, () => ( parseIso(account.lastUsedAt)));
102
+ }
103
+ markUsed(providerId, accountId, now) {
104
+ this.lastUsedOverlay.set(scopedKey(providerId, accountId), now);
105
+ }
106
+ }, _class);
107
+
108
+ // src/SubscriptionAccountService.ts
109
+ var _SubscriptionAccountHealth = require('@omnicross/core/pipeline/SubscriptionAccountHealth');
110
+
111
+ // src/scheduler/accountModelMap.ts
112
+ function canonicalModelId(value) {
113
+ const idx = value.indexOf(",");
114
+ const bare = idx >= 0 ? value.slice(idx + 1) : value;
115
+ return bare.trim().toLowerCase();
116
+ }
117
+ function accountSupportsModel(supportedModels, model) {
118
+ if (!supportedModels) return true;
119
+ const target = canonicalModelId(model);
120
+ if (Array.isArray(supportedModels)) {
121
+ return supportedModels.some((m) => canonicalModelId(m) === target);
122
+ }
123
+ return Object.keys(supportedModels).some((k) => canonicalModelId(k) === target);
124
+ }
125
+ function remapForAccount(supportedModels, model) {
126
+ if (!supportedModels || Array.isArray(supportedModels)) return model;
127
+ const target = canonicalModelId(model);
128
+ for (const [key, actual] of Object.entries(supportedModels)) {
129
+ if (canonicalModelId(key) === target) return actual;
130
+ }
131
+ return model;
132
+ }
133
+ function remapReportForAccount(supportedModels, model) {
134
+ if (!model) return void 0;
135
+ const remapped = remapForAccount(supportedModels, model);
136
+ return remapped === model ? void 0 : remapped;
137
+ }
29
138
 
30
- // src/index.ts
31
- var src_exports = {};
32
- __export(src_exports, {
33
- SubscriptionAccountService: () => SubscriptionAccountService,
34
- SubscriptionDispatcher: () => SubscriptionDispatcher,
35
- SubscriptionProviderRegistry: () => SubscriptionProviderRegistry,
36
- claudeOAuth: () => claude_exports,
37
- codexOAuth: () => codex_exports,
38
- geminiOAuth: () => gemini_exports,
39
- getSubscriptionAccountService: () => getSubscriptionAccountService,
40
- getSubscriptionProviderRegistry: () => getSubscriptionProviderRegistry,
41
- setSubscriptionAccountService: () => setSubscriptionAccountService,
42
- setSubscriptionProviderRegistry: () => setSubscriptionProviderRegistry
43
- });
44
- module.exports = __toCommonJS(src_exports);
139
+ // src/scheduler/accountSelection.ts
140
+ var ACCOUNTS_KEY = {
141
+ claude: "claudeAccounts",
142
+ codex: "codexAccounts",
143
+ gemini: "geminiAccounts",
144
+ opencodego: "opencodegoAccounts"
145
+ };
146
+ var ACTIVE_KEY = {
147
+ claude: "activeClaudeAccountId",
148
+ codex: "activeCodexAccountId",
149
+ gemini: "activeGeminiAccountId",
150
+ opencodego: "activeOpencodegoAccountId"
151
+ };
152
+ function gateSchedulable(accounts, providerId, health, now, resolvedModel, supportedModelsById) {
153
+ if (!health && !resolvedModel || accounts.length < 2) return accounts;
154
+ return accounts.map((a) => {
155
+ const healthOk = health ? health.isSchedulable(providerId, a.id, now) : true;
156
+ const modelOk = resolvedModel ? accountSupportsModel(supportedModelsById.get(a.id), resolvedModel) : true;
157
+ return { ...a, schedulable: healthOk && modelOk };
158
+ });
159
+ }
160
+ function isPoolGated(accounts, health, resolvedModel) {
161
+ return (health !== void 0 || resolvedModel !== void 0) && accounts.length >= 2;
162
+ }
163
+ function readSchedulableAccounts(config, providerId) {
164
+ const raw = _nullishCoalesce(config[ACCOUNTS_KEY[providerId]], () => ( []));
165
+ const accounts = raw.map((a) => ({
166
+ id: a.id,
167
+ priority: a.priority,
168
+ lastUsedAt: a.lastUsedAt,
169
+ createdAt: a.createdAt
170
+ }));
171
+ const supportedModelsById = new Map(
172
+ raw.map((a) => [a.id, a.supportedModels])
173
+ );
174
+ const activeAccountId = config[ACTIVE_KEY[providerId]];
175
+ return { accounts, activeAccountId, supportedModelsById };
176
+ }
177
+ function pickByIdTarget(selector, gated, providerId, activeAccountId, sessionKey, now, healthGated) {
178
+ const selection = selector.select({ providerId, accounts: gated, activeAccountId, sessionKey, now });
179
+ if (selection && !selection.isActive) return selection.accountId;
180
+ if (selection === null && healthGated) {
181
+ const schedulable = gated.filter((a) => a.schedulable !== false);
182
+ if (schedulable.length === 1 && schedulable[0].id !== activeAccountId) return schedulable[0].id;
183
+ }
184
+ return void 0;
185
+ }
186
+ async function resolveSelectedToken(selector, tokens, providerId, sessionKey, activeGetter, ctx) {
187
+ const health = _optionalChain([ctx, 'optionalAccess', _ => _.health]);
188
+ const report = _optionalChain([ctx, 'optionalAccess', _2 => _2.reportSelection]);
189
+ const now = _optionalChain([ctx, 'optionalAccess', _3 => _3.now]);
190
+ const resolvedModel = _optionalChain([ctx, 'optionalAccess', _4 => _4.resolvedModel]);
191
+ if (selector && tokens.getAccessTokenForAccount) {
192
+ const config = await tokens.getFullConfig();
193
+ const { accounts, activeAccountId, supportedModelsById } = readSchedulableAccounts(config, providerId);
194
+ const gated = gateSchedulable(accounts, providerId, health, now, resolvedModel, supportedModelsById);
195
+ const poolGated = isPoolGated(accounts, health, resolvedModel);
196
+ const remapFor = (id) => remapReportForAccount(supportedModelsById.get(id), resolvedModel);
197
+ const targetId = pickByIdTarget(selector, gated, providerId, activeAccountId, sessionKey, now, poolGated);
198
+ if (targetId !== void 0) {
199
+ const byId = await tokens.getAccessTokenForAccount(providerId, targetId);
200
+ if (byId) {
201
+ maybeTouchLastUsed(selector, tokens, providerId, targetId);
202
+ _optionalChain([report, 'optionalCall', _5 => _5(targetId, false, remapFor(targetId))]);
203
+ return byId;
204
+ }
205
+ selector.evictAffinity(providerId, targetId);
206
+ _optionalChain([health, 'optionalAccess', _6 => _6.recordUpstreamOutcome, 'call', _7 => _7(providerId, targetId, { status: 401, now })]);
207
+ const remaining = gated.filter((a) => a.id !== targetId);
208
+ const retryId = pickByIdTarget(selector, remaining, providerId, activeAccountId, sessionKey, now, poolGated);
209
+ if (retryId !== void 0) {
210
+ const retryToken = await tokens.getAccessTokenForAccount(providerId, retryId);
211
+ if (retryToken) {
212
+ maybeTouchLastUsed(selector, tokens, providerId, retryId);
213
+ _optionalChain([report, 'optionalCall', _8 => _8(retryId, false, remapFor(retryId))]);
214
+ return retryToken;
215
+ }
216
+ }
217
+ }
218
+ if (activeAccountId) _optionalChain([report, 'optionalCall', _9 => _9(activeAccountId, true, remapFor(activeAccountId))]);
219
+ return activeGetter();
220
+ }
221
+ return activeGetter();
222
+ }
223
+ function maybeTouchLastUsed(selector, tokens, providerId, accountId) {
224
+ if (!tokens.touchAccountLastUsed) return;
225
+ if (!selector.duePersist(providerId, accountId)) return;
226
+ void tokens.touchAccountLastUsed(providerId, accountId, (/* @__PURE__ */ new Date()).toISOString()).catch(() => {
227
+ });
228
+ }
229
+ async function refreshSelectedAccount(selector, tokens, mutex, providerId, sessionKey) {
230
+ if (!sessionKey || !selector || !tokens.refreshAccountToken) return null;
231
+ const config = await tokens.getFullConfig();
232
+ const { accounts, activeAccountId } = readSchedulableAccounts(config, providerId);
233
+ const selection = selector.select({ providerId, accounts, activeAccountId, sessionKey });
234
+ if (!selection || selection.isActive) return null;
235
+ const accountId = selection.accountId;
236
+ return mutex.run(`${providerId}:${accountId}`, async () => {
237
+ try {
238
+ return await _asyncNullishCoalesce(await tokens.refreshAccountToken(providerId, accountId), async () => ( false));
239
+ } catch (err) {
240
+ console.warn(`[accountSelection] ${providerId}:${accountId} by-id refresh failed:`, err);
241
+ return false;
242
+ }
243
+ });
244
+ }
45
245
 
46
246
  // src/auth/OAuthBearerAuthStrategy.ts
47
247
  var REFRESH_LEAD_MS = 5 * 6e4;
48
- var OAuthBearerAuthStrategy = class {
49
- constructor(providerId, tokens, mutex) {
248
+ var OAuthBearerAuthStrategy = (_class2 = class {
249
+ constructor(providerId, tokens, mutex, selector, health) {;_class2.prototype.__init4.call(this);
50
250
  this.tokens = tokens;
51
251
  this.mutex = mutex;
252
+ this.selector = selector;
253
+ this.health = health;
52
254
  this.providerId = providerId;
53
255
  }
54
- tokens;
55
- mutex;
56
- kind = "oauth-bearer";
57
- providerId;
58
- async applyHeaders(headers, _hints) {
59
- const token = await this.resolveAccessToken();
256
+
257
+
258
+
259
+
260
+ __init4() {this.kind = "oauth-bearer"}
261
+
262
+ async applyHeaders(headers, hints) {
263
+ const token = await resolveSelectedToken(
264
+ this.selector,
265
+ this.tokens,
266
+ this.providerId,
267
+ _optionalChain([hints, 'optionalAccess', _10 => _10.sessionKey]),
268
+ () => this.resolveAccessToken(),
269
+ { health: this.health, reportSelection: _optionalChain([hints, 'optionalAccess', _11 => _11.reportSelection]), resolvedModel: _optionalChain([hints, 'optionalAccess', _12 => _12.resolvedModel]) }
270
+ );
60
271
  if (!token) {
61
272
  return;
62
273
  }
63
274
  headers["Authorization"] = `Bearer ${token}`;
64
275
  }
65
- async onUnauthorized() {
276
+ async onUnauthorized(sessionKey) {
277
+ const byId = await refreshSelectedAccount(this.selector, this.tokens, this.mutex, this.providerId, sessionKey);
278
+ if (byId !== null) return byId;
66
279
  return this.mutex.run(`${this.providerId}:refresh`, async () => {
67
280
  try {
68
281
  return this.providerId === "codex" ? await this.tokens.refreshCodexToken() : await this.tokens.refreshGeminiToken();
@@ -75,7 +288,7 @@ var OAuthBearerAuthStrategy = class {
75
288
  async describeStatus() {
76
289
  const config = await this.tokens.getFullConfig();
77
290
  const entry = this.providerId === "codex" ? config.codex : config.gemini;
78
- if (!entry?.accessToken) {
291
+ if (!_optionalChain([entry, 'optionalAccess', _13 => _13.accessToken])) {
79
292
  return { providerId: this.providerId, ok: false, reason: "missing-credential" };
80
293
  }
81
294
  if (entry.status === "expired") {
@@ -92,7 +305,7 @@ var OAuthBearerAuthStrategy = class {
92
305
  async resolveAccessToken() {
93
306
  const config = await this.tokens.getFullConfig();
94
307
  const entry = this.providerId === "codex" ? config.codex : config.gemini;
95
- if (!entry?.accessToken) return null;
308
+ if (!_optionalChain([entry, 'optionalAccess', _14 => _14.accessToken])) return null;
96
309
  const expiresAtMs = entry.expiresAt ? new Date(entry.expiresAt).getTime() : 0;
97
310
  const expiringSoon = expiresAtMs > 0 && Date.now() >= expiresAtMs - REFRESH_LEAD_MS;
98
311
  if (expiringSoon && entry.refreshToken) {
@@ -102,29 +315,42 @@ var OAuthBearerAuthStrategy = class {
102
315
  if (!refreshed) return null;
103
316
  const fresh = await this.tokens.getFullConfig();
104
317
  const freshEntry = this.providerId === "codex" ? fresh.codex : fresh.gemini;
105
- return freshEntry?.accessToken ?? null;
318
+ return _nullishCoalesce(_optionalChain([freshEntry, 'optionalAccess', _15 => _15.accessToken]), () => ( null));
106
319
  }
107
320
  if (entry.status === "expired") return null;
108
321
  return entry.accessToken;
109
322
  }
110
- };
323
+ }, _class2);
111
324
 
112
325
  // src/auth/PassThroughAuthStrategy.ts
113
- var PassThroughAuthStrategy = class {
114
- constructor(tokens, mutex) {
326
+ var PassThroughAuthStrategy = (_class3 = class {
327
+ constructor(tokens, mutex, selector, health) {;_class3.prototype.__init5.call(this);_class3.prototype.__init6.call(this);
115
328
  this.tokens = tokens;
116
329
  this.mutex = mutex;
117
- }
118
- tokens;
119
- mutex;
120
- kind = "pass-through";
121
- providerId = "claude";
122
- async applyHeaders(headers, _hints) {
123
- const token = await this.tokens.getValidClaudeAccessToken();
330
+ this.selector = selector;
331
+ this.health = health;
332
+ }
333
+
334
+
335
+
336
+
337
+ __init5() {this.kind = "pass-through"}
338
+ __init6() {this.providerId = "claude"}
339
+ async applyHeaders(headers, hints) {
340
+ const token = await resolveSelectedToken(
341
+ this.selector,
342
+ this.tokens,
343
+ "claude",
344
+ _optionalChain([hints, 'optionalAccess', _16 => _16.sessionKey]),
345
+ () => this.tokens.getValidClaudeAccessToken(),
346
+ { health: this.health, reportSelection: _optionalChain([hints, 'optionalAccess', _17 => _17.reportSelection]), resolvedModel: _optionalChain([hints, 'optionalAccess', _18 => _18.resolvedModel]) }
347
+ );
124
348
  if (!token) return;
125
349
  headers["Authorization"] = `Bearer ${token}`;
126
350
  }
127
- async onUnauthorized() {
351
+ async onUnauthorized(sessionKey) {
352
+ const byId = await refreshSelectedAccount(this.selector, this.tokens, this.mutex, "claude", sessionKey);
353
+ if (byId !== null) return byId;
128
354
  return this.mutex.run("claude:refresh", async () => {
129
355
  try {
130
356
  return await this.tokens.refreshClaudeToken();
@@ -137,7 +363,7 @@ var PassThroughAuthStrategy = class {
137
363
  async describeStatus() {
138
364
  const config = await this.tokens.getFullConfig();
139
365
  const claude = config.claude;
140
- if (!claude?.accessToken) {
366
+ if (!_optionalChain([claude, 'optionalAccess', _19 => _19.accessToken])) {
141
367
  return { providerId: "claude", ok: false, reason: "missing-credential" };
142
368
  }
143
369
  if (claude.status === "expired") {
@@ -150,11 +376,11 @@ var PassThroughAuthStrategy = class {
150
376
  }
151
377
  return { providerId: "claude", ok: true, expiresAt: claude.expiresAt };
152
378
  }
153
- };
379
+ }, _class3);
154
380
 
155
381
  // src/auth/RefreshMutex.ts
156
- var RefreshMutex = class {
157
- inflight = /* @__PURE__ */ new Map();
382
+ var RefreshMutex = (_class4 = class {constructor() { _class4.prototype.__init7.call(this); }
383
+ __init7() {this.inflight = /* @__PURE__ */ new Map()}
158
384
  /**
159
385
  * Run `task()` exclusively for `key`. If another caller is already running
160
386
  * for the same key, this call awaits the existing promise instead of
@@ -173,34 +399,45 @@ var RefreshMutex = class {
173
399
  this.inflight.set(key, promise);
174
400
  return promise;
175
401
  }
176
- };
402
+ }, _class4);
177
403
 
178
404
  // src/auth/StaticBearerAuthStrategy.ts
179
405
  var ANTHROPIC_SHAPE_PATH = "/v1/messages";
180
- var StaticBearerAuthStrategy = class {
181
- constructor(tokens) {
406
+ var StaticBearerAuthStrategy = (_class5 = class {
407
+ constructor(tokens, selector, health) {;_class5.prototype.__init8.call(this);_class5.prototype.__init9.call(this);
182
408
  this.tokens = tokens;
183
- }
184
- tokens;
185
- kind = "static-bearer";
186
- providerId = "opencodego";
409
+ this.selector = selector;
410
+ this.health = health;
411
+ }
412
+
413
+
414
+
415
+ __init8() {this.kind = "static-bearer"}
416
+ __init9() {this.providerId = "opencodego"}
187
417
  async applyHeaders(headers, hints) {
188
- const key = await this.tokens.getValidOpenCodeGoApiKey();
418
+ const key = await resolveSelectedToken(
419
+ this.selector,
420
+ this.tokens,
421
+ "opencodego",
422
+ _optionalChain([hints, 'optionalAccess', _20 => _20.sessionKey]),
423
+ () => this.tokens.getValidOpenCodeGoApiKey(),
424
+ { health: this.health, reportSelection: _optionalChain([hints, 'optionalAccess', _21 => _21.reportSelection]), resolvedModel: _optionalChain([hints, 'optionalAccess', _22 => _22.resolvedModel]) }
425
+ );
189
426
  if (!key) {
190
427
  return;
191
428
  }
192
429
  headers["Authorization"] = `Bearer ${key}`;
193
- if (hints?.upstreamUrl?.includes(ANTHROPIC_SHAPE_PATH)) {
430
+ if (_optionalChain([hints, 'optionalAccess', _23 => _23.upstreamUrl, 'optionalAccess', _24 => _24.includes, 'call', _25 => _25(ANTHROPIC_SHAPE_PATH)])) {
194
431
  headers["x-api-key"] = key;
195
432
  }
196
433
  }
197
- async onUnauthorized() {
434
+ async onUnauthorized(_sessionKey) {
198
435
  return false;
199
436
  }
200
437
  async describeStatus() {
201
438
  const config = await this.tokens.getFullConfig();
202
439
  const oc = config.opencodego;
203
- if (!oc?.apiKey) {
440
+ if (!_optionalChain([oc, 'optionalAccess', _26 => _26.apiKey])) {
204
441
  return { providerId: "opencodego", ok: false, reason: "missing-credential" };
205
442
  }
206
443
  if (oc.status === "error") {
@@ -208,7 +445,7 @@ var StaticBearerAuthStrategy = class {
208
445
  }
209
446
  return { providerId: "opencodego", ok: true };
210
447
  }
211
- };
448
+ }, _class5);
212
449
 
213
450
  // src/SubscriptionAccountService.ts
214
451
  var DISPLAY_NAMES = {
@@ -217,20 +454,24 @@ var DISPLAY_NAMES = {
217
454
  gemini: "Gemini (Google OAuth)",
218
455
  opencodego: "OpenCodeGo (Bearer key)"
219
456
  };
220
- var SubscriptionAccountService = class {
221
- mutex = new RefreshMutex();
222
- strategies;
223
- constructor(tokens) {
457
+ var SubscriptionAccountService = (_class6 = class {
458
+ __init10() {this.mutex = new RefreshMutex()}
459
+ /** ONE account-pool scheduler (subscription-account-scheduling) shared by all
460
+ * four strategies so they share the affinity map + the `lastUsedAt` overlay. */
461
+ __init11() {this.selector = new SubscriptionAccountSelector()}
462
+
463
+ constructor(tokens) {;_class6.prototype.__init10.call(this);_class6.prototype.__init11.call(this);
464
+ const health = _SubscriptionAccountHealth.getSharedAccountHealth.call(void 0, );
224
465
  this.strategies = /* @__PURE__ */ new Map([
225
- ["claude", new PassThroughAuthStrategy(tokens, this.mutex)],
226
- ["codex", new OAuthBearerAuthStrategy("codex", tokens, this.mutex)],
227
- ["gemini", new OAuthBearerAuthStrategy("gemini", tokens, this.mutex)],
228
- ["opencodego", new StaticBearerAuthStrategy(tokens)]
466
+ ["claude", new PassThroughAuthStrategy(tokens, this.mutex, this.selector, health)],
467
+ ["codex", new OAuthBearerAuthStrategy("codex", tokens, this.mutex, this.selector, health)],
468
+ ["gemini", new OAuthBearerAuthStrategy("gemini", tokens, this.mutex, this.selector, health)],
469
+ ["opencodego", new StaticBearerAuthStrategy(tokens, this.selector, health)]
229
470
  ]);
230
471
  }
231
472
  /** Returns the strategy bound to a subscription provider, or `null` for unknown ids. */
232
473
  getStrategy(providerId) {
233
- return this.strategies.get(providerId) ?? null;
474
+ return _nullishCoalesce(this.strategies.get(providerId), () => ( null));
234
475
  }
235
476
  /** Diagnostic for the `subscription:status` IPC. */
236
477
  async getStatus(providerId) {
@@ -253,7 +494,7 @@ var SubscriptionAccountService = class {
253
494
  }
254
495
  return entries;
255
496
  }
256
- };
497
+ }, _class6);
257
498
  var _moduleSingleton = null;
258
499
  function setSubscriptionAccountService(svc) {
259
500
  _moduleSingleton = svc;
@@ -262,48 +503,32 @@ function getSubscriptionAccountService() {
262
503
  return _moduleSingleton;
263
504
  }
264
505
 
265
- // ../core/src/outbound-api/subscriptionRegistryPort.ts
266
- var _registry = null;
267
- function setSubscriptionRegistryForOutbound(registry) {
268
- _registry = registry;
269
- }
506
+ // src/SubscriptionProviderRegistry.ts
270
507
 
271
- // ../core/src/transformer/transformers/GeminiCodeAssistTransformer.ts
272
- var DEFAULT_CODE_ASSIST_ENDPOINT = "https://cloudcode-pa.googleapis.com";
273
- var DEFAULT_CODE_ASSIST_API_VERSION = "v1internal";
274
- function resolveCodeAssistEndpoint() {
275
- return (process.env.CODE_ASSIST_ENDPOINT || DEFAULT_CODE_ASSIST_ENDPOINT).replace(/\/+$/, "");
276
- }
277
- function resolveCodeAssistApiVersion() {
278
- return process.env.CODE_ASSIST_API_VERSION || DEFAULT_CODE_ASSIST_API_VERSION;
279
- }
280
- function buildCodeAssistUrl(stream) {
281
- const base = resolveCodeAssistEndpoint();
282
- const version = resolveCodeAssistApiVersion();
283
- const method = stream ? "streamGenerateContent?alt=sse" : "generateContent";
284
- return `${base}/${version}:${method}`;
285
- }
508
+
509
+ var _subscriptionRegistryPort = require('@omnicross/core/outbound-api/subscriptionRegistryPort');
510
+ var _GeminiCodeAssistTransformer = require('@omnicross/core/transformer/transformers/GeminiCodeAssistTransformer');
286
511
 
287
512
  // src/opencodego/CircuitBreaker.ts
288
- var CircuitBreaker = class {
289
- state = "closed";
513
+ var CircuitBreaker = (_class7 = class {
514
+ __init12() {this.state = "closed"}
290
515
  /** CONSECUTIVE failures while closed (reset by any closed success). */
291
- failureCount = 0;
516
+ __init13() {this.failureCount = 0}
292
517
  /** Successes accumulated in the current half-open probe window. */
293
- successCount = 0;
518
+ __init14() {this.successCount = 0}
294
519
  /** Test calls admitted in the current half-open window (cap = halfOpenMaxCalls). */
295
- halfOpenCalls = 0;
520
+ __init15() {this.halfOpenCalls = 0}
296
521
  /** `now()` at the last recorded failure — drives the open→half-open elapsed check. */
297
- lastFailureTime = 0;
298
- threshold;
299
- openMs;
300
- halfOpenMaxCalls;
301
- now;
302
- constructor(opts = {}) {
303
- this.threshold = opts.threshold ?? 3;
304
- this.openMs = opts.openMs ?? 3e4;
305
- this.halfOpenMaxCalls = opts.halfOpenMaxCalls ?? 3;
306
- this.now = opts.now ?? Date.now;
522
+ __init16() {this.lastFailureTime = 0}
523
+
524
+
525
+
526
+
527
+ constructor(opts = {}) {;_class7.prototype.__init12.call(this);_class7.prototype.__init13.call(this);_class7.prototype.__init14.call(this);_class7.prototype.__init15.call(this);_class7.prototype.__init16.call(this);
528
+ this.threshold = _nullishCoalesce(opts.threshold, () => ( 3));
529
+ this.openMs = _nullishCoalesce(opts.openMs, () => ( 3e4));
530
+ this.halfOpenMaxCalls = _nullishCoalesce(opts.halfOpenMaxCalls, () => ( 3));
531
+ this.now = _nullishCoalesce(opts.now, () => ( Date.now));
307
532
  }
308
533
  /** Current state (diagnostics / tests). */
309
534
  getState() {
@@ -382,13 +607,13 @@ var CircuitBreaker = class {
382
607
  this.state = "open";
383
608
  }
384
609
  }
385
- };
386
- var CircuitBreakerRegistry = class {
387
- constructor(options = {}) {
610
+ }, _class7);
611
+ var CircuitBreakerRegistry = (_class8 = class {
612
+ constructor(options = {}) {;_class8.prototype.__init17.call(this);
388
613
  this.options = options;
389
614
  }
390
- options;
391
- breakers = /* @__PURE__ */ new Map();
615
+
616
+ __init17() {this.breakers = /* @__PURE__ */ new Map()}
392
617
  /** Get (or lazily create) the breaker for a model id. */
393
618
  get(modelId) {
394
619
  let breaker = this.breakers.get(modelId);
@@ -410,7 +635,7 @@ var CircuitBreakerRegistry = class {
410
635
  recordFailure(modelId) {
411
636
  this.get(modelId).recordFailure();
412
637
  }
413
- };
638
+ }, _class8);
414
639
 
415
640
  // src/opencodego/defaults.ts
416
641
  var DEFAULT_OPENCODEGO_LONG_CONTEXT_THRESHOLD = 8e4;
@@ -545,7 +770,7 @@ function classifyZenShape(modelId) {
545
770
  return "chat";
546
771
  }
547
772
  function resolveOpenCodeGoShape(entry) {
548
- const half = entry.provider ?? "go";
773
+ const half = _nullishCoalesce(entry.provider, () => ( "go"));
549
774
  if (half === "zen") return classifyZenShape(entry.modelId);
550
775
  const normalized = entry.modelId.toLowerCase();
551
776
  if (GO_ANTHROPIC_SHAPE_PREFIXES.some((p) => normalized.startsWith(p))) {
@@ -555,12 +780,12 @@ function resolveOpenCodeGoShape(entry) {
555
780
  }
556
781
  function resolveOpenCodeGoHalf(modelId, config) {
557
782
  if (!config) return "go";
558
- for (const entry of Object.values(config.modelMap ?? {})) {
559
- if (entry?.modelId === modelId) return entry.provider ?? "go";
783
+ for (const entry of Object.values(_nullishCoalesce(config.modelMap, () => ( {})))) {
784
+ if (_optionalChain([entry, 'optionalAccess', _27 => _27.modelId]) === modelId) return _nullishCoalesce(entry.provider, () => ( "go"));
560
785
  }
561
- for (const list of Object.values(config.fallbacks ?? {})) {
562
- for (const entry of list ?? []) {
563
- if (entry?.modelId === modelId) return entry.provider ?? "go";
786
+ for (const list of Object.values(_nullishCoalesce(config.fallbacks, () => ( {})))) {
787
+ for (const entry of _nullishCoalesce(list, () => ( []))) {
788
+ if (_optionalChain([entry, 'optionalAccess', _28 => _28.modelId]) === modelId) return _nullishCoalesce(entry.provider, () => ( "go"));
564
789
  }
565
790
  }
566
791
  return "go";
@@ -660,11 +885,11 @@ function hasBackgroundPattern(loweredSlices) {
660
885
  return containsAny(loweredSlices, BACKGROUND_KEYWORDS);
661
886
  }
662
887
  function resolveOpenCodeGoScenario(summary, config) {
663
- const longContextThreshold = config?.modelMap?.long_context?.contextThreshold ?? DEFAULT_OPENCODEGO_LONG_CONTEXT_THRESHOLD;
888
+ const longContextThreshold = _nullishCoalesce(_optionalChain([config, 'optionalAccess', _29 => _29.modelMap, 'optionalAccess', _30 => _30.long_context, 'optionalAccess', _31 => _31.contextThreshold]), () => ( DEFAULT_OPENCODEGO_LONG_CONTEXT_THRESHOLD));
664
889
  if (summary.estimatedInputTokens >= longContextThreshold) {
665
890
  return "long_context";
666
891
  }
667
- const rawSlices = summary.matchText ?? [];
892
+ const rawSlices = _nullishCoalesce(summary.matchText, () => ( []));
668
893
  const loweredSlices = toLowerSlices(summary.matchText);
669
894
  if (hasComplexPattern(loweredSlices)) return "complex";
670
895
  if (hasThinkingPattern(loweredSlices, rawSlices)) return "think";
@@ -692,8 +917,8 @@ function resolveOpenCodeGoTarget(modelId, config) {
692
917
  const shape = resolveOpenCodeGoShape({ provider: half, modelId });
693
918
  return { half, shape };
694
919
  }
695
- var SubscriptionProviderRegistry = class {
696
- constructor(accounts, tokens) {
920
+ var SubscriptionProviderRegistry = (_class9 = class {
921
+ constructor(accounts, tokens) {;_class9.prototype.__init18.call(this);
697
922
  this.accounts = accounts;
698
923
  this.tokens = tokens;
699
924
  const claude = this.accounts.getStrategy("claude");
@@ -765,7 +990,7 @@ var SubscriptionProviderRegistry = class {
765
990
  // (resolved once per account via `GeminiCodeAssistProjectResolver`).
766
991
  // `resolveUpstreamUrl` ignores the model (Code Assist has no per-model
767
992
  // path); the URL is the version-segment colon-method endpoint.
768
- resolveUpstreamUrl: (_model) => buildCodeAssistUrl(false),
993
+ resolveUpstreamUrl: (_model) => _GeminiCodeAssistTransformer.buildCodeAssistUrl.call(void 0, false),
769
994
  providerTransformerNames: ["gemini-code-assist"],
770
995
  modelTransformerNames: []
771
996
  }
@@ -790,7 +1015,7 @@ var SubscriptionProviderRegistry = class {
790
1015
  resolveUpstreamUrl: (model, config) => {
791
1016
  const oc = config;
792
1017
  const { half, shape } = resolveOpenCodeGoTarget(model, oc);
793
- const override = half === "zen" ? oc?.zenBaseUrl : oc?.baseUrl;
1018
+ const override = half === "zen" ? _optionalChain([oc, 'optionalAccess', _32 => _32.zenBaseUrl]) : _optionalChain([oc, 'optionalAccess', _33 => _33.baseUrl]);
794
1019
  return buildOpenCodeGoUrl(half, shape, override);
795
1020
  },
796
1021
  // zen seam (Decision 3): vary the provider transformer chain by resolved
@@ -808,7 +1033,7 @@ var SubscriptionProviderRegistry = class {
808
1033
  modelTransformerNames: [],
809
1034
  modelMapper: (sdkModel, summary, config) => {
810
1035
  const scenario = resolveOpenCodeGoScenario(summary, config);
811
- const entry = config?.modelMap?.[scenario] ?? config?.modelMap?.default ?? DEFAULT_OPENCODEGO_MODEL_MAP[scenario] ?? DEFAULT_OPENCODEGO_MODEL_MAP.default;
1036
+ const entry = _nullishCoalesce(_nullishCoalesce(_nullishCoalesce(_optionalChain([config, 'optionalAccess', _34 => _34.modelMap, 'optionalAccess', _35 => _35[scenario]]), () => ( _optionalChain([config, 'optionalAccess', _36 => _36.modelMap, 'optionalAccess', _37 => _37.default]))), () => ( DEFAULT_OPENCODEGO_MODEL_MAP[scenario])), () => ( DEFAULT_OPENCODEGO_MODEL_MAP.default));
812
1037
  if (!entry) {
813
1038
  return { resolvedModel: sdkModel, scenario };
814
1039
  }
@@ -827,7 +1052,7 @@ var SubscriptionProviderRegistry = class {
827
1052
  // wedge permanently in half-open). When NO circuit is open this returns
828
1053
  // the same first non-attempted entry as the prior `!attempted` filter.
829
1054
  nextFallback: (scenario, attempted, config) => {
830
- const list = config?.fallbacks?.[scenario] ?? DEFAULT_OPENCODEGO_FALLBACKS[scenario] ?? [];
1055
+ const list = _nullishCoalesce(_nullishCoalesce(_optionalChain([config, 'optionalAccess', _38 => _38.fallbacks, 'optionalAccess', _39 => _39[scenario]]), () => ( DEFAULT_OPENCODEGO_FALLBACKS[scenario])), () => ( []));
831
1056
  for (const entry of list) {
832
1057
  if (attempted.includes(entry.modelId)) continue;
833
1058
  if (this.breaker.allowRequest(entry.modelId)) return entry;
@@ -846,9 +1071,9 @@ var SubscriptionProviderRegistry = class {
846
1071
  ]
847
1072
  ]);
848
1073
  }
849
- accounts;
850
- tokens;
851
- profiles;
1074
+
1075
+
1076
+
852
1077
  /**
853
1078
  * Per-model circuit breaker for opencodego routing (D5). ONE registry-owned
854
1079
  * process singleton, built here and captured by the opencodego profile's
@@ -858,12 +1083,12 @@ var SubscriptionProviderRegistry = class {
858
1083
  * exactly the reference's long-lived `FallbackHandler`. Constructed with the
859
1084
  * default reference thresholds (3 / 30s / 3) and the default `Date.now` clock.
860
1085
  */
861
- breaker = new CircuitBreakerRegistry();
1086
+ __init18() {this.breaker = new CircuitBreakerRegistry()}
862
1087
  /** Returns the dispatch profile for a known subscription provider, or
863
1088
  * `null` for unknown ids (callers must treat null as "fall back to the
864
1089
  * legacy LLM provider DB lookup"). */
865
1090
  getProfile(providerId) {
866
- return this.profiles.get(providerId) ?? null;
1091
+ return _nullishCoalesce(this.profiles.get(providerId), () => ( null));
867
1092
  }
868
1093
  /** Read the currently-stored OpenCodeGo config so the proxy can pick up
869
1094
  * user overrides (modelMap / fallbacks / baseUrl). Wraps the injected
@@ -873,116 +1098,24 @@ var SubscriptionProviderRegistry = class {
873
1098
  const full = await this.tokens.getFullConfig();
874
1099
  return full.opencodego;
875
1100
  }
876
- };
1101
+ }, _class9);
877
1102
  var _moduleSingleton2 = null;
878
1103
  function setSubscriptionProviderRegistry(svc) {
879
1104
  _moduleSingleton2 = svc;
880
- setSubscriptionRegistryForOutbound(svc ?? null);
1105
+ _subscriptionRegistryPort.setSubscriptionRegistryForOutbound.call(void 0, _nullishCoalesce(svc, () => ( null)));
881
1106
  }
882
1107
  function getSubscriptionProviderRegistry() {
883
1108
  return _moduleSingleton2;
884
1109
  }
885
1110
 
886
- // ../core/src/ports/gemini-code-assist-resolver.ts
887
- var resolver = null;
888
- function getGeminiCodeAssistResolver() {
889
- return resolver;
890
- }
1111
+ // src/SubscriptionDispatcher.ts
1112
+ var _geminicodeassistresolver = require('@omnicross/core/ports/gemini-code-assist-resolver');
1113
+
891
1114
 
892
- // ../core/src/provider-proxy/matchText.ts
893
- var MATCH_TEXT_PER_MESSAGE_CAP = 8192;
894
- var MATCH_TEXT_RECENT_MESSAGES = 6;
895
- function flattenMatchText(value) {
896
- if (typeof value === "string") return value;
897
- if (Array.isArray(value)) {
898
- const parts = [];
899
- for (const item of value) {
900
- const text = flattenMatchText(item);
901
- if (text) parts.push(text);
902
- }
903
- return parts.join("\n");
904
- }
905
- if (value && typeof value === "object") {
906
- const obj = value;
907
- if (obj.type === "tool_result" && obj.content !== void 0) {
908
- return flattenMatchText(obj.content);
909
- }
910
- if (typeof obj.text === "string") return obj.text;
911
- }
912
- return "";
913
- }
914
- function collectMatchText(anthropicBody) {
915
- const messages = Array.isArray(anthropicBody.messages) ? anthropicBody.messages : [];
916
- const slices = [];
917
- const sys = flattenMatchText(anthropicBody.system).trim();
918
- if (sys) slices.push(sys.slice(0, MATCH_TEXT_PER_MESSAGE_CAP));
919
- const recent = [];
920
- for (let i = messages.length - 1; i >= 0 && recent.length < MATCH_TEXT_RECENT_MESSAGES; i--) {
921
- const message = messages[i];
922
- if (!message || typeof message !== "object") continue;
923
- const role = message.role;
924
- if (role !== "user" && role !== "system") continue;
925
- const text = flattenMatchText(message.content).trim();
926
- if (text) recent.push(text.slice(0, MATCH_TEXT_PER_MESSAGE_CAP));
927
- }
928
- for (let i = recent.length - 1; i >= 0; i--) slices.push(recent[i]);
929
- return slices;
930
- }
931
1115
 
932
- // ../core/src/serializeError.ts
933
- function serializeError(err) {
934
- if (err == null) return "Unknown error (null)";
935
- if (err instanceof Error) {
936
- let msg = err.message || err.name || "Error";
937
- if (err.cause) {
938
- msg += ` [cause: ${serializeError(err.cause)}]`;
939
- }
940
- const anyErr = err;
941
- if (anyErr.status != null) msg += ` (status: ${anyErr.status})`;
942
- else if (anyErr.code != null) msg += ` (code: ${anyErr.code})`;
943
- return msg;
944
- }
945
- if (typeof err === "string") return err || "Empty error string";
946
- if (typeof err !== "object") return String(err);
947
- const obj = err;
948
- if (typeof obj.message === "string" && obj.message) {
949
- let msg = obj.message;
950
- if (obj.status != null) msg += ` (status: ${obj.status})`;
951
- else if (obj.code != null) msg += ` (code: ${obj.code})`;
952
- if (typeof obj.type === "string") msg += ` [type: ${obj.type}]`;
953
- return msg;
954
- }
955
- if (typeof obj.error === "string" && obj.error) {
956
- return obj.error;
957
- }
958
- if (obj.error && typeof obj.error === "object") {
959
- const inner = obj.error;
960
- if (typeof inner.message === "string" && inner.message) {
961
- let msg = inner.message;
962
- if (typeof inner.type === "string") msg += ` [type: ${inner.type}]`;
963
- return msg;
964
- }
965
- }
966
- try {
967
- const json = JSON.stringify(err, getCircularReplacer(), 2);
968
- if (json && json.length > 1e3) {
969
- return json.slice(0, 1e3) + "... (truncated)";
970
- }
971
- return json || "Unserializable error";
972
- } catch {
973
- return `Unserializable error: ${Object.prototype.toString.call(err)}`;
974
- }
975
- }
976
- function getCircularReplacer() {
977
- const seen = /* @__PURE__ */ new WeakSet();
978
- return (_key, value) => {
979
- if (typeof value === "object" && value !== null) {
980
- if (seen.has(value)) return "[Circular]";
981
- seen.add(value);
982
- }
983
- return value;
984
- };
985
- }
1116
+
1117
+ var _matchText = require('@omnicross/core/provider-proxy/matchText');
1118
+ var _serializeError = require('@omnicross/core/serializeError');
986
1119
 
987
1120
  // src/opencodego/token-count.ts
988
1121
  var cachedEncode = null;
@@ -1001,14 +1134,15 @@ var SubscriptionDispatcher = class {
1001
1134
  this.hooks = hooks;
1002
1135
  this.getOpenCodeGoConfig = getOpenCodeGoConfig;
1003
1136
  }
1004
- profile;
1005
- hooks;
1006
- getOpenCodeGoConfig;
1137
+
1138
+
1139
+
1007
1140
  /**
1008
1141
  * Entry point — called by the host proxy's request handler after model
1009
1142
  * resolution and probe-detection.
1010
1143
  */
1011
1144
  async dispatch(req) {
1145
+ const sessionKey = _matchText.deriveSubscriptionSessionKey.call(void 0, req.anthropicBody);
1012
1146
  const ocConfig = this.profile.providerId === "opencodego" ? await this.getOpenCodeGoConfig() : void 0;
1013
1147
  let scenario = "default";
1014
1148
  let resolvedModel = req.fallbackModel;
@@ -1019,7 +1153,7 @@ var SubscriptionDispatcher = class {
1019
1153
  scenario = mapped.scenario;
1020
1154
  req.anthropicBody.model = resolvedModel;
1021
1155
  }
1022
- const upstreamUrl = this.profile.resolveUpstreamUrl?.(resolvedModel, ocConfig);
1156
+ const upstreamUrl = _optionalChain([this, 'access', _40 => _40.profile, 'access', _41 => _41.resolveUpstreamUrl, 'optionalCall', _42 => _42(resolvedModel, ocConfig)]);
1023
1157
  if (!upstreamUrl) {
1024
1158
  throw new Error(`[SubscriptionDispatcher] profile=${this.profile.providerId} missing resolveUpstreamUrl`);
1025
1159
  }
@@ -1027,55 +1161,67 @@ var SubscriptionDispatcher = class {
1027
1161
  provider: resolveOpenCodeGoHalf(resolvedModel, ocConfig),
1028
1162
  modelId: resolvedModel
1029
1163
  }) === "anthropic") {
1030
- await this.dispatchAnthropicShapeBypass(req, upstreamUrl, resolvedModel, scenario, ocConfig);
1164
+ await this.dispatchAnthropicShapeBypass(req, upstreamUrl, resolvedModel, scenario, ocConfig, sessionKey);
1031
1165
  return;
1032
1166
  }
1033
- await this.dispatchTransformerChain(req, upstreamUrl, resolvedModel, scenario, ocConfig);
1167
+ await this.dispatchTransformerChain(req, upstreamUrl, resolvedModel, scenario, ocConfig, sessionKey);
1034
1168
  }
1035
1169
  /** Bypass path for OpenCodeGo MiniMax models — forwards Anthropic body verbatim. */
1036
- async dispatchAnthropicShapeBypass(req, upstreamUrl, resolvedModel, scenario, ocConfig) {
1170
+ async dispatchAnthropicShapeBypass(req, upstreamUrl, resolvedModel, scenario, ocConfig, sessionKey) {
1037
1171
  const gate = this.gatePrimaryModel(resolvedModel, scenario, ocConfig);
1038
1172
  const attempted = gate.attempted;
1039
1173
  let currentModel = gate.firstModel;
1174
+ let usedAccountId;
1175
+ const reportSelection = (accountId) => {
1176
+ usedAccountId = accountId;
1177
+ };
1040
1178
  while (attempted.length < MAX_FALLBACK_ATTEMPTS_LOCAL) {
1041
1179
  attempted.push(currentModel);
1042
1180
  req.anthropicBody.model = currentModel;
1043
1181
  const headers = { "content-type": "application/json" };
1044
- await this.applyHeadersWithRetry(headers, { upstreamUrl, resolvedModel: currentModel });
1182
+ await this.applyHeadersWithRetry(headers, { upstreamUrl, resolvedModel: currentModel, sessionKey, reportSelection });
1045
1183
  console.info(
1046
1184
  `[AgentProxy:subscription] REQ#${req.reqId} | opencodego anthropic-shape -> ${upstreamUrl} model=${currentModel} attempt=${attempted.length}`
1047
1185
  );
1048
1186
  try {
1049
1187
  const upstream = await this.hooks.fetchWithRetry(upstreamUrl, headers, req.anthropicBody, currentModel);
1050
- this.profile.recordModelOutcome?.(currentModel, true);
1188
+ _optionalChain([this, 'access', _43 => _43.profile, 'access', _44 => _44.recordModelOutcome, 'optionalCall', _45 => _45(currentModel, true)]);
1189
+ this.markHealth(usedAccountId, 200);
1051
1190
  await this.hooks.writeProxyResponse(req.res, upstream, req.isStream, req.reqId);
1052
1191
  return;
1053
1192
  } catch (err) {
1054
- const handled = await this.maybeRetryAfterError(err, headers, req, currentModel);
1193
+ const handled = await this.maybeRetryAfterError(err, headers, req, currentModel, sessionKey);
1055
1194
  if (handled.retryOnce) {
1056
- const upstream = await this.hooks.fetchWithRetry(upstreamUrl, handled.headers, req.anthropicBody, currentModel);
1057
- this.profile.recordModelOutcome?.(currentModel, true);
1058
- await this.hooks.writeProxyResponse(req.res, upstream, req.isStream, req.reqId);
1059
- return;
1195
+ try {
1196
+ const upstream = await this.hooks.fetchWithRetry(upstreamUrl, handled.headers, req.anthropicBody, currentModel);
1197
+ _optionalChain([this, 'access', _46 => _46.profile, 'access', _47 => _47.recordModelOutcome, 'optionalCall', _48 => _48(currentModel, true)]);
1198
+ this.markHealth(usedAccountId, 200);
1199
+ await this.hooks.writeProxyResponse(req.res, upstream, req.isStream, req.reqId);
1200
+ return;
1201
+ } catch (retryErr) {
1202
+ this.markHealth(usedAccountId, errStatus(retryErr), retryErr);
1203
+ throw retryErr;
1204
+ }
1060
1205
  }
1061
1206
  if (caughtErrorBreakerOutcome(err) === "failure") {
1062
- this.profile.recordModelOutcome?.(currentModel, false);
1207
+ _optionalChain([this, 'access', _49 => _49.profile, 'access', _50 => _50.recordModelOutcome, 'optionalCall', _51 => _51(currentModel, false)]);
1063
1208
  }
1064
- const next = this.profile.nextFallback?.(scenario, attempted, ocConfig);
1209
+ this.markHealth(usedAccountId, errStatus(err), err);
1210
+ const next = _optionalChain([this, 'access', _52 => _52.profile, 'access', _53 => _53.nextFallback, 'optionalCall', _54 => _54(scenario, attempted, ocConfig)]);
1065
1211
  if (!next || attempted.length >= MAX_FALLBACK_ATTEMPTS_LOCAL) {
1066
1212
  throw err;
1067
1213
  }
1068
1214
  console.warn(
1069
1215
  `[AgentProxy:subscription] REQ#${req.reqId} | opencodego fallback ${currentModel} -> ${next.modelId} after error:`,
1070
- serializeError(err)
1216
+ _serializeError.serializeError.call(void 0, err)
1071
1217
  );
1072
1218
  currentModel = next.modelId;
1073
1219
  }
1074
1220
  }
1075
1221
  }
1076
1222
  /** Standard subscription transformer chain — Codex/Gemini/OpenCodeGo OpenAI-shape. */
1077
- async dispatchTransformerChain(req, upstreamUrl, resolvedModel, scenario, ocConfig) {
1078
- const providerNames = this.profile.resolveProviderTransformerNames?.(resolvedModel, ocConfig) ?? this.profile.providerTransformerNames;
1223
+ async dispatchTransformerChain(req, upstreamUrl, resolvedModel, scenario, ocConfig, sessionKey) {
1224
+ const providerNames = _nullishCoalesce(_optionalChain([this, 'access', _55 => _55.profile, 'access', _56 => _56.resolveProviderTransformerNames, 'optionalCall', _57 => _57(resolvedModel, ocConfig)]), () => ( this.profile.providerTransformerNames));
1079
1225
  const providerTransformers = this.resolveTransformers(providerNames);
1080
1226
  const modelTransformers = this.resolveTransformers(this.profile.modelTransformerNames);
1081
1227
  const transformerProvider = {
@@ -1091,6 +1237,10 @@ var SubscriptionDispatcher = class {
1091
1237
  const gate = this.gatePrimaryModel(resolvedModel, scenario, ocConfig);
1092
1238
  const attempted = gate.attempted;
1093
1239
  let currentModel = gate.firstModel;
1240
+ let usedAccountId;
1241
+ const reportSelection = (accountId) => {
1242
+ usedAccountId = accountId;
1243
+ };
1094
1244
  while (attempted.length < MAX_FALLBACK_ATTEMPTS_LOCAL) {
1095
1245
  attempted.push(currentModel);
1096
1246
  req.anthropicBody.model = currentModel;
@@ -1105,14 +1255,15 @@ var SubscriptionDispatcher = class {
1105
1255
  ...config.headers
1106
1256
  };
1107
1257
  stripAuthHeaders(headers);
1108
- await this.applyHeadersWithRetry(headers, { upstreamUrl, resolvedModel: currentModel });
1109
- const fetchUrl = usesResponsesChain(providerNames) ? upstreamUrl : resolveConfigUrl(config.url) ?? upstreamUrl;
1258
+ await this.applyHeadersWithRetry(headers, { upstreamUrl, resolvedModel: currentModel, sessionKey, reportSelection });
1259
+ const fetchUrl = usesResponsesChain(providerNames) ? upstreamUrl : _nullishCoalesce(resolveConfigUrl(config.url), () => ( upstreamUrl));
1110
1260
  console.info(
1111
1261
  `[AgentProxy:subscription] REQ#${req.reqId} | provider=${this.profile.providerId} -> ${fetchUrl} model=${currentModel} attempt=${attempted.length}`
1112
1262
  );
1113
1263
  try {
1114
1264
  const upstream = await this.hooks.fetchWithRetry(fetchUrl, headers, requestBody, currentModel);
1115
- this.profile.recordModelOutcome?.(currentModel, true);
1265
+ _optionalChain([this, 'access', _58 => _58.profile, 'access', _59 => _59.recordModelOutcome, 'optionalCall', _60 => _60(currentModel, true)]);
1266
+ this.markHealth(usedAccountId, 200);
1116
1267
  const finalResponse = await this.hooks.executor.executeResponseChain(
1117
1268
  requestBody,
1118
1269
  upstream,
@@ -1123,30 +1274,37 @@ var SubscriptionDispatcher = class {
1123
1274
  await this.hooks.writeProxyResponse(req.res, finalResponse, req.isStream, req.reqId);
1124
1275
  return;
1125
1276
  } catch (err) {
1126
- const handled = await this.maybeRetryAfterError(err, headers, req, currentModel);
1277
+ const handled = await this.maybeRetryAfterError(err, headers, req, currentModel, sessionKey);
1127
1278
  if (handled.retryOnce) {
1128
- const upstream = await this.hooks.fetchWithRetry(fetchUrl, handled.headers, requestBody, currentModel);
1129
- this.profile.recordModelOutcome?.(currentModel, true);
1130
- const finalResponse = await this.hooks.executor.executeResponseChain(
1131
- requestBody,
1132
- upstream,
1133
- transformerProvider,
1134
- { providerTransformers, modelTransformers },
1135
- { endpointTransformer: this.hooks.endpointTransformer }
1136
- );
1137
- await this.hooks.writeProxyResponse(req.res, finalResponse, req.isStream, req.reqId);
1138
- return;
1279
+ try {
1280
+ const upstream = await this.hooks.fetchWithRetry(fetchUrl, handled.headers, requestBody, currentModel);
1281
+ _optionalChain([this, 'access', _61 => _61.profile, 'access', _62 => _62.recordModelOutcome, 'optionalCall', _63 => _63(currentModel, true)]);
1282
+ this.markHealth(usedAccountId, 200);
1283
+ const finalResponse = await this.hooks.executor.executeResponseChain(
1284
+ requestBody,
1285
+ upstream,
1286
+ transformerProvider,
1287
+ { providerTransformers, modelTransformers },
1288
+ { endpointTransformer: this.hooks.endpointTransformer }
1289
+ );
1290
+ await this.hooks.writeProxyResponse(req.res, finalResponse, req.isStream, req.reqId);
1291
+ return;
1292
+ } catch (retryErr) {
1293
+ this.markHealth(usedAccountId, errStatus(retryErr), retryErr);
1294
+ throw retryErr;
1295
+ }
1139
1296
  }
1140
1297
  if (caughtErrorBreakerOutcome(err) === "failure") {
1141
- this.profile.recordModelOutcome?.(currentModel, false);
1298
+ _optionalChain([this, 'access', _64 => _64.profile, 'access', _65 => _65.recordModelOutcome, 'optionalCall', _66 => _66(currentModel, false)]);
1142
1299
  }
1143
- const next = this.profile.nextFallback?.(scenario, attempted, ocConfig);
1300
+ this.markHealth(usedAccountId, errStatus(err), err);
1301
+ const next = _optionalChain([this, 'access', _67 => _67.profile, 'access', _68 => _68.nextFallback, 'optionalCall', _69 => _69(scenario, attempted, ocConfig)]);
1144
1302
  if (!next || attempted.length >= MAX_FALLBACK_ATTEMPTS_LOCAL) {
1145
1303
  throw err;
1146
1304
  }
1147
1305
  console.warn(
1148
1306
  `[AgentProxy:subscription] REQ#${req.reqId} | ${this.profile.providerId} fallback ${currentModel} -> ${next.modelId} after error:`,
1149
- serializeError(err)
1307
+ _serializeError.serializeError.call(void 0, err)
1150
1308
  );
1151
1309
  currentModel = next.modelId;
1152
1310
  }
@@ -1170,7 +1328,7 @@ var SubscriptionDispatcher = class {
1170
1328
  return { firstModel: primaryModel, attempted: [] };
1171
1329
  }
1172
1330
  const skipped = [primaryModel];
1173
- const firstAdmitting = this.profile.nextFallback?.(scenario, skipped, ocConfig);
1331
+ const firstAdmitting = _optionalChain([this, 'access', _70 => _70.profile, 'access', _71 => _71.nextFallback, 'optionalCall', _72 => _72(scenario, skipped, ocConfig)]);
1174
1332
  if (firstAdmitting) {
1175
1333
  console.warn(
1176
1334
  `[AgentProxy:subscription] opencodego primary ${primaryModel} circuit open -> first admitting fallback ${firstAdmitting.modelId}`
@@ -1187,12 +1345,12 @@ var SubscriptionDispatcher = class {
1187
1345
  * Returns `{ retryOnce: true, headers }` when the strategy refreshed
1188
1346
  * successfully (caller should retry once); otherwise re-throws.
1189
1347
  */
1190
- async maybeRetryAfterError(err, headers, req, resolvedModel) {
1191
- const status = err?.status ?? 0;
1348
+ async maybeRetryAfterError(err, headers, req, resolvedModel, sessionKey) {
1349
+ const status = _nullishCoalesce(_optionalChain([err, 'optionalAccess', _73 => _73.status]), () => ( 0));
1192
1350
  if (status !== 401) {
1193
1351
  return { retryOnce: false, headers };
1194
1352
  }
1195
- const refreshed = await this.profile.authStrategy.onUnauthorized();
1353
+ const refreshed = await this.profile.authStrategy.onUnauthorized(sessionKey);
1196
1354
  if (!refreshed) {
1197
1355
  console.warn(
1198
1356
  `[AgentProxy:subscription] REQ#${req.reqId} | 401 not recoverable for provider=${this.profile.providerId}`
@@ -1201,16 +1359,38 @@ var SubscriptionDispatcher = class {
1201
1359
  }
1202
1360
  const fresh = { ...headers };
1203
1361
  stripAuthHeaders(fresh);
1204
- await this.applyHeadersWithRetry(fresh, { upstreamUrl: "", resolvedModel });
1362
+ await this.applyHeadersWithRetry(fresh, { upstreamUrl: "", resolvedModel, sessionKey });
1205
1363
  return { retryOnce: true, headers: fresh };
1206
1364
  }
1207
1365
  async applyHeadersWithRetry(headers, hints) {
1208
1366
  try {
1209
1367
  await this.profile.authStrategy.applyHeaders(headers, hints);
1210
1368
  } catch (err) {
1211
- console.warn("[AgentProxy:subscription] authStrategy.applyHeaders threw:", serializeError(err));
1369
+ console.warn("[AgentProxy:subscription] authStrategy.applyHeaders threw:", _serializeError.serializeError.call(void 0, err));
1212
1370
  }
1213
1371
  }
1372
+ /**
1373
+ * Mark the served account's health against ONE attempt's outcome
1374
+ * (subscription-account-health, task 3.4). No-op when no account was reported
1375
+ * (non-pooled / single-account) or on a session-cancel (status 0). On a caught
1376
+ * error `err` is passed so the 429-reset / 403-ban drivers are read STRUCTURALLY
1377
+ * from the error's upstream `headers` + `bodyText` (the `fetchWithRetry`
1378
+ * contract) — so daemon-path 429 cooldown + ban blocking function for
1379
+ * multi-account codex/gemini/opencodego pools; absent headers ⇒ a bare-429
1380
+ * (unmarked, lazy re-probe). Success (2xx) clears; 401/5xx/thrown → transient.
1381
+ */
1382
+ markHealth(accountId, status, err) {
1383
+ if (accountId === void 0 || status === 0) return;
1384
+ const headers = err !== void 0 ? errHeaders(err) : void 0;
1385
+ const reset = headers ? _SubscriptionAccountHealth.resolveResetSeconds.call(void 0, this.profile.providerId, headers) : { resetHeaderSeconds: null, retryAfterSeconds: null };
1386
+ const bodyText = status === 403 && err !== void 0 ? errBodyText(err) : void 0;
1387
+ _SubscriptionAccountHealth.getSharedAccountHealth.call(void 0, ).recordUpstreamOutcome(this.profile.providerId, accountId, {
1388
+ status,
1389
+ resetHeaderSeconds: reset.resetHeaderSeconds,
1390
+ retryAfterSeconds: reset.retryAfterSeconds,
1391
+ bodyText
1392
+ });
1393
+ }
1214
1394
  /**
1215
1395
  * Resolve the Code Assist project for the gemini subscription profile. Pulls
1216
1396
  * the Bearer the bound `AuthStrategy` would inject (so the strategy stays the
@@ -1222,12 +1402,12 @@ var SubscriptionDispatcher = class {
1222
1402
  async resolveGeminiProject() {
1223
1403
  const probe = {};
1224
1404
  await this.applyHeadersWithRetry(probe, { upstreamUrl: "", resolvedModel: "" });
1225
- const bearer = probe.Authorization ?? probe.authorization ?? "";
1405
+ const bearer = _nullishCoalesce(_nullishCoalesce(probe.Authorization, () => ( probe.authorization)), () => ( ""));
1226
1406
  const accessToken = bearer.replace(/^Bearer\s+/i, "").trim();
1227
1407
  if (!accessToken) return void 0;
1228
- const resolver2 = getGeminiCodeAssistResolver();
1229
- if (!resolver2) return void 0;
1230
- return resolver2.resolveProject(accessToken);
1408
+ const resolver = _geminicodeassistresolver.getGeminiCodeAssistResolver.call(void 0, );
1409
+ if (!resolver) return void 0;
1410
+ return resolver.resolveProject(accessToken);
1231
1411
  }
1232
1412
  resolveTransformers(names) {
1233
1413
  if (!names || names.length === 0) return [];
@@ -1253,7 +1433,7 @@ var SubscriptionDispatcher = class {
1253
1433
  } else if (Array.isArray(system)) {
1254
1434
  for (const block of system) {
1255
1435
  if (block && typeof block === "object" && "text" in block) {
1256
- totalChars += String(block.text ?? "").length;
1436
+ totalChars += String(_nullishCoalesce(block.text, () => ( ""))).length;
1257
1437
  }
1258
1438
  }
1259
1439
  }
@@ -1282,13 +1462,29 @@ var SubscriptionDispatcher = class {
1282
1462
  // and the core `/v1/messages` path produce IDENTICAL `matchText` for the
1283
1463
  // same body — equivalence by construction. `@omnicross/subscriptions` →
1284
1464
  // `@omnicross/core` is the allowed direction; core imports nothing back.
1285
- matchText: collectMatchText(anthropicBody)
1465
+ matchText: _matchText.collectMatchText.call(void 0, anthropicBody)
1286
1466
  };
1287
1467
  }
1288
1468
  };
1289
1469
  var MAX_FALLBACK_ATTEMPTS_LOCAL = 3;
1470
+ function errStatus(err) {
1471
+ const status = _optionalChain([err, 'optionalAccess', _74 => _74.status]);
1472
+ return typeof status === "number" ? status : null;
1473
+ }
1474
+ function errHeaders(err) {
1475
+ const h = _optionalChain([err, 'optionalAccess', _75 => _75.headers]);
1476
+ if (!h) return void 0;
1477
+ if (typeof h.get === "function") return h;
1478
+ if (typeof h === "object") return h;
1479
+ return void 0;
1480
+ }
1481
+ function errBodyText(err) {
1482
+ const e = err;
1483
+ const raw = typeof _optionalChain([e, 'optionalAccess', _76 => _76.bodyText]) === "string" ? e.bodyText : typeof _optionalChain([e, 'optionalAccess', _77 => _77.body]) === "string" ? e.body : void 0;
1484
+ return _optionalChain([raw, 'optionalAccess', _78 => _78.slice, 'call', _79 => _79(0, 2048)]);
1485
+ }
1290
1486
  function caughtErrorBreakerOutcome(err) {
1291
- const status = err?.status;
1487
+ const status = _optionalChain([err, 'optionalAccess', _80 => _80.status]);
1292
1488
  if (typeof status !== "number") return "failure";
1293
1489
  if (status === 0) return "neutral";
1294
1490
  if (status >= 500 || status === 429) return "failure";
@@ -1312,368 +1508,18 @@ function stripAuthHeaders(headers) {
1312
1508
  delete headers["X-Goog-Api-Key"];
1313
1509
  }
1314
1510
 
1315
- // src/oauth/flows/claude.ts
1316
- var claude_exports = {};
1317
- __export(claude_exports, {
1318
- exchangeCodeForTokens: () => exchangeCodeForTokens,
1319
- exchangeSetupTokenCode: () => exchangeSetupTokenCode,
1320
- generateAuthParams: () => generateAuthParams,
1321
- generateSetupTokenParams: () => generateSetupTokenParams,
1322
- refreshAccessToken: () => refreshAccessToken
1323
- });
1324
- var import_node_crypto = __toESM(require("crypto"), 1);
1325
1511
 
1326
- // src/oauth/fetchPort.ts
1327
- function errorMessage(error, errorDescription) {
1328
- if (errorDescription) return errorDescription;
1329
- if (typeof error === "string") return error;
1330
- if (error && typeof error === "object") {
1331
- const e = error;
1332
- if (typeof e.message === "string" && e.message) return e.message;
1333
- if (typeof e.error_description === "string" && e.error_description) {
1334
- return e.error_description;
1335
- }
1336
- return JSON.stringify(error);
1337
- }
1338
- return String(error);
1339
- }
1340
- async function postForm(fetchImpl, url, params, parseErrorMessage) {
1341
- const response = await fetchImpl(url, {
1342
- method: "POST",
1343
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
1344
- body: params.toString()
1345
- });
1346
- const responseData = await response.text();
1347
- let data;
1348
- try {
1349
- data = JSON.parse(responseData);
1350
- } catch {
1351
- throw new Error(parseErrorMessage);
1352
- }
1353
- if (data.error) {
1354
- throw new Error(errorMessage(data.error, data.error_description));
1355
- }
1356
- return data;
1357
- }
1358
- async function postJson(fetchImpl, url, body, parseErrorMessage, extraHeaders = {}) {
1359
- const response = await fetchImpl(url, {
1360
- method: "POST",
1361
- headers: { "Content-Type": "application/json", ...extraHeaders },
1362
- body: JSON.stringify(body)
1363
- });
1364
- const responseData = await response.text();
1365
- let data;
1366
- try {
1367
- data = JSON.parse(responseData);
1368
- } catch {
1369
- throw new Error(parseErrorMessage);
1370
- }
1371
- if (data.error) {
1372
- throw new Error(errorMessage(data.error, data.error_description));
1373
- }
1374
- return data;
1375
- }
1376
1512
 
1377
- // src/oauth/flows/claude.ts
1378
- var CLAUDE_TOKEN_HEADERS = {
1379
- "User-Agent": "claude-cli/1.0.56 (external, cli)",
1380
- Accept: "application/json, text/plain, */*",
1381
- "Accept-Language": "en-US,en;q=0.9",
1382
- Referer: "https://claude.ai/",
1383
- Origin: "https://claude.ai"
1384
- };
1385
- var CLAUDE_OAUTH_CONFIG = {
1386
- clientId: "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
1387
- authorizationEndpoint: "https://claude.ai/oauth/authorize",
1388
- // The token endpoint stays on console.anthropic.com (still the live value —
1389
- // matches the official Claude Code CLI / claude-relay-service reference); only
1390
- // the OAuth callback moved to platform.claude.com (2026). The redirect_uri MUST
1391
- // match what the client is registered for AND match between authorize + token
1392
- // exchange. Scopes mirror the live Claude Code authorize URL.
1393
- tokenEndpoint: "https://console.anthropic.com/v1/oauth/token",
1394
- redirectUri: "https://platform.claude.com/oauth/code/callback",
1395
- scopes: [
1396
- "org:create_api_key",
1397
- "user:profile",
1398
- "user:inference",
1399
- "user:sessions:claude_code",
1400
- "user:mcp_servers",
1401
- "user:file_upload"
1402
- ]
1403
- };
1404
- var SETUP_TOKEN_CONFIG = {
1405
- scopes: ["user:inference"]
1406
- // Only inference permission, no API key creation
1407
- };
1408
- function generatePkce() {
1409
- const codeVerifier = import_node_crypto.default.randomBytes(32).toString("base64url");
1410
- const codeChallenge = import_node_crypto.default.createHash("sha256").update(codeVerifier).digest("base64url");
1411
- const state = import_node_crypto.default.randomBytes(16).toString("hex");
1412
- return { codeVerifier, codeChallenge, state };
1413
- }
1414
- function generateAuthParams() {
1415
- const { codeVerifier, codeChallenge, state } = generatePkce();
1416
- const params = new URLSearchParams({
1417
- code: "true",
1418
- client_id: CLAUDE_OAUTH_CONFIG.clientId,
1419
- response_type: "code",
1420
- redirect_uri: CLAUDE_OAUTH_CONFIG.redirectUri,
1421
- scope: CLAUDE_OAUTH_CONFIG.scopes.join(" "),
1422
- code_challenge: codeChallenge,
1423
- code_challenge_method: "S256",
1424
- state
1425
- });
1426
- const authUrl = `${CLAUDE_OAUTH_CONFIG.authorizationEndpoint}?${params.toString()}`;
1427
- return { authUrl, codeVerifier, state };
1428
- }
1429
- function generateSetupTokenParams() {
1430
- const { codeVerifier, codeChallenge, state } = generatePkce();
1431
- const params = new URLSearchParams({
1432
- code: "true",
1433
- client_id: CLAUDE_OAUTH_CONFIG.clientId,
1434
- response_type: "code",
1435
- redirect_uri: CLAUDE_OAUTH_CONFIG.redirectUri,
1436
- scope: SETUP_TOKEN_CONFIG.scopes.join(" "),
1437
- code_challenge: codeChallenge,
1438
- code_challenge_method: "S256",
1439
- state
1440
- });
1441
- const authUrl = `${CLAUDE_OAUTH_CONFIG.authorizationEndpoint}?${params.toString()}`;
1442
- return { authUrl, codeVerifier, state };
1443
- }
1444
- async function exchangeCodeForTokens(request, fetchImpl) {
1445
- const { authorizationCode, codeVerifier, state } = request;
1446
- const code = authorizationCode.split("#")[0]?.split("&")[0] ?? authorizationCode;
1447
- const data = await postJson(
1448
- fetchImpl,
1449
- CLAUDE_OAUTH_CONFIG.tokenEndpoint,
1450
- {
1451
- grant_type: "authorization_code",
1452
- client_id: CLAUDE_OAUTH_CONFIG.clientId,
1453
- code,
1454
- code_verifier: codeVerifier,
1455
- redirect_uri: CLAUDE_OAUTH_CONFIG.redirectUri,
1456
- state
1457
- },
1458
- "Failed to parse token response",
1459
- CLAUDE_TOKEN_HEADERS
1460
- );
1461
- return {
1462
- accessToken: data.access_token,
1463
- // The authorization_code grant always returns a refresh_token; the original
1464
- // helper read it from an untyped `data` and declared the field `string`.
1465
- refreshToken: data.refresh_token,
1466
- expiresIn: data.expires_in,
1467
- scopes: data.scope?.split(" ") || CLAUDE_OAUTH_CONFIG.scopes
1468
- };
1469
- }
1470
- async function exchangeSetupTokenCode(request, fetchImpl) {
1471
- const { authorizationCode, codeVerifier, state } = request;
1472
- const code = authorizationCode.split("#")[0]?.split("&")[0] ?? authorizationCode;
1473
- const data = await postJson(
1474
- fetchImpl,
1475
- CLAUDE_OAUTH_CONFIG.tokenEndpoint,
1476
- {
1477
- grant_type: "authorization_code",
1478
- client_id: CLAUDE_OAUTH_CONFIG.clientId,
1479
- code,
1480
- code_verifier: codeVerifier,
1481
- redirect_uri: CLAUDE_OAUTH_CONFIG.redirectUri,
1482
- state
1483
- },
1484
- "Failed to parse setup token response",
1485
- CLAUDE_TOKEN_HEADERS
1486
- );
1487
- return {
1488
- accessToken: data.access_token,
1489
- expiresIn: data.expires_in,
1490
- scopes: data.scope?.split(" ") || SETUP_TOKEN_CONFIG.scopes
1491
- };
1492
- }
1493
- async function refreshAccessToken(refreshToken, fetchImpl) {
1494
- const data = await postJson(
1495
- fetchImpl,
1496
- CLAUDE_OAUTH_CONFIG.tokenEndpoint,
1497
- {
1498
- grant_type: "refresh_token",
1499
- client_id: CLAUDE_OAUTH_CONFIG.clientId,
1500
- refresh_token: refreshToken
1501
- },
1502
- "Failed to parse refresh response",
1503
- CLAUDE_TOKEN_HEADERS
1504
- );
1505
- return {
1506
- accessToken: data.access_token,
1507
- refreshToken: data.refresh_token || refreshToken,
1508
- expiresIn: data.expires_in
1509
- };
1510
- }
1511
1513
 
1512
- // src/oauth/flows/codex.ts
1513
- var codex_exports = {};
1514
- __export(codex_exports, {
1515
- exchangeCodeForTokens: () => exchangeCodeForTokens2,
1516
- generateAuthParams: () => generateAuthParams2,
1517
- refreshAccessToken: () => refreshAccessToken2
1518
- });
1519
- var import_node_crypto2 = __toESM(require("crypto"), 1);
1520
- var CODEX_OAUTH_CONFIG = {
1521
- clientId: "app_EMoamEEZ73f0CkXaXp7hrann",
1522
- authorizationEndpoint: "https://auth.openai.com/oauth/authorize",
1523
- tokenEndpoint: "https://auth.openai.com/oauth/token",
1524
- redirectUri: "http://localhost:1455/auth/callback",
1525
- scopes: ["openid", "profile", "email", "offline_access"]
1526
- };
1527
- function generateAuthParams2() {
1528
- const codeVerifier = import_node_crypto2.default.randomBytes(64).toString("hex");
1529
- const codeChallenge = import_node_crypto2.default.createHash("sha256").update(codeVerifier).digest("base64url");
1530
- const state = import_node_crypto2.default.randomBytes(16).toString("hex");
1531
- const params = new URLSearchParams({
1532
- response_type: "code",
1533
- client_id: CODEX_OAUTH_CONFIG.clientId,
1534
- redirect_uri: CODEX_OAUTH_CONFIG.redirectUri,
1535
- scope: CODEX_OAUTH_CONFIG.scopes.join(" "),
1536
- code_challenge: codeChallenge,
1537
- code_challenge_method: "S256",
1538
- state
1539
- });
1540
- const authUrl = `${CODEX_OAUTH_CONFIG.authorizationEndpoint}?${params.toString()}`;
1541
- return { authUrl, codeVerifier, state };
1542
- }
1543
- async function exchangeCodeForTokens2(request, fetchImpl) {
1544
- const { authorizationCode, codeVerifier } = request;
1545
- const params = new URLSearchParams({
1546
- grant_type: "authorization_code",
1547
- client_id: CODEX_OAUTH_CONFIG.clientId,
1548
- code: authorizationCode,
1549
- code_verifier: codeVerifier,
1550
- redirect_uri: CODEX_OAUTH_CONFIG.redirectUri
1551
- });
1552
- const data = await postForm(
1553
- fetchImpl,
1554
- CODEX_OAUTH_CONFIG.tokenEndpoint,
1555
- params,
1556
- "Failed to parse token response"
1557
- );
1558
- return {
1559
- accessToken: data.access_token,
1560
- // authorization_code grant returns both; the original helper read them from
1561
- // an untyped `data` and declared the fields `string`.
1562
- refreshToken: data.refresh_token,
1563
- idToken: data.id_token,
1564
- expiresIn: data.expires_in
1565
- };
1566
- }
1567
- async function refreshAccessToken2(refreshToken, fetchImpl) {
1568
- const params = new URLSearchParams({
1569
- grant_type: "refresh_token",
1570
- client_id: CODEX_OAUTH_CONFIG.clientId,
1571
- refresh_token: refreshToken,
1572
- scope: "openid profile email"
1573
- });
1574
- const data = await postForm(
1575
- fetchImpl,
1576
- CODEX_OAUTH_CONFIG.tokenEndpoint,
1577
- params,
1578
- "Failed to parse refresh response"
1579
- );
1580
- return {
1581
- accessToken: data.access_token,
1582
- idToken: data.id_token,
1583
- refreshToken: data.refresh_token || refreshToken,
1584
- expiresIn: data.expires_in || 3600
1585
- };
1586
- }
1587
1514
 
1588
- // src/oauth/flows/gemini.ts
1589
- var gemini_exports = {};
1590
- __export(gemini_exports, {
1591
- exchangeCodeForTokens: () => exchangeCodeForTokens3,
1592
- generateAuthParams: () => generateAuthParams3,
1593
- refreshAccessToken: () => refreshAccessToken3
1594
- });
1595
- var import_node_crypto3 = __toESM(require("crypto"), 1);
1596
- var GEMINI_OAUTH_CONFIG = {
1597
- clientId: "681255809395-oo8ft2oprdrnp9e3aqf6av3hmdib135j.apps.googleusercontent.com",
1598
- // The Gemini CLI's *public* installed-app OAuth client secret (mirrors the
1599
- // upstream CLI). Per Google's OAuth docs, native-app client secrets are not
1600
- // treated as confidential — not a leaked key.
1601
- clientSecret: "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",
1602
- // allowlist-secret
1603
- authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
1604
- tokenEndpoint: "https://oauth2.googleapis.com/token",
1605
- redirectUri: "urn:ietf:wg:oauth:2.0:oob",
1606
- scopes: ["https://www.googleapis.com/auth/cloud-platform"]
1607
- };
1608
- function generateAuthParams3() {
1609
- const codeVerifier = import_node_crypto3.default.randomBytes(32).toString("base64url");
1610
- const codeChallenge = import_node_crypto3.default.createHash("sha256").update(codeVerifier).digest("base64url");
1611
- const state = import_node_crypto3.default.randomBytes(16).toString("hex");
1612
- const params = new URLSearchParams({
1613
- client_id: GEMINI_OAUTH_CONFIG.clientId,
1614
- redirect_uri: GEMINI_OAUTH_CONFIG.redirectUri,
1615
- scope: GEMINI_OAUTH_CONFIG.scopes.join(" "),
1616
- response_type: "code",
1617
- code_challenge: codeChallenge,
1618
- code_challenge_method: "S256",
1619
- state,
1620
- access_type: "offline",
1621
- prompt: "consent"
1622
- });
1623
- const authUrl = `${GEMINI_OAUTH_CONFIG.authorizationEndpoint}?${params.toString()}`;
1624
- return { authUrl, codeVerifier, state };
1625
- }
1626
- async function exchangeCodeForTokens3(authorizationCode, codeVerifier, fetchImpl) {
1627
- const params = new URLSearchParams({
1628
- grant_type: "authorization_code",
1629
- client_id: GEMINI_OAUTH_CONFIG.clientId,
1630
- client_secret: GEMINI_OAUTH_CONFIG.clientSecret,
1631
- code: authorizationCode,
1632
- code_verifier: codeVerifier,
1633
- redirect_uri: GEMINI_OAUTH_CONFIG.redirectUri
1634
- });
1635
- const data = await postForm(
1636
- fetchImpl,
1637
- GEMINI_OAUTH_CONFIG.tokenEndpoint,
1638
- params,
1639
- "Failed to parse token response"
1640
- );
1641
- return {
1642
- accessToken: data.access_token,
1643
- // authorization_code grant returns a refresh_token; the original helper read
1644
- // it from an untyped `data` and declared the field `string`.
1645
- refreshToken: data.refresh_token,
1646
- expiresIn: data.expires_in
1647
- };
1648
- }
1649
- async function refreshAccessToken3(refreshToken, fetchImpl) {
1650
- const params = new URLSearchParams({
1651
- grant_type: "refresh_token",
1652
- client_id: GEMINI_OAUTH_CONFIG.clientId,
1653
- client_secret: GEMINI_OAUTH_CONFIG.clientSecret,
1654
- refresh_token: refreshToken
1655
- });
1656
- const data = await postForm(
1657
- fetchImpl,
1658
- GEMINI_OAUTH_CONFIG.tokenEndpoint,
1659
- params,
1660
- "Failed to parse refresh response"
1661
- );
1662
- return {
1663
- accessToken: data.access_token,
1664
- expiresIn: data.expires_in
1665
- };
1666
- }
1667
- // Annotate the CommonJS export names for ESM import in node:
1668
- 0 && (module.exports = {
1669
- SubscriptionAccountService,
1670
- SubscriptionDispatcher,
1671
- SubscriptionProviderRegistry,
1672
- claudeOAuth,
1673
- codexOAuth,
1674
- geminiOAuth,
1675
- getSubscriptionAccountService,
1676
- getSubscriptionProviderRegistry,
1677
- setSubscriptionAccountService,
1678
- setSubscriptionProviderRegistry
1679
- });
1515
+
1516
+
1517
+
1518
+
1519
+
1520
+
1521
+
1522
+
1523
+
1524
+
1525
+ exports.DEFAULT_ACCOUNT_PRIORITY = DEFAULT_ACCOUNT_PRIORITY; exports.LAST_USED_PERSIST_THROTTLE_MS = LAST_USED_PERSIST_THROTTLE_MS; exports.SESSION_AFFINITY_TTL_MS = SESSION_AFFINITY_TTL_MS; exports.SubscriptionAccountSelector = SubscriptionAccountSelector; exports.SubscriptionAccountService = SubscriptionAccountService; exports.SubscriptionDispatcher = SubscriptionDispatcher; exports.SubscriptionProviderRegistry = SubscriptionProviderRegistry; exports.claudeOAuth = _chunkTPW5Q25Ycjs.claude_exports; exports.codexOAuth = _chunkTPW5Q25Ycjs.codex_exports; exports.geminiOAuth = _chunkTPW5Q25Ycjs.gemini_exports; exports.getSubscriptionAccountService = getSubscriptionAccountService; exports.getSubscriptionProviderRegistry = getSubscriptionProviderRegistry; exports.setSubscriptionAccountService = setSubscriptionAccountService; exports.setSubscriptionProviderRegistry = setSubscriptionProviderRegistry;