github-issue-tower-defence-management 1.132.0 → 1.133.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +7 -7
  3. package/bin/adapter/entry-points/cli/index.js +4 -4
  4. package/bin/adapter/entry-points/cli/index.js.map +1 -1
  5. package/bin/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.js +2 -1
  6. package/bin/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.js.map +1 -1
  7. package/bin/adapter/entry-points/handlers/OauthTokenSelectHandler.js +2 -1
  8. package/bin/adapter/entry-points/handlers/OauthTokenSelectHandler.js.map +1 -1
  9. package/bin/adapter/proxy/TokenListLoader.js +4 -5
  10. package/bin/adapter/proxy/TokenListLoader.js.map +1 -1
  11. package/bin/adapter/repositories/NodeTmuxSessionRepository.js +19 -0
  12. package/bin/adapter/repositories/NodeTmuxSessionRepository.js.map +1 -1
  13. package/bin/adapter/repositories/clSessionScopeUnitName.js +10 -0
  14. package/bin/adapter/repositories/clSessionScopeUnitName.js.map +1 -0
  15. package/bin/domain/usecases/LiveSessionOauthTokenSelectUseCase.js +5 -4
  16. package/bin/domain/usecases/LiveSessionOauthTokenSelectUseCase.js.map +1 -1
  17. package/bin/domain/usecases/OauthTokenSelectUseCase.js +31 -4
  18. package/bin/domain/usecases/OauthTokenSelectUseCase.js.map +1 -1
  19. package/package.json +1 -1
  20. package/src/adapter/entry-points/cli/index.ts +4 -4
  21. package/src/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.ts +28 -24
  22. package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts +28 -24
  23. package/src/adapter/proxy/TokenListLoader.test.ts +52 -0
  24. package/src/adapter/proxy/TokenListLoader.ts +25 -5
  25. package/src/adapter/repositories/NodeTmuxSessionRepository.test.ts +55 -0
  26. package/src/adapter/repositories/NodeTmuxSessionRepository.ts +27 -0
  27. package/src/adapter/repositories/clSessionScopeUnitName.test.ts +76 -0
  28. package/src/adapter/repositories/clSessionScopeUnitName.ts +5 -0
  29. package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.test.ts +74 -0
  30. package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts +13 -2
  31. package/src/domain/usecases/OauthTokenSelectUseCase.test.ts +162 -0
  32. package/src/domain/usecases/OauthTokenSelectUseCase.ts +47 -2
  33. package/types/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.d.ts.map +1 -1
  34. package/types/adapter/entry-points/handlers/OauthTokenSelectHandler.d.ts.map +1 -1
  35. package/types/adapter/proxy/TokenListLoader.d.ts +1 -0
  36. package/types/adapter/proxy/TokenListLoader.d.ts.map +1 -1
  37. package/types/adapter/repositories/NodeTmuxSessionRepository.d.ts +1 -0
  38. package/types/adapter/repositories/NodeTmuxSessionRepository.d.ts.map +1 -1
  39. package/types/adapter/repositories/clSessionScopeUnitName.d.ts +2 -0
  40. package/types/adapter/repositories/clSessionScopeUnitName.d.ts.map +1 -0
  41. package/types/domain/usecases/LiveSessionOauthTokenSelectUseCase.d.ts +2 -2
  42. package/types/domain/usecases/LiveSessionOauthTokenSelectUseCase.d.ts.map +1 -1
  43. package/types/domain/usecases/OauthTokenSelectUseCase.d.ts +6 -1
  44. package/types/domain/usecases/OauthTokenSelectUseCase.d.ts.map +1 -1
@@ -247,3 +247,165 @@ describe('OauthTokenSelectUseCase', () => {
247
247
  expect(alive?.eligible).toBe(true);
248
248
  });
249
249
  });
