@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/chunk-IXGHVMZB.js +365 -0
- package/dist/chunk-TPW5Q25Y.cjs +365 -0
- package/dist/index.cjs +508 -662
- package/dist/index.d.cts +154 -670
- package/dist/index.d.ts +154 -670
- package/dist/index.js +408 -522
- package/dist/oauth.cjs +4 -390
- package/dist/oauth.d.cts +3 -4
- package/dist/oauth.d.ts +3 -4
- package/dist/oauth.js +5 -358
- package/package.json +2 -1
package/dist/index.cjs
CHANGED
|
@@ -1,68 +1,281 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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/
|
|
31
|
-
var
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
223
|
-
|
|
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)
|
|
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
|
-
//
|
|
266
|
-
var _registry = null;
|
|
267
|
-
function setSubscriptionRegistryForOutbound(registry) {
|
|
268
|
-
_registry = registry;
|
|
269
|
-
}
|
|
506
|
+
// src/SubscriptionProviderRegistry.ts
|
|
270
507
|
|
|
271
|
-
|
|
272
|
-
var
|
|
273
|
-
var
|
|
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
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
constructor(opts = {}) {
|
|
303
|
-
this.threshold = opts.threshold
|
|
304
|
-
this.openMs = opts.openMs
|
|
305
|
-
this.halfOpenMaxCalls = opts.halfOpenMaxCalls
|
|
306
|
-
this.now = opts.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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
850
|
-
|
|
851
|
-
|
|
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)
|
|
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
|
|
1105
|
+
_subscriptionRegistryPort.setSubscriptionRegistryForOutbound.call(void 0, _nullishCoalesce(svc, () => ( null)));
|
|
881
1106
|
}
|
|
882
1107
|
function getSubscriptionProviderRegistry() {
|
|
883
1108
|
return _moduleSingleton2;
|
|
884
1109
|
}
|
|
885
1110
|
|
|
886
|
-
//
|
|
887
|
-
var
|
|
888
|
-
|
|
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
|
-
|
|
933
|
-
|
|
934
|
-
|
|
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
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
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
|
|
1207
|
+
_optionalChain([this, 'access', _49 => _49.profile, 'access', _50 => _50.recordModelOutcome, 'optionalCall', _51 => _51(currentModel, false)]);
|
|
1063
1208
|
}
|
|
1064
|
-
|
|
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
|
|
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)
|
|
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
|
|
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
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
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
|
|
1298
|
+
_optionalChain([this, 'access', _64 => _64.profile, 'access', _65 => _65.recordModelOutcome, 'optionalCall', _66 => _66(currentModel, false)]);
|
|
1142
1299
|
}
|
|
1143
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1229
|
-
if (!
|
|
1230
|
-
return
|
|
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
|
|
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
|
|
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
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
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;
|