@omnicross/subscriptions 0.1.6 → 0.1.8

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.
@@ -1,8 +1,6 @@
1
- var __defProp = Object.defineProperty;
2
- var __export = (target, all) => {
3
- for (var name in all)
4
- __defProp(target, name, { get: all[name], enumerable: true });
5
- };
1
+ import {
2
+ __export
3
+ } from "./chunk-MLKGABMK.js";
6
4
 
7
5
  // src/oauth/flows/claude.ts
8
6
  var claude_exports = {};
@@ -29,39 +27,70 @@ function errorMessage(error, errorDescription) {
29
27
  }
30
28
  return String(error);
31
29
  }
32
- async function postForm(fetchImpl, url, params, parseErrorMessage) {
33
- const response = await fetchImpl(url, {
34
- method: "POST",
35
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
36
- body: params.toString()
37
- });
30
+ function withStatus(message, response) {
31
+ return response.ok ? message : `${message} (HTTP ${response.status})`;
32
+ }
33
+ var DEFAULT_TOKEN_TIMEOUT_MS = 3e4;
34
+ async function fetchWithTimeout(fetchImpl, url, init, timeoutMs) {
35
+ const controller = new AbortController();
36
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
37
+ try {
38
+ return await fetchImpl(url, { ...init, signal: controller.signal });
39
+ } catch (e) {
40
+ if (controller.signal.aborted) {
41
+ throw new Error(`token endpoint timed out after ${Math.round(timeoutMs / 1e3)}s`);
42
+ }
43
+ throw e;
44
+ } finally {
45
+ clearTimeout(timer);
46
+ }
47
+ }
48
+ async function postForm(fetchImpl, url, params, parseErrorMessage, timeoutMs = DEFAULT_TOKEN_TIMEOUT_MS) {
49
+ const response = await fetchWithTimeout(
50
+ fetchImpl,
51
+ url,
52
+ {
53
+ method: "POST",
54
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
55
+ body: params.toString()
56
+ },
57
+ timeoutMs
58
+ );
38
59
  const responseData = await response.text();
39
60
  let data;
40
61
  try {
41
62
  data = JSON.parse(responseData);
42
63
  } catch {
43
- throw new Error(parseErrorMessage);
64
+ throw new Error(withStatus(parseErrorMessage, response));
44
65
  }
45
66
  if (data.error) {
46
- throw new Error(errorMessage(data.error, data.error_description));
67
+ throw new Error(withStatus(errorMessage(data.error, data.error_description), response));
47
68
  }
48
69
  return data;
49
70
  }
50
- async function postJson(fetchImpl, url, body, parseErrorMessage, extraHeaders = {}) {
51
- const response = await fetchImpl(url, {
52
- method: "POST",
53
- headers: { "Content-Type": "application/json", ...extraHeaders },
54
- body: JSON.stringify(body)
55
- });
71
+ async function postJson(fetchImpl, url, body, parseErrorMessage, extraHeaders = {}, timeoutMs = DEFAULT_TOKEN_TIMEOUT_MS) {
72
+ const response = await fetchWithTimeout(
73
+ fetchImpl,
74
+ url,
75
+ {
76
+ method: "POST",
77
+ headers: { "Content-Type": "application/json", ...extraHeaders },
78
+ body: JSON.stringify(body)
79
+ },
80
+ timeoutMs
81
+ );
56
82
  const responseData = await response.text();
57
83
  let data;
58
84
  try {
59
85
  data = JSON.parse(responseData);
60
86
  } catch {
61
- throw new Error(parseErrorMessage);
87
+ throw new Error(withStatus(parseErrorMessage, response));
62
88
  }
63
89
  if (data.error) {
64
- throw new Error(errorMessage(data.error, data.error_description));
90
+ throw new Error(withStatus(errorMessage(data.error, data.error_description), response));
91
+ }
92
+ if (!response.ok) {
93
+ throw new Error(withStatus("token endpoint rejected the request", response));
65
94
  }
66
95
  return data;
67
96
  }
@@ -0,0 +1,33 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/scheduler/accountModelMap.ts
2
+ function canonicalModelId(value) {
3
+ const idx = value.indexOf(",");
4
+ const bare = idx >= 0 ? value.slice(idx + 1) : value;
5
+ return bare.trim().toLowerCase();
6
+ }
7
+ function accountSupportsModel(supportedModels, model) {
8
+ if (!supportedModels) return true;
9
+ const target = canonicalModelId(model);
10
+ if (Array.isArray(supportedModels)) {
11
+ return supportedModels.some((m) => canonicalModelId(m) === target);
12
+ }
13
+ return Object.keys(supportedModels).some((k) => canonicalModelId(k) === target);
14
+ }
15
+ function remapForAccount(supportedModels, model) {
16
+ if (!supportedModels || Array.isArray(supportedModels)) return model;
17
+ const target = canonicalModelId(model);
18
+ for (const [key, actual] of Object.entries(supportedModels)) {
19
+ if (canonicalModelId(key) === target) return actual;
20
+ }
21
+ return model;
22
+ }
23
+ function remapReportForAccount(supportedModels, model) {
24
+ if (!model) return void 0;
25
+ const remapped = remapForAccount(supportedModels, model);
26
+ return remapped === model ? void 0 : remapped;
27
+ }
28
+
29
+
30
+
31
+
32
+
33
+ exports.accountSupportsModel = accountSupportsModel; exports.remapForAccount = remapForAccount; exports.remapReportForAccount = remapReportForAccount;
@@ -0,0 +1,9 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+
8
+
9
+ exports.__export = __export;
@@ -0,0 +1,9 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __export = (target, all) => {
3
+ for (var name in all)
4
+ __defProp(target, name, { get: all[name], enumerable: true });
5
+ };
6
+
7
+ export {
8
+ __export
9
+ };
@@ -0,0 +1,33 @@
1
+ // src/scheduler/accountModelMap.ts
2
+ function canonicalModelId(value) {
3
+ const idx = value.indexOf(",");
4
+ const bare = idx >= 0 ? value.slice(idx + 1) : value;
5
+ return bare.trim().toLowerCase();
6
+ }
7
+ function accountSupportsModel(supportedModels, model) {
8
+ if (!supportedModels) return true;
9
+ const target = canonicalModelId(model);
10
+ if (Array.isArray(supportedModels)) {
11
+ return supportedModels.some((m) => canonicalModelId(m) === target);
12
+ }
13
+ return Object.keys(supportedModels).some((k) => canonicalModelId(k) === target);
14
+ }
15
+ function remapForAccount(supportedModels, model) {
16
+ if (!supportedModels || Array.isArray(supportedModels)) return model;
17
+ const target = canonicalModelId(model);
18
+ for (const [key, actual] of Object.entries(supportedModels)) {
19
+ if (canonicalModelId(key) === target) return actual;
20
+ }
21
+ return model;
22
+ }
23
+ function remapReportForAccount(supportedModels, model) {
24
+ if (!model) return void 0;
25
+ const remapped = remapForAccount(supportedModels, model);
26
+ return remapped === model ? void 0 : remapped;
27
+ }
28
+
29
+ export {
30
+ accountSupportsModel,
31
+ remapForAccount,
32
+ remapReportForAccount
33
+ };
@@ -1,12 +1,10 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return 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 __defProp = Object.defineProperty;
2
- var __export = (target, all) => {
3
- for (var name in all)
4
- __defProp(target, name, { get: all[name], enumerable: true });
5
- };
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return 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; }
2
+
3
+ var _chunk75ZPJI57cjs = require('./chunk-75ZPJI57.cjs');
6
4
 
7
5
  // src/oauth/flows/claude.ts
8
6
  var claude_exports = {};
9
- __export(claude_exports, {
7
+ _chunk75ZPJI57cjs.__export.call(void 0, claude_exports, {
10
8
  exchangeCodeForTokens: () => exchangeCodeForTokens,
11
9
  exchangeSetupTokenCode: () => exchangeSetupTokenCode,
12
10
  generateAuthParams: () => generateAuthParams,
@@ -29,39 +27,70 @@ function errorMessage(error, errorDescription) {
29
27
  }
30
28
  return String(error);
31
29
  }
32
- async function postForm(fetchImpl, url, params, parseErrorMessage) {
33
- const response = await fetchImpl(url, {
34
- method: "POST",
35
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
36
- body: params.toString()
37
- });
30
+ function withStatus(message, response) {
31
+ return response.ok ? message : `${message} (HTTP ${response.status})`;
32
+ }
33
+ var DEFAULT_TOKEN_TIMEOUT_MS = 3e4;
34
+ async function fetchWithTimeout(fetchImpl, url, init, timeoutMs) {
35
+ const controller = new AbortController();
36
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
37
+ try {
38
+ return await fetchImpl(url, { ...init, signal: controller.signal });
39
+ } catch (e) {
40
+ if (controller.signal.aborted) {
41
+ throw new Error(`token endpoint timed out after ${Math.round(timeoutMs / 1e3)}s`);
42
+ }
43
+ throw e;
44
+ } finally {
45
+ clearTimeout(timer);
46
+ }
47
+ }
48
+ async function postForm(fetchImpl, url, params, parseErrorMessage, timeoutMs = DEFAULT_TOKEN_TIMEOUT_MS) {
49
+ const response = await fetchWithTimeout(
50
+ fetchImpl,
51
+ url,
52
+ {
53
+ method: "POST",
54
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
55
+ body: params.toString()
56
+ },
57
+ timeoutMs
58
+ );
38
59
  const responseData = await response.text();
39
60
  let data;
40
61
  try {
41
62
  data = JSON.parse(responseData);
42
63
  } catch (e2) {
43
- throw new Error(parseErrorMessage);
64
+ throw new Error(withStatus(parseErrorMessage, response));
44
65
  }
45
66
  if (data.error) {
46
- throw new Error(errorMessage(data.error, data.error_description));
67
+ throw new Error(withStatus(errorMessage(data.error, data.error_description), response));
47
68
  }
48
69
  return data;
49
70
  }
50
- async function postJson(fetchImpl, url, body, parseErrorMessage, extraHeaders = {}) {
51
- const response = await fetchImpl(url, {
52
- method: "POST",
53
- headers: { "Content-Type": "application/json", ...extraHeaders },
54
- body: JSON.stringify(body)
55
- });
71
+ async function postJson(fetchImpl, url, body, parseErrorMessage, extraHeaders = {}, timeoutMs = DEFAULT_TOKEN_TIMEOUT_MS) {
72
+ const response = await fetchWithTimeout(
73
+ fetchImpl,
74
+ url,
75
+ {
76
+ method: "POST",
77
+ headers: { "Content-Type": "application/json", ...extraHeaders },
78
+ body: JSON.stringify(body)
79
+ },
80
+ timeoutMs
81
+ );
56
82
  const responseData = await response.text();
57
83
  let data;
58
84
  try {
59
85
  data = JSON.parse(responseData);
60
86
  } catch (e3) {
61
- throw new Error(parseErrorMessage);
87
+ throw new Error(withStatus(parseErrorMessage, response));
62
88
  }
63
89
  if (data.error) {
64
- throw new Error(errorMessage(data.error, data.error_description));
90
+ throw new Error(withStatus(errorMessage(data.error, data.error_description), response));
91
+ }
92
+ if (!response.ok) {
93
+ throw new Error(withStatus("token endpoint rejected the request", response));
65
94
  }
66
95
  return data;
67
96
  }
@@ -204,7 +233,7 @@ async function refreshAccessToken(refreshToken, fetchImpl) {
204
233
 
205
234
  // src/oauth/flows/codex.ts
206
235
  var codex_exports = {};
207
- __export(codex_exports, {
236
+ _chunk75ZPJI57cjs.__export.call(void 0, codex_exports, {
208
237
  exchangeCodeForTokens: () => exchangeCodeForTokens2,
209
238
  generateAuthParams: () => generateAuthParams2,
210
239
  refreshAccessToken: () => refreshAccessToken2
@@ -280,7 +309,7 @@ async function refreshAccessToken2(refreshToken, fetchImpl) {
280
309
 
281
310
  // src/oauth/flows/gemini.ts
282
311
  var gemini_exports = {};
283
- __export(gemini_exports, {
312
+ _chunk75ZPJI57cjs.__export.call(void 0, gemini_exports, {
284
313
  exchangeCodeForTokens: () => exchangeCodeForTokens3,
285
314
  generateAuthParams: () => generateAuthParams3,
286
315
  refreshAccessToken: () => refreshAccessToken3
package/dist/index.cjs CHANGED
@@ -2,7 +2,12 @@
2
2
 
3
3
 
4
4
 
5
- var _chunkTPW5Q25Ycjs = require('./chunk-TPW5Q25Y.cjs');
5
+ var _chunkUXUMAXZJcjs = require('./chunk-UXUMAXZJ.cjs');
6
+
7
+
8
+
9
+ var _chunk6TTHLCG6cjs = require('./chunk-6TTHLCG6.cjs');
10
+ require('./chunk-75ZPJI57.cjs');
6
11
 
7
12
  // src/scheduler/SubscriptionAccountSelector.ts
8
13
  var SESSION_AFFINITY_TTL_MS = 36e5;
@@ -61,7 +66,7 @@ var SubscriptionAccountSelector = (_class = class {constructor() { _class.protot
61
66
  * conversation).
62
67
  */
63
68
  evictAffinity(providerId, accountId) {
64
- const prefix = `${providerId} `;
69
+ const prefix = scopedKey(providerId, "");
65
70
  for (const [key, entry] of this.affinity) {
66
71
  if (entry.accountId === accountId && key.startsWith(prefix)) {
67
72
  this.affinity.delete(key);
@@ -116,36 +121,6 @@ var _AccountAllowanceScheduling = require('@omnicross/core/pipeline/AccountAllow
116
121
 
117
122
 
118
123
  var _BoundAccountSelectionError = require('@omnicross/core/pipeline/BoundAccountSelectionError');
119
-
120
- // src/scheduler/accountModelMap.ts
121
- function canonicalModelId(value) {
122
- const idx = value.indexOf(",");
123
- const bare = idx >= 0 ? value.slice(idx + 1) : value;
124
- return bare.trim().toLowerCase();
125
- }
126
- function accountSupportsModel(supportedModels, model) {
127
- if (!supportedModels) return true;
128
- const target = canonicalModelId(model);
129
- if (Array.isArray(supportedModels)) {
130
- return supportedModels.some((m) => canonicalModelId(m) === target);
131
- }
132
- return Object.keys(supportedModels).some((k) => canonicalModelId(k) === target);
133
- }
134
- function remapForAccount(supportedModels, model) {
135
- if (!supportedModels || Array.isArray(supportedModels)) return model;
136
- const target = canonicalModelId(model);
137
- for (const [key, actual] of Object.entries(supportedModels)) {
138
- if (canonicalModelId(key) === target) return actual;
139
- }
140
- return model;
141
- }
142
- function remapReportForAccount(supportedModels, model) {
143
- if (!model) return void 0;
144
- const remapped = remapForAccount(supportedModels, model);
145
- return remapped === model ? void 0 : remapped;
146
- }
147
-
148
- // src/scheduler/accountSelection.ts
149
124
  var ACCOUNTS_KEY = {
150
125
  claude: "claudeAccounts",
151
126
  codex: "codexAccounts",
@@ -162,7 +137,7 @@ function gateSchedulable(accounts, providerId, health, now, resolvedModel, suppo
162
137
  if (!health && !resolvedModel || accounts.length < 2) return accounts;
163
138
  return accounts.map((a) => {
164
139
  const healthOk = health ? health.isSchedulable(providerId, a.id, now) : true;
165
- const modelOk = resolvedModel ? accountSupportsModel(supportedModelsById.get(a.id), resolvedModel) : true;
140
+ const modelOk = resolvedModel ? _chunk6TTHLCG6cjs.accountSupportsModel.call(void 0, supportedModelsById.get(a.id), resolvedModel) : true;
166
141
  return { ...a, schedulable: a.schedulable !== false && healthOk && modelOk };
167
142
  });
168
143
  }
@@ -172,7 +147,7 @@ function isPoolGated(accounts, health, resolvedModel) {
172
147
  function hasUsableToken(token) {
173
148
  return typeof token === "string" && token.trim() !== "";
174
149
  }
175
- async function resolveStrictPreferredToken(tokens, providerId, preferredId, activeGetter, ctx) {
150
+ async function resolveStrictPreferredToken(selector, tokens, providerId, preferredId, activeGetter, ctx) {
176
151
  let config;
177
152
  try {
178
153
  config = await tokens.getFullConfig();
@@ -197,7 +172,7 @@ async function resolveStrictPreferredToken(tokens, providerId, preferredId, acti
197
172
  if (ctx.health && !ctx.health.isSchedulable(providerId, preferredId, ctx.now)) {
198
173
  throw new (0, _BoundAccountSelectionError.BoundAccountSelectionError)(providerId, "unhealthy");
199
174
  }
200
- if (ctx.resolvedModel && !accountSupportsModel(supportedModelsById.get(preferredId), ctx.resolvedModel)) {
175
+ if (ctx.resolvedModel && !_chunk6TTHLCG6cjs.accountSupportsModel.call(void 0, supportedModelsById.get(preferredId), ctx.resolvedModel)) {
201
176
  throw new (0, _BoundAccountSelectionError.BoundAccountSelectionError)(providerId, "model-incompatible");
202
177
  }
203
178
  const allowance = _AccountAllowanceScheduling.getSharedAccountAllowanceScheduling.call(void 0, ).evaluate(
@@ -218,7 +193,8 @@ async function resolveStrictPreferredToken(tokens, providerId, preferredId, acti
218
193
  if (!hasUsableToken(token)) {
219
194
  throw new (0, _BoundAccountSelectionError.BoundAccountSelectionError)(providerId, "empty-token");
220
195
  }
221
- const remapped = remapReportForAccount(supportedModelsById.get(preferredId), ctx.resolvedModel);
196
+ const remapped = _chunk6TTHLCG6cjs.remapReportForAccount.call(void 0, supportedModelsById.get(preferredId), ctx.resolvedModel);
197
+ if (selector) maybeTouchLastUsed(selector, tokens, providerId, preferredId);
222
198
  _optionalChain([ctx, 'access', _ => _.reportSelection, 'optionalCall', _2 => _2(preferredId, preferredId === activeAccountId, remapped)]);
223
199
  return token;
224
200
  }
@@ -284,7 +260,7 @@ async function resolveSelectedToken(selector, tokens, providerId, sessionKey, ac
284
260
  const preferredId = typeof _optionalChain([ctx, 'optionalAccess', _9 => _9.preferredAccountId]) === "string" && ctx.preferredAccountId.trim() !== "" ? ctx.preferredAccountId.trim() : void 0;
285
261
  const preferredGroup = typeof _optionalChain([ctx, 'optionalAccess', _10 => _10.preferredAccountGroup]) === "string" && ctx.preferredAccountGroup.trim() !== "" ? ctx.preferredAccountGroup.trim() : void 0;
286
262
  if (preferredId && ctx && ctx.boundAccountFallbackPolicy !== "pool") {
287
- return resolveStrictPreferredToken(tokens, providerId, preferredId, activeGetter, ctx);
263
+ return resolveStrictPreferredToken(selector, tokens, providerId, preferredId, activeGetter, ctx);
288
264
  }
289
265
  if (selector && tokens.getAccessTokenForAccount) {
290
266
  const config = await tokens.getFullConfig();
@@ -327,7 +303,7 @@ async function resolveSelectedToken(selector, tokens, providerId, sessionKey, ac
327
303
  gated = gateByAllowance(healthAndModelGated, providerId, now);
328
304
  }
329
305
  const poolGated = isPoolGated(candidates, health, resolvedModel) || candidates.some((account) => account.schedulable === false) || preferredGroup !== void 0;
330
- const remapFor = (id) => remapReportForAccount(supportedModelsById.get(id), resolvedModel);
306
+ const remapFor = (id) => _chunk6TTHLCG6cjs.remapReportForAccount.call(void 0, supportedModelsById.get(id), resolvedModel);
331
307
  if (preferredId) {
332
308
  const preferred = gated.find((a) => a.id === preferredId);
333
309
  if (preferred && preferred.schedulable !== false) {
@@ -375,7 +351,12 @@ async function resolveSelectedToken(selector, tokens, providerId, sessionKey, ac
375
351
  if (activeAllowance.action === "pause") {
376
352
  throw new (0, _AccountAllowanceScheduling.AccountAllowanceExhaustedError)(providerId, activeAllowance.resumeAt);
377
353
  }
378
- _optionalChain([report, 'optionalCall', _22 => _22(activeAccountId, true, remapFor(activeAccountId))]);
354
+ const token = await activeGetter();
355
+ if (hasUsableToken(token)) {
356
+ maybeTouchLastUsed(selector, tokens, providerId, activeAccountId);
357
+ _optionalChain([report, 'optionalCall', _22 => _22(activeAccountId, true, remapFor(activeAccountId))]);
358
+ }
359
+ return token;
379
360
  }
380
361
  return activeGetter();
381
362
  }
@@ -1697,4 +1678,4 @@ function stripAuthHeaders(headers) {
1697
1678
 
1698
1679
 
1699
1680
 
1700
- 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;
1681
+ 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 = _chunkUXUMAXZJcjs.claude_exports; exports.codexOAuth = _chunkUXUMAXZJcjs.codex_exports; exports.geminiOAuth = _chunkUXUMAXZJcjs.gemini_exports; exports.getSubscriptionAccountService = getSubscriptionAccountService; exports.getSubscriptionProviderRegistry = getSubscriptionProviderRegistry; exports.setSubscriptionAccountService = setSubscriptionAccountService; exports.setSubscriptionProviderRegistry = setSubscriptionProviderRegistry;
package/dist/index.js CHANGED
@@ -2,7 +2,12 @@ import {
2
2
  claude_exports,
3
3
  codex_exports,
4
4
  gemini_exports
5
- } from "./chunk-IXGHVMZB.js";
5
+ } from "./chunk-4HYDPKHR.js";
6
+ import {
7
+ accountSupportsModel,
8
+ remapReportForAccount
9
+ } from "./chunk-TDDDW53K.js";
10
+ import "./chunk-MLKGABMK.js";
6
11
 
7
12
  // src/scheduler/SubscriptionAccountSelector.ts
8
13
  var SESSION_AFFINITY_TTL_MS = 36e5;
@@ -61,7 +66,7 @@ var SubscriptionAccountSelector = class {
61
66
  * conversation).
62
67
  */
63
68
  evictAffinity(providerId, accountId) {
64
- const prefix = `${providerId} `;
69
+ const prefix = scopedKey(providerId, "");
65
70
  for (const [key, entry] of this.affinity) {
66
71
  if (entry.accountId === accountId && key.startsWith(prefix)) {
67
72
  this.affinity.delete(key);
@@ -116,36 +121,6 @@ import {
116
121
  import {
117
122
  BoundAccountSelectionError
118
123
  } from "@omnicross/core/pipeline/BoundAccountSelectionError";
119
-
120
- // src/scheduler/accountModelMap.ts
121
- function canonicalModelId(value) {
122
- const idx = value.indexOf(",");
123
- const bare = idx >= 0 ? value.slice(idx + 1) : value;
124
- return bare.trim().toLowerCase();
125
- }
126
- function accountSupportsModel(supportedModels, model) {
127
- if (!supportedModels) return true;
128
- const target = canonicalModelId(model);
129
- if (Array.isArray(supportedModels)) {
130
- return supportedModels.some((m) => canonicalModelId(m) === target);
131
- }
132
- return Object.keys(supportedModels).some((k) => canonicalModelId(k) === target);
133
- }
134
- function remapForAccount(supportedModels, model) {
135
- if (!supportedModels || Array.isArray(supportedModels)) return model;
136
- const target = canonicalModelId(model);
137
- for (const [key, actual] of Object.entries(supportedModels)) {
138
- if (canonicalModelId(key) === target) return actual;
139
- }
140
- return model;
141
- }
142
- function remapReportForAccount(supportedModels, model) {
143
- if (!model) return void 0;
144
- const remapped = remapForAccount(supportedModels, model);
145
- return remapped === model ? void 0 : remapped;
146
- }
147
-
148
- // src/scheduler/accountSelection.ts
149
124
  var ACCOUNTS_KEY = {
150
125
  claude: "claudeAccounts",
151
126
  codex: "codexAccounts",
@@ -172,7 +147,7 @@ function isPoolGated(accounts, health, resolvedModel) {
172
147
  function hasUsableToken(token) {
173
148
  return typeof token === "string" && token.trim() !== "";
174
149
  }
175
- async function resolveStrictPreferredToken(tokens, providerId, preferredId, activeGetter, ctx) {
150
+ async function resolveStrictPreferredToken(selector, tokens, providerId, preferredId, activeGetter, ctx) {
176
151
  let config;
177
152
  try {
178
153
  config = await tokens.getFullConfig();
@@ -219,6 +194,7 @@ async function resolveStrictPreferredToken(tokens, providerId, preferredId, acti
219
194
  throw new BoundAccountSelectionError(providerId, "empty-token");
220
195
  }
221
196
  const remapped = remapReportForAccount(supportedModelsById.get(preferredId), ctx.resolvedModel);
197
+ if (selector) maybeTouchLastUsed(selector, tokens, providerId, preferredId);
222
198
  ctx.reportSelection?.(preferredId, preferredId === activeAccountId, remapped);
223
199
  return token;
224
200
  }
@@ -284,7 +260,7 @@ async function resolveSelectedToken(selector, tokens, providerId, sessionKey, ac
284
260
  const preferredId = typeof ctx?.preferredAccountId === "string" && ctx.preferredAccountId.trim() !== "" ? ctx.preferredAccountId.trim() : void 0;
285
261
  const preferredGroup = typeof ctx?.preferredAccountGroup === "string" && ctx.preferredAccountGroup.trim() !== "" ? ctx.preferredAccountGroup.trim() : void 0;
286
262
  if (preferredId && ctx && ctx.boundAccountFallbackPolicy !== "pool") {
287
- return resolveStrictPreferredToken(tokens, providerId, preferredId, activeGetter, ctx);
263
+ return resolveStrictPreferredToken(selector, tokens, providerId, preferredId, activeGetter, ctx);
288
264
  }
289
265
  if (selector && tokens.getAccessTokenForAccount) {
290
266
  const config = await tokens.getFullConfig();
@@ -375,7 +351,12 @@ async function resolveSelectedToken(selector, tokens, providerId, sessionKey, ac
375
351
  if (activeAllowance.action === "pause") {
376
352
  throw new AccountAllowanceExhaustedError(providerId, activeAllowance.resumeAt);
377
353
  }
378
- report?.(activeAccountId, true, remapFor(activeAccountId));
354
+ const token = await activeGetter();
355
+ if (hasUsableToken(token)) {
356
+ maybeTouchLastUsed(selector, tokens, providerId, activeAccountId);
357
+ report?.(activeAccountId, true, remapFor(activeAccountId));
358
+ }
359
+ return token;
379
360
  }
380
361
  return activeGetter();
381
362
  }
package/dist/oauth.cjs CHANGED
@@ -2,9 +2,10 @@
2
2
 
3
3
 
4
4
 
5
- var _chunkTPW5Q25Ycjs = require('./chunk-TPW5Q25Y.cjs');
5
+ var _chunkUXUMAXZJcjs = require('./chunk-UXUMAXZJ.cjs');
6
+ require('./chunk-75ZPJI57.cjs');
6
7
 
7
8
 
8
9
 
9
10
 
10
- exports.claudeOAuth = _chunkTPW5Q25Ycjs.claude_exports; exports.codexOAuth = _chunkTPW5Q25Ycjs.codex_exports; exports.geminiOAuth = _chunkTPW5Q25Ycjs.gemini_exports;
11
+ exports.claudeOAuth = _chunkUXUMAXZJcjs.claude_exports; exports.codexOAuth = _chunkUXUMAXZJcjs.codex_exports; exports.geminiOAuth = _chunkUXUMAXZJcjs.gemini_exports;
package/dist/oauth.js CHANGED
@@ -2,7 +2,8 @@ import {
2
2
  claude_exports,
3
3
  codex_exports,
4
4
  gemini_exports
5
- } from "./chunk-IXGHVMZB.js";
5
+ } from "./chunk-4HYDPKHR.js";
6
+ import "./chunk-MLKGABMK.js";
6
7
  export {
7
8
  claude_exports as claudeOAuth,
8
9
  codex_exports as codexOAuth,
@@ -0,0 +1,11 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+
4
+
5
+ var _chunk6TTHLCG6cjs = require('../chunk-6TTHLCG6.cjs');
6
+ require('../chunk-75ZPJI57.cjs');
7
+
8
+
9
+
10
+
11
+ exports.accountSupportsModel = _chunk6TTHLCG6cjs.accountSupportsModel; exports.remapForAccount = _chunk6TTHLCG6cjs.remapForAccount; exports.remapReportForAccount = _chunk6TTHLCG6cjs.remapReportForAccount;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * accountModelMap — pure helpers for the per-account `supportedModels` field
3
+ * (subscription-account-model-map, design D1).
4
+ *
5
+ * Dual-format `SubscriptionAccountEntry.supportedModels`:
6
+ * - **array** — an ALLOW-LIST of supported logical models; no remap.
7
+ * - **object** — the KEYS are the same allow-list AND each VALUE is the
8
+ * account's ACTUAL upstream model (a logical→actual remap).
9
+ * - **absent** — supports every model, no remap (zero regression).
10
+ *
11
+ * Both helpers are pure. They fold into the EXISTING `accountSelection`
12
+ * eligibility path (`accountSupportsModel` → `gateSchedulable`) and the outbound
13
+ * body-model finalize point (`remapForAccount`); they add no new mechanism.
14
+ *
15
+ * Model ids are compared on ONE canonical form — the bare, trimmed, lower-cased
16
+ * id (a `"providerId,modelId"` ref reduces to its `modelId`) — identical to the
17
+ * key-model restriction's `canonicalModelId` (#6), so membership is case- and
18
+ * ref-shape-insensitive and an alias cannot slip past.
19
+ *
20
+ * @module scheduler/accountModelMap
21
+ */
22
+ /** The `supportedModels` value shape (mirrors the contract field). */
23
+ type SupportedModels = string[] | Record<string, string>;
24
+ /**
25
+ * Whether an account with this `supportedModels` supports the resolved model.
26
+ * - absent ⇒ `true` (supports everything — zero regression).
27
+ * - array ⇒ case-insensitive membership.
28
+ * - object ⇒ the model is one of the KEYS (the keys are the allow-list).
29
+ */
30
+ declare function accountSupportsModel(supportedModels: SupportedModels | undefined, model: string): boolean;
31
+ /**
32
+ * The ACTUAL upstream model this account serves the logical model as. Only the
33
+ * OBJECT form remaps: when a key matches the resolved model its value is
34
+ * returned; an array form, an absent map, or a missing key ⇒ the model unchanged.
35
+ */
36
+ declare function remapForAccount(supportedModels: SupportedModels | undefined, model: string): string;
37
+ /**
38
+ * The remapped model to REPORT to the relay, or `undefined` when there is no
39
+ * actual change (array form / absent / no matching key / a remap equal to the
40
+ * resolved model). Returning `undefined` on a no-op keeps the outbound body
41
+ * byte-identical on the same-format path (the relay only rewrites `body.model`
42
+ * when a value is present).
43
+ */
44
+ declare function remapReportForAccount(supportedModels: SupportedModels | undefined, model: string | undefined): string | undefined;
45
+
46
+ export { type SupportedModels, accountSupportsModel, remapForAccount, remapReportForAccount };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * accountModelMap — pure helpers for the per-account `supportedModels` field
3
+ * (subscription-account-model-map, design D1).
4
+ *
5
+ * Dual-format `SubscriptionAccountEntry.supportedModels`:
6
+ * - **array** — an ALLOW-LIST of supported logical models; no remap.
7
+ * - **object** — the KEYS are the same allow-list AND each VALUE is the
8
+ * account's ACTUAL upstream model (a logical→actual remap).
9
+ * - **absent** — supports every model, no remap (zero regression).
10
+ *
11
+ * Both helpers are pure. They fold into the EXISTING `accountSelection`
12
+ * eligibility path (`accountSupportsModel` → `gateSchedulable`) and the outbound
13
+ * body-model finalize point (`remapForAccount`); they add no new mechanism.
14
+ *
15
+ * Model ids are compared on ONE canonical form — the bare, trimmed, lower-cased
16
+ * id (a `"providerId,modelId"` ref reduces to its `modelId`) — identical to the
17
+ * key-model restriction's `canonicalModelId` (#6), so membership is case- and
18
+ * ref-shape-insensitive and an alias cannot slip past.
19
+ *
20
+ * @module scheduler/accountModelMap
21
+ */
22
+ /** The `supportedModels` value shape (mirrors the contract field). */
23
+ type SupportedModels = string[] | Record<string, string>;
24
+ /**
25
+ * Whether an account with this `supportedModels` supports the resolved model.
26
+ * - absent ⇒ `true` (supports everything — zero regression).
27
+ * - array ⇒ case-insensitive membership.
28
+ * - object ⇒ the model is one of the KEYS (the keys are the allow-list).
29
+ */
30
+ declare function accountSupportsModel(supportedModels: SupportedModels | undefined, model: string): boolean;
31
+ /**
32
+ * The ACTUAL upstream model this account serves the logical model as. Only the
33
+ * OBJECT form remaps: when a key matches the resolved model its value is
34
+ * returned; an array form, an absent map, or a missing key ⇒ the model unchanged.
35
+ */
36
+ declare function remapForAccount(supportedModels: SupportedModels | undefined, model: string): string;
37
+ /**
38
+ * The remapped model to REPORT to the relay, or `undefined` when there is no
39
+ * actual change (array form / absent / no matching key / a remap equal to the
40
+ * resolved model). Returning `undefined` on a no-op keeps the outbound body
41
+ * byte-identical on the same-format path (the relay only rewrites `body.model`
42
+ * when a value is present).
43
+ */
44
+ declare function remapReportForAccount(supportedModels: SupportedModels | undefined, model: string | undefined): string | undefined;
45
+
46
+ export { type SupportedModels, accountSupportsModel, remapForAccount, remapReportForAccount };
@@ -0,0 +1,11 @@
1
+ import {
2
+ accountSupportsModel,
3
+ remapForAccount,
4
+ remapReportForAccount
5
+ } from "../chunk-TDDDW53K.js";
6
+ import "../chunk-MLKGABMK.js";
7
+ export {
8
+ accountSupportsModel,
9
+ remapForAccount,
10
+ remapReportForAccount
11
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omnicross/subscriptions",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Omnicross subscription-as-provider auth strategies, OAuth flows, and the OpenCodeGo scenario dispatcher.",
5
5
  "license": "MIT",
6
6
  "author": "Sayo (https://github.com/Dumoedss)",