250
+
251
+ const sweepingRandom = (count: number): (() => number) => {
252
+ let index = 0;
253
+ return () => {
254
+ const value = (index + 0.5) / count;
255
+ index += 1;
256
+ return value;
257
+ };
258
+ };
259
+
260
+ const throwingRandom = (): number => {
261
+ throw new Error('random source must not be consulted');
262
+ };
263
+
264
+ const withSelectionWeight = (
265
+ base: OauthTokenCandidate,
266
+ selectionWeight: number,
267
+ ): OauthTokenCandidate => ({ ...base, selectionWeight });
268
+
269
+ describe('OauthTokenSelectUseCase selectionWeight', () => {
270
+ const useCase = new OauthTokenSelectUseCase();
271
+
272
+ it('keeps the deterministic soonest-7d selection and never consults random when weights are uniform', () => {
273
+ const result = useCase.run(
274
+ [
275
+ candidate(
276
+ 'far',
277
+ snapshot({ sevenDayUtilization: 0.1, sevenDayReset: NOW + 6 * DAY }),
278
+ ),
279
+ candidate(
280
+ 'soon',
281
+ snapshot({ sevenDayUtilization: 0.1, sevenDayReset: NOW + 2 * DAY }),
282
+ ),
283
+ ],
284
+ NOW,
285
+ throwingRandom,
286
+ );
287
+
288
+ expect(result.selected?.name).toBe('soon');
289
+ });
290
+
291
+ it('treats an absent selectionWeight the same as weight 1 (uniform, deterministic)', () => {
292
+ const result = useCase.run(
293
+ [
294
+ withSelectionWeight(
295
+ candidate('far', snapshot({ sevenDayReset: NOW + 6 * DAY })),
296
+ 1,
297
+ ),
298
+ candidate('soon', snapshot({ sevenDayReset: NOW + 2 * DAY })),
299
+ ],
300
+ NOW,
301
+ throwingRandom,
302
+ );
303
+
304
+ expect(result.selected?.name).toBe('soon');
305
+ });
306
+
307
+ it('selects a sole eligible low-weight token without consulting random (no starvation)', () => {
308
+ const result = useCase.run(
309
+ [
310
+ withSelectionWeight(candidate('lowWeightOnly', snapshot({})), 0.01),
311
+ candidate('blocked', snapshot({ fiveHourUtilization: 0.9 })),
312
+ ],
313
+ NOW,
314
+ throwingRandom,
315
+ );
316
+
317
+ expect(result.selected?.name).toBe('lowWeightOnly');
318
+ });
319
+
320
+ it('chooses a lower-weight token proportionally less often among eligible candidates', () => {
321
+ const count = 1000;
322
+ const random = sweepingRandom(count);
323
+ const selectionCounts = new Map<string, number>();
324
+
325
+ for (let i = 0; i < count; i += 1) {
326
+ const result = useCase.run(
327
+ [
328
+ withSelectionWeight(candidate('heavy', snapshot({})), 1),
329
+ withSelectionWeight(candidate('light', snapshot({})), 0.5),
330
+ ],
331
+ NOW,
332
+ random,
333
+ );
334
+ const name = result.selected?.name ?? 'none';
335
+ selectionCounts.set(name, (selectionCounts.get(name) ?? 0) + 1);
336
+ }
337
+
338
+ const heavy = selectionCounts.get('heavy') ?? 0;
339
+ const light = selectionCounts.get('light') ?? 0;
340
+ expect(heavy + light).toBe(count);
341
+ expect(light).toBeGreaterThan(0);
342
+ expect(light).toBeLessThan(heavy);
343
+ expect(Math.abs(light / count - 1 / 3)).toBeLessThan(0.02);
344
+ expect(Math.abs(heavy / count - 2 / 3)).toBeLessThan(0.02);
345
+ });
346
+
347
+ it('chooses a higher-weight token more often among eligible candidates', () => {
348
+ const count = 1000;
349
+ const random = sweepingRandom(count);
350
+ const selectionCounts = new Map<string, number>();
351
+
352
+ for (let i = 0; i < count; i += 1) {
353
+ const result = useCase.run(
354
+ [
355
+ withSelectionWeight(candidate('double', snapshot({})), 2),
356
+ withSelectionWeight(candidate('single', snapshot({})), 1),
357
+ ],
358
+ NOW,
359
+ random,
360
+ );
361
+ const name = result.selected?.name ?? 'none';
362
+ selectionCounts.set(name, (selectionCounts.get(name) ?? 0) + 1);
363
+ }
364
+
365
+ const double = selectionCounts.get('double') ?? 0;
366
+ const single = selectionCounts.get('single') ?? 0;
367
+ expect(double).toBeGreaterThan(single);
368
+ expect(single).toBeGreaterThan(0);
369
+ });
370
+
371
+ it('gives a zero-weight token no chance while other eligible tokens exist', () => {
372
+ const count = 100;
373
+ const random = sweepingRandom(count);
374
+ const selectionCounts = new Map<string, number>();
375
+
376
+ for (let i = 0; i < count; i += 1) {
377
+ const result = useCase.run(
378
+ [
379
+ withSelectionWeight(candidate('zero', snapshot({})), 0),
380
+ withSelectionWeight(candidate('positive', snapshot({})), 1),
381
+ ],
382
+ NOW,
383
+ random,
384
+ );
385
+ const name = result.selected?.name ?? 'none';
386
+ selectionCounts.set(name, (selectionCounts.get(name) ?? 0) + 1);
387
+ }
388
+
389
+ expect(selectionCounts.get('zero') ?? 0).toBe(0);
390
+ expect(selectionCounts.get('positive') ?? 0).toBe(count);
391
+ });
392
+
393
+ it('falls back to the deterministic best when every eligible weight is zero', () => {
394
+ const result = useCase.run(
395
+ [
396
+ withSelectionWeight(
397
+ candidate('far', snapshot({ sevenDayReset: NOW + 6 * DAY })),
398
+ 0,
399
+ ),
400
+ withSelectionWeight(
401
+ candidate('soon', snapshot({ sevenDayReset: NOW + 2 * DAY })),
402
+ 0,
403
+ ),
404
+ ],
405
+ NOW,
406
+ throwingRandom,
407
+ );
408
+
409
+ expect(result.selected?.name).toBe('soon');
410
+ });
411
+ });
@@ -12,6 +12,16 @@ export type OauthTokenCandidate = {
12
12
  subscriptionDisabled: boolean;
13
13
  unifiedRejected: boolean;
14
14
  fableRejected: boolean;
15
+ selectionWeight?: number;
16
+ };
17
+
18
+ export type SelectionRandom = () => number;
19
+
20
+ export const DEFAULT_SELECTION_WEIGHT = 1;
21
+
22
+ export const selectionWeightOf = (candidate: OauthTokenCandidate): number => {
23
+ const weight = candidate.selectionWeight ?? DEFAULT_SELECTION_WEIGHT;
24
+ return weight > 0 ? weight : 0;
15
25
  };
16
26
 
17
27
  export type OauthTokenCandidateMetrics = {
@@ -34,10 +44,38 @@ const SEVEN_DAYS_IN_SECONDS = 7 * SECONDS_PER_DAY;
34
44
  export const FIVE_HOUR_MIN_FREE_RATIO = 0.6;
35
45
  export const SEVEN_DAY_MIN_FREE_RATIO = 0.07;
36
46
 
47
+ export const selectWeightedCandidate = <Entry>(
48
+ eligible: Entry[],
49
+ candidateOf: (entry: Entry) => OauthTokenCandidate,
50
+ deterministicBest: Entry,
51
+ random: SelectionRandom,
52
+ ): Entry => {
53
+ if (eligible.length <= 1) {
54
+ return deterministicBest;
55
+ }
56
+ const weights = eligible.map((entry) =>
57
+ selectionWeightOf(candidateOf(entry)),
58
+ );
59
+ const totalWeight = weights.reduce((sum, weight) => sum + weight, 0);
60
+ const uniform = weights.every((weight) => weight === weights[0]);
61
+ if (uniform || totalWeight <= 0) {
62
+ return deterministicBest;
63
+ }
64
+ let threshold = random() * totalWeight;
65
+ for (let index = 0; index < eligible.length; index += 1) {
66
+ threshold -= weights[index];
67
+ if (threshold < 0) {
68
+ return eligible[index];
69
+ }
70
+ }
71
+ return eligible[eligible.length - 1];
72
+ };
73
+
37
74
  export class OauthTokenSelectUseCase {
38
75
  run = (
39
76
  candidates: OauthTokenCandidate[],
40
77
  nowEpochSeconds: number,
78
+ random: SelectionRandom = Math.random,
41
79
  ): OauthTokenSelectResult => {
42
80
  const evaluated = candidates.map((candidate) => ({
43
81
  candidate,
@@ -51,13 +89,20 @@ export class OauthTokenSelectUseCase {
51
89
  return { selected: null, metrics };
52
90
  }
53
91
 
54
- const best = eligible.reduce((bestEntry, currentEntry) =>
92
+ const deterministicBest = eligible.reduce((bestEntry, currentEntry) =>
55
93
  currentEntry.metric.sevenDayEndEpoch < bestEntry.metric.sevenDayEndEpoch
56
94
  ? currentEntry
57
95
  : bestEntry,
58
96
  );
59
97
 
60
- return { selected: best.candidate, metrics };
98
+ const selected = selectWeightedCandidate(
99
+ eligible,
100
+ (entry) => entry.candidate,
101
+ deterministicBest,
102
+ random,
103
+ );
104
+
105
+ return { selected: selected.candidate, metrics };
61
106
  };
62
107
 
63
108
  private evaluate = (
@@ -1 +1 @@
1
- {"version":3,"file":"LiveSessionOauthTokenSelectHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,yEAAyE,CAAC;AACtH,OAAO,EAEL,kCAAkC,EACnC,MAAM,6DAA6D,CAAC;AAcrE,MAAM,MAAM,uCAAuC,GAAG;IACpD,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,wCAAwC,GAAG;IACrD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAEF,qBAAa,kCAAkC;IAE3C,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,qBAAqB;gBADrB,OAAO,GAAE,kCAA6E,EACtF,qBAAqB,GAAE,2BAAmE;IAG7G,MAAM,GACJ,OAAO,uCAAuC,KAC7C,wCAAwC,CA+DzC;IAEF,OAAO,CAAC,iBAAiB,CAyBvB;CACH"}
1
+ {"version":3,"file":"LiveSessionOauthTokenSelectHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,2BAA2B,EAAE,MAAM,yEAAyE,CAAC;AACtH,OAAO,EAEL,kCAAkC,EACnC,MAAM,6DAA6D,CAAC;AAerE,MAAM,MAAM,uCAAuC,GAAG;IACpD,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,wCAAwC,GAAG;IACrD,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAEF,qBAAa,kCAAkC;IAE3C,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,qBAAqB;gBADrB,OAAO,GAAE,kCAA6E,EACtF,qBAAqB,GAAE,2BAAmE;IAG7G,MAAM,GACJ,OAAO,uCAAuC,KAC7C,wCAAwC,CAkEzC;IAEF,OAAO,CAAC,iBAAiB,CAyBvB;CACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"OauthTokenSelectHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,uBAAuB,EACxB,MAAM,kDAAkD,CAAC;AAQ1D,MAAM,MAAM,4BAA4B,GAAG;IACzC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAKF,eAAO,MAAM,wBAAwB,GACnC,cAAc,MAAM,GAAG,IAAI,KAC1B,MAAM,GAAG,IASX,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,mBAAmB,MAAM,GAAG,IAAI,KAC/B,MASF,CAAC;AAEF,qBAAa,uBAAuB;IAEhC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE,uBAAuD;IAGnF,MAAM,GACJ,OAAO,4BAA4B,KAClC,6BAA6B,CAyD9B;IAEF,OAAO,CAAC,iBAAiB,CAuBvB;CACH"}
1
+ {"version":3,"file":"OauthTokenSelectHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,uBAAuB,EACxB,MAAM,kDAAkD,CAAC;AAQ1D,MAAM,MAAM,4BAA4B,GAAG;IACzC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB,CAAC;AAKF,eAAO,MAAM,wBAAwB,GACnC,cAAc,MAAM,GAAG,IAAI,KAC1B,MAAM,GAAG,IASX,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAChC,mBAAmB,MAAM,GAAG,IAAI,KAC/B,MASF,CAAC;AAEF,qBAAa,uBAAuB;IAEhC,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,GAAE,uBAAuD;IAGnF,MAAM,GACJ,OAAO,4BAA4B,KAClC,6BAA6B,CA4D9B;IAEF,OAAO,CAAC,iBAAiB,CAuBvB;CACH"}
@@ -1,6 +1,7 @@
1
1
  export type TokenEntry = {
2
2
  name: string;
3
3
  token: string;
4
+ selectionWeight?: number;
4
5
  };
5
6
  export declare const loadTokenEntries: (jsonPath: string) => TokenEntry[] | null;
6
7
  export declare const loadTokens: (jsonPath: string) => string[] | null;
@@ -1 +1 @@
1
- {"version":3,"file":"TokenListLoader.d.ts","sourceRoot":"","sources":["../../../src/adapter/proxy/TokenListLoader.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAAI,UAAU,MAAM,KAAG,UAAU,EAAE,GAAG,IA0BlE,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,UAAU,MAAM,KAAG,MAAM,EAAE,GAAG,IAIxD,CAAC"}
1
+ {"version":3,"file":"TokenListLoader.d.ts","sourceRoot":"","sources":["../../../src/adapter/proxy/TokenListLoader.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAYF,eAAO,MAAM,gBAAgB,GAAI,UAAU,MAAM,KAAG,UAAU,EAAE,GAAG,IAmClE,CAAC;AAEF,eAAO,MAAM,UAAU,GAAI,UAAU,MAAM,KAAG,MAAM,EAAE,GAAG,IAIxD,CAAC"}
@@ -9,5 +9,6 @@ export declare class NodeTmuxSessionRepository implements TmuxSessionRepository
9
9
  listInteractiveProcessCommandLines: () => Promise<string[]>;
10
10
  launchDetachedSession: (sessionName: string, launcherCommand: string, issueUrl: string) => Promise<void>;
11
11
  killSession: (sessionName: string) => Promise<void>;
12
+ private stopClSessionScope;
12
13
  }
13
14
  //# sourceMappingURL=NodeTmuxSessionRepository.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"NodeTmuxSessionRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/NodeTmuxSessionRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6DAA6D,CAAC;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,gEAAgE,CAAC;AACvG,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAExE,qBAAa,yBAA0B,YAAW,qBAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,kBAAkB;gBAAlB,kBAAkB,EAAE,kBAAkB;IAEnE,oBAAoB,QAAa,OAAO,CAAC,MAAM,EAAE,CAAC,CAYhD;IAEF,4BAA4B,QAAa,OAAO,CAAC,eAAe,EAAE,CAAC,CAkBjE;IAEF,kCAAkC,QAAa,OAAO,CAAC,MAAM,EAAE,CAAC,CAY9D;IAEF,qBAAqB,GACnB,aAAa,MAAM,EACnB,iBAAiB,MAAM,EACvB,UAAU,MAAM,KACf,OAAO,CAAC,IAAI,CAAC,CAcd;IAEF,WAAW,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CAYtD;CACH"}
1
+ {"version":3,"file":"NodeTmuxSessionRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/NodeTmuxSessionRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6DAA6D,CAAC;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,gEAAgE,CAAC;AACvG,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAGxE,qBAAa,yBAA0B,YAAW,qBAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,kBAAkB;gBAAlB,kBAAkB,EAAE,kBAAkB;IAEnE,oBAAoB,QAAa,OAAO,CAAC,MAAM,EAAE,CAAC,CAYhD;IAEF,4BAA4B,QAAa,OAAO,CAAC,eAAe,EAAE,CAAC,CAkBjE;IAEF,kCAAkC,QAAa,OAAO,CAAC,MAAM,EAAE,CAAC,CAY9D;IAEF,qBAAqB,GACnB,aAAa,MAAM,EACnB,iBAAiB,MAAM,EACvB,UAAU,MAAM,KACf,OAAO,CAAC,IAAI,CAAC,CAcd;IAEF,WAAW,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CAatD;IAEF,OAAO,CAAC,kBAAkB,CAuBxB;CACH"}
@@ -0,0 +1,2 @@
1
+ export declare const clSessionScopeUnitName: (sessionName: string) => string;
2
+ //# sourceMappingURL=clSessionScopeUnitName.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clSessionScopeUnitName.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/clSessionScopeUnitName.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,GAAI,aAAa,MAAM,KAAG,MAI5D,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { ClaudeLiveSession } from './adapter-interfaces/ClaudeLiveSessionRepository';
2
- import { OauthTokenCandidate, OauthTokenSelectUseCase } from './OauthTokenSelectUseCase';
2
+ import { OauthTokenCandidate, OauthTokenSelectUseCase, SelectionRandom } from './OauthTokenSelectUseCase';
3
3
  export type LiveSessionOauthTokenCandidateMetrics = {
4
4
  name: string;
5
5
  fiveHourFreeRatio: number;
@@ -16,7 +16,7 @@ export type LiveSessionOauthTokenSelectResult = {
16
16
  export declare class LiveSessionOauthTokenSelectUseCase {
17
17
  private readonly rateLimitSelectUseCase;
18
18
  constructor(rateLimitSelectUseCase?: OauthTokenSelectUseCase);
19
- run: (candidates: OauthTokenCandidate[], liveSessions: ClaudeLiveSession[], nowEpochSeconds: number) => LiveSessionOauthTokenSelectResult;
19
+ run: (candidates: OauthTokenCandidate[], liveSessions: ClaudeLiveSession[], nowEpochSeconds: number, random?: SelectionRandom) => LiveSessionOauthTokenSelectResult;
20
20
  private preferred;
21
21
  private liveSessionCountByToken;
22
22
  }
@@ -1 +1 @@
1
- {"version":3,"file":"LiveSessionOauthTokenSelectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AACrF,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACxB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,qCAAqC,GAAG;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC9C,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,OAAO,EAAE,qCAAqC,EAAE,CAAC;CAClD,CAAC;AAEF,qBAAa,kCAAkC;IAE3C,OAAO,CAAC,QAAQ,CAAC,sBAAsB;gBAAtB,sBAAsB,GAAE,uBAAuD;IAGlG,GAAG,GACD,YAAY,mBAAmB,EAAE,EACjC,cAAc,iBAAiB,EAAE,EACjC,iBAAiB,MAAM,KACtB,iCAAiC,CAuClC;IAEF,OAAO,CAAC,SAAS,CAUf;IAEF,OAAO,CAAC,uBAAuB,CAe7B;CACH"}
1
+ {"version":3,"file":"LiveSessionOauthTokenSelectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AACrF,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,eAAe,EAEhB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,qCAAqC,GAAG;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC9C,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,OAAO,EAAE,qCAAqC,EAAE,CAAC;CAClD,CAAC;AAEF,qBAAa,kCAAkC;IAE3C,OAAO,CAAC,QAAQ,CAAC,sBAAsB;gBAAtB,sBAAsB,GAAE,uBAAuD;IAGlG,GAAG,GACD,YAAY,mBAAmB,EAAE,EACjC,cAAc,iBAAiB,EAAE,EACjC,iBAAiB,MAAM,EACvB,SAAQ,eAA6B,KACpC,iCAAiC,CA+ClC;IAEF,OAAO,CAAC,SAAS,CAUf;IAEF,OAAO,CAAC,uBAAuB,CAe7B;CACH"}
@@ -11,7 +11,11 @@ export type OauthTokenCandidate = {
11
11
  subscriptionDisabled: boolean;
12
12
  unifiedRejected: boolean;
13
13
  fableRejected: boolean;
14
+ selectionWeight?: number;
14
15
  };
16
+ export type SelectionRandom = () => number;
17
+ export declare const DEFAULT_SELECTION_WEIGHT = 1;
18
+ export declare const selectionWeightOf: (candidate: OauthTokenCandidate) => number;
15
19
  export type OauthTokenCandidateMetrics = {
16
20
  name: string;
17
21
  fiveHourFreeRatio: number;
@@ -26,8 +30,9 @@ export type OauthTokenSelectResult = {
26
30
  };
27
31
  export declare const FIVE_HOUR_MIN_FREE_RATIO = 0.6;
28
32
  export declare const SEVEN_DAY_MIN_FREE_RATIO = 0.07;
33
+ export declare const selectWeightedCandidate: <Entry>(eligible: Entry[], candidateOf: (entry: Entry) => OauthTokenCandidate, deterministicBest: Entry, random: SelectionRandom) => Entry;
29
34
  export declare class OauthTokenSelectUseCase {
30
- run: (candidates: OauthTokenCandidate[], nowEpochSeconds: number) => OauthTokenSelectResult;
35
+ run: (candidates: OauthTokenCandidate[], nowEpochSeconds: number, random?: SelectionRandom) => OauthTokenSelectResult;
31
36
  private evaluate;
32
37
  private exclusionReason;
33
38
  private fiveHourFreeRatio;
@@ -1 +1 @@
1
- {"version":3,"file":"OauthTokenSelectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/OauthTokenSelectUseCase.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,wBAAwB,GAAG;IACrC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,wBAAwB,GAAG,IAAI,CAAC;IAC1C,oBAAoB,EAAE,OAAO,CAAC;IAC9B,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,OAAO,EAAE,0BAA0B,EAAE,CAAC;CACvC,CAAC;AAKF,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,qBAAa,uBAAuB;IAClC,GAAG,GACD,YAAY,mBAAmB,EAAE,EACjC,iBAAiB,MAAM,KACtB,sBAAsB,CAoBvB;IAEF,OAAO,CAAC,QAAQ,CAiCd;IAEF,OAAO,CAAC,eAAe,CAuBrB;IAEF,OAAO,CAAC,iBAAiB,CAWvB;IAEF,OAAO,CAAC,iBAAiB,CAWvB;IAEF,OAAO,CAAC,gBAAgB,CActB;IAEF,OAAO,CAAC,aAAa,CAGwC;IAE7D,OAAO,CAAC,wBAAwB,CAG9B;IAEF,OAAO,CAAC,SAAS,CAAsD;CACxE"}
1
+ {"version":3,"file":"OauthTokenSelectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/OauthTokenSelectUseCase.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,wBAAwB,GAAG;IACrC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,wBAAwB,GAAG,IAAI,CAAC;IAC1C,oBAAoB,EAAE,OAAO,CAAC;IAC9B,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,MAAM,MAAM,CAAC;AAE3C,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,eAAO,MAAM,iBAAiB,GAAI,WAAW,mBAAmB,KAAG,MAGlE,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,OAAO,EAAE,0BAA0B,EAAE,CAAC;CACvC,CAAC;AAKF,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,eAAO,MAAM,uBAAuB,GAAI,KAAK,EAC3C,UAAU,KAAK,EAAE,EACjB,aAAa,CAAC,KAAK,EAAE,KAAK,KAAK,mBAAmB,EAClD,mBAAmB,KAAK,EACxB,QAAQ,eAAe,KACtB,KAoBF,CAAC;AAEF,qBAAa,uBAAuB;IAClC,GAAG,GACD,YAAY,mBAAmB,EAAE,EACjC,iBAAiB,MAAM,EACvB,SAAQ,eAA6B,KACpC,sBAAsB,CA2BvB;IAEF,OAAO,CAAC,QAAQ,CAiCd;IAEF,OAAO,CAAC,eAAe,CAuBrB;IAEF,OAAO,CAAC,iBAAiB,CAWvB;IAEF,OAAO,CAAC,iBAAiB,CAWvB;IAEF,OAAO,CAAC,gBAAgB,CActB;IAEF,OAAO,CAAC,aAAa,CAGwC;IAE7D,OAAO,CAAC,wBAAwB,CAG9B;IAEF,OAAO,CAAC,SAAS,CAAsD;CACxE"}