github-issue-tower-defence-management 1.195.0 → 1.196.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.
- package/.github/workflows/codeql.yml +2 -2
- package/CHANGELOG.md +12 -0
- package/README.md +2 -2
- package/bin/adapter/entry-points/cli/fleetConfig.js +2 -0
- package/bin/adapter/entry-points/cli/fleetConfig.js.map +1 -1
- package/bin/adapter/entry-points/handlers/OauthTokenSelectHandler.js +1 -1
- package/bin/adapter/entry-points/handlers/OauthTokenSelectHandler.js.map +1 -1
- package/bin/domain/usecases/LiveSessionOauthTokenSelectUseCase.js +17 -2
- package/bin/domain/usecases/LiveSessionOauthTokenSelectUseCase.js.map +1 -1
- package/bin/domain/usecases/OauthTokenSelectUseCase.js +18 -10
- package/bin/domain/usecases/OauthTokenSelectUseCase.js.map +1 -1
- package/package.json +2 -2
- package/src/adapter/entry-points/cli/fleetConfig.test.ts +33 -1
- package/src/adapter/entry-points/cli/fleetConfig.ts +16 -0
- package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.test.ts +78 -0
- package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts +2 -0
- package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.test.ts +137 -17
- package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts +30 -2
- package/src/domain/usecases/OauthTokenSelectUseCase.test.ts +99 -0
- package/src/domain/usecases/OauthTokenSelectUseCase.ts +30 -5
- package/types/adapter/entry-points/cli/fleetConfig.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/OauthTokenSelectHandler.d.ts.map +1 -1
- package/types/domain/usecases/LiveSessionOauthTokenSelectUseCase.d.ts +3 -0
- package/types/domain/usecases/LiveSessionOauthTokenSelectUseCase.d.ts.map +1 -1
- package/types/domain/usecases/OauthTokenSelectUseCase.d.ts +7 -1
- package/types/domain/usecases/OauthTokenSelectUseCase.d.ts.map +1 -1
|
@@ -73,7 +73,7 @@ jobs:
|
|
|
73
73
|
|
|
74
74
|
# Initializes the CodeQL tools for scanning.
|
|
75
75
|
- name: Initialize CodeQL
|
|
76
|
-
uses: github/codeql-action/init@v4.37.
|
|
76
|
+
uses: github/codeql-action/init@v4.37.8
|
|
77
77
|
with:
|
|
78
78
|
languages: ${{ matrix.language }}
|
|
79
79
|
build-mode: ${{ matrix.build-mode }}
|
|
@@ -102,6 +102,6 @@ jobs:
|
|
|
102
102
|
exit 1
|
|
103
103
|
|
|
104
104
|
- name: Perform CodeQL Analysis
|
|
105
|
-
uses: github/codeql-action/analyze@v4.37.
|
|
105
|
+
uses: github/codeql-action/analyze@v4.37.8
|
|
106
106
|
with:
|
|
107
107
|
category: '/language:${{matrix.language}}'
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
# [1.196.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.195.0...v1.196.0) (2026-08-29)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **deps:** update dependency googleapis to v176 ([#1831](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1831)) ([b03e898](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/b03e898f85d90f918d31b1efdf2acd2587962d5e))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **select-token:** apply stricter token selection thresholds for CL scripts ([#1826](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1826)) ([6301773](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/63017733898c6336b8d0776fd2b7846b778d91bb)), closes [HiromiShikata/umino-corporait-operation#30998](https://github.com/HiromiShikata/umino-corporait-operation/issues/30998)
|
|
12
|
+
|
|
1
13
|
# [1.195.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.194.2...v1.195.0) (2026-08-29)
|
|
2
14
|
|
|
3
15
|
|
package/README.md
CHANGED
|
@@ -159,9 +159,9 @@ storyProgressCommentEnabled: false
|
|
|
159
159
|
|
|
160
160
|
The `checkIssueReviewReadiness` sub-command lets an agent self-check whether an issue is currently review-ready. It does NOT change the issue Status field and does NOT post any comment. It writes a single JSON line to stdout of the shape `{ "reviewReady": boolean, "rejections": [{ "type": string, "detail": string }] }` and exits 0 on a successful evaluation regardless of readiness; a non-zero exit indicates an operational error (auth failure, network error). The rejection types include: `ISSUE_NOT_FOUND`, `NO_REPORT_FROM_AGENT_BOT`, `REPORT_HAS_NEXT_STEP`, `PULL_REQUEST_NOT_FOUND`, `PULL_REQUEST_IS_DRAFT`, `PULL_REQUEST_CONFLICTED`, `ANY_CI_JOB_FAILED_OR_IN_PROGRESS`, `REQUIRED_CI_JOB_NEVER_STARTED`, `ANY_REVIEW_COMMENT_NOT_RESOLVED`, and `MULTIPLE_PULL_REQUESTS_FOUND`. The `--projectUrl` option is optional; when omitted the command still runs using only the issue URL.
|
|
161
161
|
|
|
162
|
-
The `selectOauthToken` sub-command reads the same per-token rate-limit cache that the `startDaemon` proxy writes (see "Claude OAuth Token Rotation" below) and prints exactly one token string to stdout so a caller can choose an appropriate token before launching Claude Code. It is read-only: it never starts the proxy, never mutates any cache file, and never writes the token anywhere. Selection runs in two stages. First, a candidate filter keeps tokens whose 5-hour window is at least
|
|
162
|
+
The `selectOauthToken` sub-command reads the same per-token rate-limit cache that the `startDaemon` proxy writes (see "Claude OAuth Token Rotation" below) and prints exactly one token string to stdout so a caller can choose an appropriate token before launching Claude Code. It is read-only: it never starts the proxy, never mutates any cache file, and never writes the token anywhere. Selection runs in two stages. First, a candidate filter keeps tokens whose 5-hour window is at least 60% free (5-hour utilization at most 0.40) AND whose 7-day window is at least 14% free (7-day utilization at most 0.86), where "% free" is `1 - utilization`. A token with no cache file, or whose window reset epoch has already passed, is treated as fully free (utilization 0) for these checks. The filter additionally excludes any token carrying a non-expired reactive `seven_day_fable` rejection marker — set when a `fable`-model request on that token was rejected with HTTP 429 (see "Claude OAuth Token Rotation" below) — because these interactive sessions run on the `fable` model; a token without the marker is treated as `fable`-usable, and the marker is ignored once its stored reset epoch has passed. Second, among the surviving candidates it selects the single token whose 7-day window reset epoch is nearest in the future (soonest reset), so weekly quota that would otherwise reset unused is consumed first; a candidate with no active 7-day window is treated as having the farthest reset (now + 7 days) and therefore sorts last. Each token entry may carry an optional `selectionWeight` (a positive number, default `1`); when the eligible candidates carry differing weights the selection among them becomes weighted-random by that weight, so a token with a smaller weight is chosen proportionally less often, while a token that is the only eligible candidate is always chosen regardless of its weight. When every eligible candidate shares the same weight (the default), the deterministic soonest-reset selection above is used unchanged. The selected token string is written to stdout (pipeable) and the per-candidate decision trace is written to stderr. When no token passes the filter, nothing is written to stdout and the command exits non-zero with an explanatory message on stderr. The token-list path comes from `--tokenListJsonPath` or the `CLAUDE_CODE_OAUTH_TOKEN_LIST_JSON_PATH` environment variable; the cache directory comes from `--cacheDir` or the `TDPM_RATELIMIT_CACHE_DIR` environment variable, defaulting to `${XDG_CACHE_HOME:-~/.cache}/tdpm/ratelimit`.
|
|
163
163
|
|
|
164
|
-
The `selectLiveSessionOauthToken` sub-command picks a token for a new live interactive Claude Code session a human is about to start, so that concurrent interactive sessions spread across distinct tokens instead of stacking onto the same one. It loads the token list and reads the same per-token rate-limit cache as `selectOauthToken`, and it is read-only in exactly the same way: it never starts the proxy, never mutates any cache file, and never writes the token anywhere. It applies
|
|
164
|
+
The `selectLiveSessionOauthToken` sub-command picks a token for a new live interactive Claude Code session a human is about to start, so that concurrent interactive sessions spread across distinct tokens instead of stacking onto the same one. It loads the token list and reads the same per-token rate-limit cache as `selectOauthToken`, and it is read-only in exactly the same way: it never starts the proxy, never mutates any cache file, and never writes the token anywhere. It applies a base rate-limit eligibility filter (5-hour window at least 25% free AND 7-day window at least 3% free, with a missing cache file or an expired window treated as fully free, and any token carrying a non-expired reactive `seven_day_fable` rejection marker excluded), and additionally excludes any token whose 5-hour window is less than `minFiveHourFreeRatio` (default 60%) free or whose 7-day window is less than `minSevenDayFreeRatio` (default 14%) free; these stricter thresholds prevent the `cl` script from starting a new interactive session on a token that is nearly exhausted. It additionally measures current live occupancy per token by scanning running Claude Code processes on the local Linux host: for each process under `/proc` it reads the NUL-separated `/proc/<pid>/environ` and, when the process is a Claude Code process, takes its `CLAUDE_CODE_OAUTH_TOKEN` and `CLAUDE_CODE_SESSION_ID`. A token's occupancy is the number of distinct `CLAUDE_CODE_SESSION_ID` values bound to it, so child processes that inherit one session id count once. Processes without `CLAUDE_CODE_OAUTH_TOKEN` (for example API-key sessions) are ignored, and a process whose environ cannot be read is skipped. Among the eligible tokens the selection is deterministic and reset-first. Each eligible token is given a concurrent session limit of `maxConcurrentSessionCount` (default `10`), scaled by its optional `selectionWeight` (a positive number, default `1`). That limit is held at its full value while the free share of the token's 5-hour window is at or above `fullSpeedFiveHourFreeRatio` (default `0.5`), and is tapered linearly in proportion to the free share below that point, never dropping under `1`. The 5-hour window is the only window that lowers the limit: it is the one that runs out within a single working stretch, and a session that hits it mid-flight has to be restarted, which costs more than it saves. The 7-day window never lowers the limit, so a weekly allowance that is about to expire is drained at full speed rather than discarded unused; the 7-day window is instead what orders the candidates. The token whose 7-day window resets soonest among those still under their limit is selected, so allowance that is about to expire is consumed before the reset discards it; ties are broken by the fewer live sessions. All four tuning numbers (`maxConcurrentSessionCount`, `fullSpeedFiveHourFreeRatio`, `minFiveHourFreeRatio`, `minSevenDayFreeRatio`) are read from the fleet-wide config file named by `--fleetConfigFilePath` or the `TDPM_FLEET_CONFIG` environment variable, under a `liveSessionOauthTokenSelection` mapping; a key the file omits keeps its built-in value, while an unreadable file or an out-of-range value is reported as an error rather than silently ignored. A token at its limit is skipped in favour of the next soonest-resetting token that still has room, and when every eligible token is at its limit the soonest-resetting one is returned rather than none. A token that is the only eligible candidate is always chosen regardless of its weight or occupancy. The selected token string is written to stdout (pipeable) and the per-candidate decision trace is written to stderr; when no token passes the filter, nothing is written to stdout and the command exits non-zero with an explanatory message on stderr. The token-list path and cache directory are resolved exactly as for `selectOauthToken`. Because occupancy is read from `/proc/<pid>/environ`, this sub-command is Linux-specific.
|
|
165
165
|
|
|
166
166
|
The `countInTmuxByHumanSessionsPerToken` sub-command reports, per Claude Code OAuth token, how many live interactive sessions running under that token belong to an issue that is currently in the GitHub Project Status `In Tmux by human`. It is read-only: it never starts the proxy, never mutates any cache file, and never writes any token anywhere. It enumerates live interactive sessions by scanning running processes on the local Linux host: for each process under `/proc` it reads the NUL-separated `/proc/<pid>/cmdline` and selects only processes launched for an interactive session, identified by a `--name <issue-url>` argument whose value is an HTTP or HTTPS URL; `Take ownership` background spawns (which have no `--name` argument) are excluded. For each selected process it reads `/proc/<pid>/environ` and takes its `CLAUDE_CODE_OAUTH_TOKEN` and `CLAUDE_CODE_SESSION_ID`; a process missing either is skipped, and a process whose files cannot be read is skipped. It then loads the project's issues with their Status (reusing the same on-disk issue cache the daemon uses), keeps only the sessions whose `--name` issue URL maps to an open issue in Status `In Tmux by human`, and counts the distinct `CLAUDE_CODE_SESSION_ID` values per token so child processes that inherit one session id count once. It writes one tab-separated line per token to stdout in the form `<tokenName>\t<count>` (the token name comes from the token list; the raw token value is never printed), and writes the per-run decision trace to stderr. The token-list path comes from `--tokenListJsonPath`, the `claudeCodeOauthTokenListJsonPath` config value, or the `CLAUDE_CODE_OAUTH_TOKEN_LIST_JSON_PATH` environment variable. Because occupancy is read from `/proc/<pid>/cmdline` and `/proc/<pid>/environ`, this sub-command is Linux-specific.
|
|
167
167
|
|
|
@@ -95,6 +95,8 @@ const loadLiveSessionOauthTokenSelectionSettings = (fleetConfigFilePath) => {
|
|
|
95
95
|
return {
|
|
96
96
|
maxConcurrentSessionCount: readBoundedNumber(section, 'maxConcurrentSessionCount', fleetConfigFilePath, LiveSessionOauthTokenSelectUseCase_1.DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.maxConcurrentSessionCount, (value) => Number.isInteger(value) && value >= 1, 'integer of at least 1'),
|
|
97
97
|
fullSpeedFiveHourFreeRatio: readBoundedNumber(section, 'fullSpeedFiveHourFreeRatio', fleetConfigFilePath, LiveSessionOauthTokenSelectUseCase_1.DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.fullSpeedFiveHourFreeRatio, (value) => value > 0 && value <= 1, 'above 0 and at most 1'),
|
|
98
|
+
minFiveHourFreeRatio: readBoundedNumber(section, 'minFiveHourFreeRatio', fleetConfigFilePath, LiveSessionOauthTokenSelectUseCase_1.DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.minFiveHourFreeRatio, (value) => value > 0 && value <= 1, 'above 0 and at most 1'),
|
|
99
|
+
minSevenDayFreeRatio: readBoundedNumber(section, 'minSevenDayFreeRatio', fleetConfigFilePath, LiveSessionOauthTokenSelectUseCase_1.DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.minSevenDayFreeRatio, (value) => value > 0 && value <= 1, 'above 0 and at most 1'),
|
|
98
100
|
};
|
|
99
101
|
};
|
|
100
102
|
exports.loadLiveSessionOauthTokenSelectionSettings = loadLiveSessionOauthTokenSelectionSettings;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleetConfig.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/cli/fleetConfig.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAwB;AACxB,uCAAyB;AACzB,oHAGqE;AAExD,QAAA,2CAA2C,GAAG,mBAAmB,CAAC;AAElE,QAAA,8CAA8C,GACzD,gCAAgC,CAAC;AAE5B,MAAM,0BAA0B,GAAG,CACxC,QAAuB,EACR,EAAE;IACjB,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,KAAK,EAAE,EAAE,CAAC;QACzC,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,MAAM,eAAe,GACnB,OAAO,CAAC,GAAG,CAAC,mDAA2C,CAAC,CAAC;IAC3D,IAAI,eAAe,KAAK,SAAS,IAAI,eAAe,KAAK,EAAE,EAAE,CAAC;QAC5D,OAAO,eAAe,CAAC;IACzB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAZW,QAAA,0BAA0B,8BAYrC;AAEF,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE,CACpE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAEvE,MAAM,WAAW,GAAG,CAClB,mBAA2B,EACK,EAAE;IAClC,MAAM,MAAM,GAAY,cAAI,CAAC,KAAK,CAChC,EAAE,CAAC,YAAY,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAC7C,CAAC;IACF,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QAC5C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,GAAG,mBAAmB,4CAA4C,CACnE,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,CAAC,sDAA8C,CAAC,CAAC;IACvE,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QAC9C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,GAAG,sDAA8C,OAAO,mBAAmB,qBAAqB,CACjG,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,CACxB,OAAgC,EAChC,GAAW,EACX,mBAA2B,EAC3B,QAAgB,EAChB,UAAsC,EACtC,WAAmB,EACX,EAAE;IACV,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,KAAK,CACb,GAAG,sDAA8C,IAAI,GAAG,OAAO,mBAAmB,qBAAqB,WAAW,GAAG,CACtH,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,GAAG,sDAA8C,IAAI,GAAG,OAAO,mBAAmB,qBAAqB,WAAW,eAAe,KAAK,GAAG,CAC1I,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC,CAAC;AAEK,MAAM,0CAA0C,GAAG,CACxD,mBAAkC,EACM,EAAE;IAC1C,IAAI,mBAAmB,KAAK,IAAI,EAAE,CAAC;QACjC,OAAO,wFAAmD,CAAC;IAC7D,CAAC;IACD,MAAM,OAAO,GAAG,WAAW,CAAC,mBAAmB,CAAC,CAAC;IACjD,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO,wFAAmD,CAAC;IAC7D,CAAC;IACD,OAAO;QACL,yBAAyB,EAAE,iBAAiB,CAC1C,OAAO,EACP,2BAA2B,EAC3B,mBAAmB,EACnB,wFAAmD,CAAC,yBAAyB,EAC7E,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAChD,uBAAuB,CACxB;QACD,0BAA0B,EAAE,iBAAiB,CAC3C,OAAO,EACP,4BAA4B,EAC5B,mBAAmB,EACnB,wFAAmD,CAAC,0BAA0B,EAC9E,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,IAAI,KAAK,IAAI,CAAC,EAClC,uBAAuB,CACxB;KACF,CAAC;AACJ,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"fleetConfig.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/cli/fleetConfig.ts"],"names":[],"mappings":"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"}
|
|
@@ -77,7 +77,7 @@ class OauthTokenSelectHandler {
|
|
|
77
77
|
selectionWeight: selectionWeight ?? OauthTokenSelectUseCase_1.DEFAULT_SELECTION_WEIGHT,
|
|
78
78
|
};
|
|
79
79
|
});
|
|
80
|
-
const result = this.useCase.run(candidates, input.nowEpochSeconds, this.random);
|
|
80
|
+
const result = this.useCase.run(candidates, input.nowEpochSeconds, this.random, OauthTokenSelectUseCase_1.CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS);
|
|
81
81
|
return {
|
|
82
82
|
selectedToken: result.selected?.token ?? null,
|
|
83
83
|
selectedName: result.selected?.name ?? null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OauthTokenSelectHandler.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts"],"names":[],"mappings":";;;AAAA,
|
|
1
|
+
{"version":3,"file":"OauthTokenSelectHandler.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,6 +5,8 @@ const OauthTokenSelectUseCase_1 = require("./OauthTokenSelectUseCase");
|
|
|
5
5
|
exports.DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS = {
|
|
6
6
|
maxConcurrentSessionCount: 10,
|
|
7
7
|
fullSpeedFiveHourFreeRatio: 0.5,
|
|
8
|
+
minFiveHourFreeRatio: 0.6,
|
|
9
|
+
minSevenDayFreeRatio: 0.14,
|
|
8
10
|
};
|
|
9
11
|
const liveSessionConcurrentLimitOf = (fiveHourFreeRatio, selectionWeight, settings) => {
|
|
10
12
|
const fiveHourThrottleFactor = Math.min(fiveHourFreeRatio / settings.fullSpeedFiveHourFreeRatio, 1);
|
|
@@ -23,6 +25,7 @@ class LiveSessionOauthTokenSelectUseCase {
|
|
|
23
25
|
const rateLimitMetric = rateLimitResult.metrics[index];
|
|
24
26
|
const liveSessionCount = liveSessionCountByToken.get(candidate.token) ?? 0;
|
|
25
27
|
const concurrentSessionLimit = (0, exports.liveSessionConcurrentLimitOf)(rateLimitMetric.fiveHourFreeRatio, (0, OauthTokenSelectUseCase_1.selectionWeightOf)(candidate), settings);
|
|
28
|
+
const exclusionReason = this.liveSessionExclusionReason(rateLimitMetric.exclusionReason, rateLimitMetric.fiveHourFreeRatio, rateLimitMetric.sevenDayFreeRatio, settings);
|
|
26
29
|
return {
|
|
27
30
|
candidate,
|
|
28
31
|
metric: {
|
|
@@ -33,8 +36,8 @@ class LiveSessionOauthTokenSelectUseCase {
|
|
|
33
36
|
liveSessionCount,
|
|
34
37
|
concurrentSessionLimit,
|
|
35
38
|
hasConcurrencyHeadroom: liveSessionCount < concurrentSessionLimit,
|
|
36
|
-
eligible:
|
|
37
|
-
exclusionReason
|
|
39
|
+
eligible: exclusionReason === null,
|
|
40
|
+
exclusionReason,
|
|
38
41
|
selectionWeight: (0, OauthTokenSelectUseCase_1.selectionWeightOf)(candidate),
|
|
39
42
|
},
|
|
40
43
|
};
|
|
@@ -49,6 +52,18 @@ class LiveSessionOauthTokenSelectUseCase {
|
|
|
49
52
|
: bestEntry);
|
|
50
53
|
return { selected: selected.candidate, metrics };
|
|
51
54
|
};
|
|
55
|
+
this.liveSessionExclusionReason = (rateLimitExclusionReason, fiveHourFreeRatio, sevenDayFreeRatio, settings) => {
|
|
56
|
+
if (rateLimitExclusionReason !== null) {
|
|
57
|
+
return rateLimitExclusionReason;
|
|
58
|
+
}
|
|
59
|
+
if (fiveHourFreeRatio < settings.minFiveHourFreeRatio) {
|
|
60
|
+
return `5h window only ${Math.round(fiveHourFreeRatio * 100)}% free (requires >= ${Math.round(settings.minFiveHourFreeRatio * 100)}% for live session selection)`;
|
|
61
|
+
}
|
|
62
|
+
if (sevenDayFreeRatio < settings.minSevenDayFreeRatio) {
|
|
63
|
+
return `7d window only ${Math.round(sevenDayFreeRatio * 100)}% free (requires >= ${Math.round(settings.minSevenDayFreeRatio * 100)}% for live session selection)`;
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
};
|
|
52
67
|
this.preferred = (candidateMetric, incumbentMetric) => {
|
|
53
68
|
if (candidateMetric.hasConcurrencyHeadroom !==
|
|
54
69
|
incumbentMetric.hasConcurrencyHeadroom) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveSessionOauthTokenSelectUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts"],"names":[],"mappings":";;;AACA,uEAImC;
|
|
1
|
+
{"version":3,"file":"LiveSessionOauthTokenSelectUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.OauthTokenSelectUseCase = exports.selectWeightedCandidate = exports.sevenDayUrgencyFactor = exports.MIN_HOURS_TO_RESET = exports.SEVEN_DAY_WINDOW_HOURS = exports.SEVEN_DAY_MIN_FREE_RATIO = exports.FIVE_HOUR_MIN_FREE_RATIO = exports.selectionWeightOf = exports.DEFAULT_SELECTION_WEIGHT = void 0;
|
|
3
|
+
exports.OauthTokenSelectUseCase = exports.selectWeightedCandidate = exports.sevenDayUrgencyFactor = exports.MIN_HOURS_TO_RESET = exports.SEVEN_DAY_WINDOW_HOURS = exports.CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS = exports.DEFAULT_OAUTH_TOKEN_SELECTION_THRESHOLDS = exports.SEVEN_DAY_MIN_FREE_RATIO = exports.FIVE_HOUR_MIN_FREE_RATIO = exports.selectionWeightOf = exports.DEFAULT_SELECTION_WEIGHT = void 0;
|
|
4
4
|
exports.DEFAULT_SELECTION_WEIGHT = 1;
|
|
5
5
|
const selectionWeightOf = (candidate) => {
|
|
6
6
|
const weight = candidate.selectionWeight ?? exports.DEFAULT_SELECTION_WEIGHT;
|
|
@@ -11,6 +11,14 @@ const SECONDS_PER_DAY = 86400;
|
|
|
11
11
|
const SEVEN_DAYS_IN_SECONDS = 7 * SECONDS_PER_DAY;
|
|
12
12
|
exports.FIVE_HOUR_MIN_FREE_RATIO = 0.25;
|
|
13
13
|
exports.SEVEN_DAY_MIN_FREE_RATIO = 0.03;
|
|
14
|
+
exports.DEFAULT_OAUTH_TOKEN_SELECTION_THRESHOLDS = {
|
|
15
|
+
fiveHourMinFreeRatio: exports.FIVE_HOUR_MIN_FREE_RATIO,
|
|
16
|
+
sevenDayMinFreeRatio: exports.SEVEN_DAY_MIN_FREE_RATIO,
|
|
17
|
+
};
|
|
18
|
+
exports.CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS = {
|
|
19
|
+
fiveHourMinFreeRatio: 0.6,
|
|
20
|
+
sevenDayMinFreeRatio: 0.14,
|
|
21
|
+
};
|
|
14
22
|
exports.SEVEN_DAY_WINDOW_HOURS = 168;
|
|
15
23
|
exports.MIN_HOURS_TO_RESET = 1;
|
|
16
24
|
const SECONDS_PER_HOUR = 3600;
|
|
@@ -41,9 +49,9 @@ const selectWeightedCandidate = (eligible, weightOf, deterministicBest, random)
|
|
|
41
49
|
exports.selectWeightedCandidate = selectWeightedCandidate;
|
|
42
50
|
class OauthTokenSelectUseCase {
|
|
43
51
|
constructor() {
|
|
44
|
-
this.run = (candidates, nowEpochSeconds, random = Math.random) => {
|
|
52
|
+
this.run = (candidates, nowEpochSeconds, random = Math.random, thresholds = exports.DEFAULT_OAUTH_TOKEN_SELECTION_THRESHOLDS) => {
|
|
45
53
|
const evaluated = candidates.map((candidate) => {
|
|
46
|
-
const evaluatedMetric = this.evaluate(candidate, nowEpochSeconds);
|
|
54
|
+
const evaluatedMetric = this.evaluate(candidate, nowEpochSeconds, thresholds);
|
|
47
55
|
return {
|
|
48
56
|
candidate,
|
|
49
57
|
metric: {
|
|
@@ -66,11 +74,11 @@ class OauthTokenSelectUseCase {
|
|
|
66
74
|
const selected = (0, exports.selectWeightedCandidate)(eligible, (entry) => entry.metric.drawWeight, deterministicBest, random);
|
|
67
75
|
return { selected: selected.candidate, metrics };
|
|
68
76
|
};
|
|
69
|
-
this.evaluate = (candidate, nowEpochSeconds) => {
|
|
77
|
+
this.evaluate = (candidate, nowEpochSeconds, thresholds) => {
|
|
70
78
|
const fiveHourFreeRatio = this.fiveHourFreeRatio(candidate.snapshot, nowEpochSeconds);
|
|
71
79
|
const sevenDayFreeRatio = this.sevenDayFreeRatio(candidate.snapshot, nowEpochSeconds);
|
|
72
80
|
const sevenDayEndEpoch = this.sevenDayEndEpoch(candidate.snapshot, nowEpochSeconds);
|
|
73
|
-
const exclusionReason = this.exclusionReason(candidate.subscriptionDisabled, candidate.unifiedRejected, candidate.fableRejected, fiveHourFreeRatio, sevenDayFreeRatio);
|
|
81
|
+
const exclusionReason = this.exclusionReason(candidate.subscriptionDisabled, candidate.unifiedRejected, candidate.fableRejected, fiveHourFreeRatio, sevenDayFreeRatio, thresholds);
|
|
74
82
|
return {
|
|
75
83
|
name: candidate.name,
|
|
76
84
|
fiveHourFreeRatio,
|
|
@@ -80,7 +88,7 @@ class OauthTokenSelectUseCase {
|
|
|
80
88
|
exclusionReason,
|
|
81
89
|
};
|
|
82
90
|
};
|
|
83
|
-
this.exclusionReason = (subscriptionDisabled, unifiedRejected, fableRejected, fiveHourFreeRatio, sevenDayFreeRatio) => {
|
|
91
|
+
this.exclusionReason = (subscriptionDisabled, unifiedRejected, fableRejected, fiveHourFreeRatio, sevenDayFreeRatio, thresholds) => {
|
|
84
92
|
if (subscriptionDisabled) {
|
|
85
93
|
return 'organization has disabled Claude subscription access for Claude Code';
|
|
86
94
|
}
|
|
@@ -90,11 +98,11 @@ class OauthTokenSelectUseCase {
|
|
|
90
98
|
if (fableRejected) {
|
|
91
99
|
return 'fable weekly limit exhausted (a fable request was rejected with HTTP 429)';
|
|
92
100
|
}
|
|
93
|
-
if (fiveHourFreeRatio <
|
|
94
|
-
return `5h window only ${this.toPercent(fiveHourFreeRatio)}% free (requires >= ${this.toPercent(
|
|
101
|
+
if (fiveHourFreeRatio < thresholds.fiveHourMinFreeRatio) {
|
|
102
|
+
return `5h window only ${this.toPercent(fiveHourFreeRatio)}% free (requires >= ${this.toPercent(thresholds.fiveHourMinFreeRatio)}%)`;
|
|
95
103
|
}
|
|
96
|
-
if (sevenDayFreeRatio <
|
|
97
|
-
return `7d window only ${this.toPercent(sevenDayFreeRatio)}% free (requires >= ${this.toPercent(
|
|
104
|
+
if (sevenDayFreeRatio < thresholds.sevenDayMinFreeRatio) {
|
|
105
|
+
return `7d window only ${this.toPercent(sevenDayFreeRatio)}% free (requires >= ${this.toPercent(thresholds.sevenDayMinFreeRatio)}%)`;
|
|
98
106
|
}
|
|
99
107
|
return null;
|
|
100
108
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OauthTokenSelectUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/OauthTokenSelectUseCase.ts"],"names":[],"mappings":";;;AAmBa,QAAA,wBAAwB,GAAG,CAAC,CAAC;AAEnC,MAAM,iBAAiB,GAAG,CAAC,SAA8B,EAAU,EAAE;IAC1E,MAAM,MAAM,GAAG,SAAS,CAAC,eAAe,IAAI,gCAAwB,CAAC;IACrE,OAAO,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACjC,CAAC,CAAC;AAHW,QAAA,iBAAiB,qBAG5B;
|
|
1
|
+
{"version":3,"file":"OauthTokenSelectUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/OauthTokenSelectUseCase.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "github-issue-tower-defence-management",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.196.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "bin/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
"dompurify": "3.4.14",
|
|
116
116
|
"dotenv": "^17.4.2",
|
|
117
117
|
"gemoji": "8.1.0",
|
|
118
|
-
"googleapis": "^
|
|
118
|
+
"googleapis": "^176.0.0",
|
|
119
119
|
"ky": "^2.0.2",
|
|
120
120
|
"marked": "18.0.10",
|
|
121
121
|
"marked-emoji": "2.0.3",
|
|
@@ -67,13 +67,15 @@ describe('loadLiveSessionOauthTokenSelectionSettings', () => {
|
|
|
67
67
|
);
|
|
68
68
|
});
|
|
69
69
|
|
|
70
|
-
it('reads
|
|
70
|
+
it('reads all tuning numbers from the fleet config file', () => {
|
|
71
71
|
const fleetConfigFilePath = writeFleetConfig(
|
|
72
72
|
[
|
|
73
73
|
'inTmuxLauncherCommand: cl',
|
|
74
74
|
'liveSessionOauthTokenSelection:',
|
|
75
75
|
' maxConcurrentSessionCount: 16',
|
|
76
76
|
' fullSpeedFiveHourFreeRatio: 0.4',
|
|
77
|
+
' minFiveHourFreeRatio: 0.7',
|
|
78
|
+
' minSevenDayFreeRatio: 0.2',
|
|
77
79
|
].join('\n'),
|
|
78
80
|
);
|
|
79
81
|
|
|
@@ -82,6 +84,8 @@ describe('loadLiveSessionOauthTokenSelectionSettings', () => {
|
|
|
82
84
|
).toEqual({
|
|
83
85
|
maxConcurrentSessionCount: 16,
|
|
84
86
|
fullSpeedFiveHourFreeRatio: 0.4,
|
|
87
|
+
minFiveHourFreeRatio: 0.7,
|
|
88
|
+
minSevenDayFreeRatio: 0.2,
|
|
85
89
|
});
|
|
86
90
|
});
|
|
87
91
|
|
|
@@ -99,6 +103,10 @@ describe('loadLiveSessionOauthTokenSelectionSettings', () => {
|
|
|
99
103
|
maxConcurrentSessionCount: 16,
|
|
100
104
|
fullSpeedFiveHourFreeRatio:
|
|
101
105
|
DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.fullSpeedFiveHourFreeRatio,
|
|
106
|
+
minFiveHourFreeRatio:
|
|
107
|
+
DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.minFiveHourFreeRatio,
|
|
108
|
+
minSevenDayFreeRatio:
|
|
109
|
+
DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.minSevenDayFreeRatio,
|
|
102
110
|
});
|
|
103
111
|
});
|
|
104
112
|
|
|
@@ -152,6 +160,30 @@ describe('loadLiveSessionOauthTokenSelectionSettings', () => {
|
|
|
152
160
|
).toThrow('fullSpeedFiveHourFreeRatio');
|
|
153
161
|
});
|
|
154
162
|
|
|
163
|
+
it('throws when the minimum five hour free ratio is above one', () => {
|
|
164
|
+
const fleetConfigFilePath = writeFleetConfig(
|
|
165
|
+
['liveSessionOauthTokenSelection:', ' minFiveHourFreeRatio: 1.5'].join(
|
|
166
|
+
'\n',
|
|
167
|
+
),
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
expect(() =>
|
|
171
|
+
loadLiveSessionOauthTokenSelectionSettings(fleetConfigFilePath),
|
|
172
|
+
).toThrow('minFiveHourFreeRatio');
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it('throws when the minimum seven day free ratio is above one', () => {
|
|
176
|
+
const fleetConfigFilePath = writeFleetConfig(
|
|
177
|
+
['liveSessionOauthTokenSelection:', ' minSevenDayFreeRatio: 1.5'].join(
|
|
178
|
+
'\n',
|
|
179
|
+
),
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
expect(() =>
|
|
183
|
+
loadLiveSessionOauthTokenSelectionSettings(fleetConfigFilePath),
|
|
184
|
+
).toThrow('minSevenDayFreeRatio');
|
|
185
|
+
});
|
|
186
|
+
|
|
155
187
|
it('throws when a tuning number is written as a string', () => {
|
|
156
188
|
const fleetConfigFilePath = writeFleetConfig(
|
|
157
189
|
[
|
|
@@ -105,5 +105,21 @@ export const loadLiveSessionOauthTokenSelectionSettings = (
|
|
|
105
105
|
(value) => value > 0 && value <= 1,
|
|
106
106
|
'above 0 and at most 1',
|
|
107
107
|
),
|
|
108
|
+
minFiveHourFreeRatio: readBoundedNumber(
|
|
109
|
+
section,
|
|
110
|
+
'minFiveHourFreeRatio',
|
|
111
|
+
fleetConfigFilePath,
|
|
112
|
+
DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.minFiveHourFreeRatio,
|
|
113
|
+
(value) => value > 0 && value <= 1,
|
|
114
|
+
'above 0 and at most 1',
|
|
115
|
+
),
|
|
116
|
+
minSevenDayFreeRatio: readBoundedNumber(
|
|
117
|
+
section,
|
|
118
|
+
'minSevenDayFreeRatio',
|
|
119
|
+
fleetConfigFilePath,
|
|
120
|
+
DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS.minSevenDayFreeRatio,
|
|
121
|
+
(value) => value > 0 && value <= 1,
|
|
122
|
+
'above 0 and at most 1',
|
|
123
|
+
),
|
|
108
124
|
};
|
|
109
125
|
};
|
|
@@ -394,6 +394,84 @@ describe('OauthTokenSelectHandler', () => {
|
|
|
394
394
|
expect(Number(drawWeightMatch?.[1])).toBeGreaterThan(0);
|
|
395
395
|
});
|
|
396
396
|
|
|
397
|
+
it('excludes a token whose 5h window has only 59% free (CL script threshold is 60%)', () => {
|
|
398
|
+
writeTokenList([{ name: 'low5h', token: 'fake-low5h' }]);
|
|
399
|
+
writeCache('fake-low5h', {
|
|
400
|
+
fiveHourUtilization: 0.41,
|
|
401
|
+
fiveHourReset: NOW + HOUR,
|
|
402
|
+
sevenDayUtilization: 0.1,
|
|
403
|
+
sevenDayReset: NOW + DAY,
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
const handler = new OauthTokenSelectHandler();
|
|
407
|
+
const output = handler.handle({
|
|
408
|
+
tokenListJsonPath: tokenListPath,
|
|
409
|
+
cacheDirectory,
|
|
410
|
+
nowEpochSeconds: NOW,
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
expect(output.selectedToken).toBeNull();
|
|
414
|
+
expect(output.diagnostics.join('\n')).toContain('5h window');
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it('treats exactly 60% 5h free as eligible in the handler (CL script boundary)', () => {
|
|
418
|
+
writeTokenList([{ name: 'boundary5h', token: 'fake-boundary5h' }]);
|
|
419
|
+
writeCache('fake-boundary5h', {
|
|
420
|
+
fiveHourUtilization: 0.4,
|
|
421
|
+
fiveHourReset: NOW + HOUR,
|
|
422
|
+
sevenDayUtilization: 0.1,
|
|
423
|
+
sevenDayReset: NOW + DAY,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
const handler = new OauthTokenSelectHandler();
|
|
427
|
+
const output = handler.handle({
|
|
428
|
+
tokenListJsonPath: tokenListPath,
|
|
429
|
+
cacheDirectory,
|
|
430
|
+
nowEpochSeconds: NOW,
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
expect(output.selectedName).toBe('boundary5h');
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
it('excludes a token whose 7d window has only 13% free (CL script threshold is 14%)', () => {
|
|
437
|
+
writeTokenList([{ name: 'low7d', token: 'fake-low7d' }]);
|
|
438
|
+
writeCache('fake-low7d', {
|
|
439
|
+
fiveHourUtilization: 0.1,
|
|
440
|
+
fiveHourReset: NOW + HOUR,
|
|
441
|
+
sevenDayUtilization: 0.87,
|
|
442
|
+
sevenDayReset: NOW + DAY,
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
const handler = new OauthTokenSelectHandler();
|
|
446
|
+
const output = handler.handle({
|
|
447
|
+
tokenListJsonPath: tokenListPath,
|
|
448
|
+
cacheDirectory,
|
|
449
|
+
nowEpochSeconds: NOW,
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
expect(output.selectedToken).toBeNull();
|
|
453
|
+
expect(output.diagnostics.join('\n')).toContain('7d window');
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
it('treats exactly 14% 7d free as eligible in the handler (CL script boundary)', () => {
|
|
457
|
+
writeTokenList([{ name: 'boundary7d', token: 'fake-boundary7d' }]);
|
|
458
|
+
writeCache('fake-boundary7d', {
|
|
459
|
+
fiveHourUtilization: 0.1,
|
|
460
|
+
fiveHourReset: NOW + HOUR,
|
|
461
|
+
sevenDayUtilization: 0.86,
|
|
462
|
+
sevenDayReset: NOW + DAY,
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
const handler = new OauthTokenSelectHandler();
|
|
466
|
+
const output = handler.handle({
|
|
467
|
+
tokenListJsonPath: tokenListPath,
|
|
468
|
+
cacheDirectory,
|
|
469
|
+
nowEpochSeconds: NOW,
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
expect(output.selectedName).toBe('boundary7d');
|
|
473
|
+
});
|
|
474
|
+
|
|
397
475
|
describe('resolveTokenListJsonPath', () => {
|
|
398
476
|
it('prefers the explicit path over the environment variable', () => {
|
|
399
477
|
process.env.CLAUDE_CODE_OAUTH_TOKEN_LIST_JSON_PATH = '/from/env.json';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
|
|
2
3
|
DEFAULT_SELECTION_WEIGHT,
|
|
3
4
|
type OauthTokenCandidate,
|
|
4
5
|
type OauthTokenSelectResult,
|
|
@@ -118,6 +119,7 @@ export class OauthTokenSelectHandler {
|
|
|
118
119
|
candidates,
|
|
119
120
|
input.nowEpochSeconds,
|
|
120
121
|
this.random,
|
|
122
|
+
CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
|
|
121
123
|
);
|
|
122
124
|
|
|
123
125
|
return {
|
|
@@ -145,11 +145,11 @@ describe('LiveSessionOauthTokenSelectUseCase', () => {
|
|
|
145
145
|
expect(result.selected?.name).toBe('distantResetIdle');
|
|
146
146
|
});
|
|
147
147
|
|
|
148
|
-
it('
|
|
148
|
+
it('excludes a seven day window that has fallen below the minimum free ratio even when sessions are below the concurrent limit', () => {
|
|
149
149
|
const result = useCase.run(
|
|
150
150
|
[
|
|
151
151
|
candidate(
|
|
152
|
-
'
|
|
152
|
+
'nearlyUsedSevenDay',
|
|
153
153
|
snapshot({
|
|
154
154
|
sevenDayReset: NOW + 3 * DAY,
|
|
155
155
|
sevenDayUtilization: 0.9,
|
|
@@ -160,29 +160,27 @@ describe('LiveSessionOauthTokenSelectUseCase', () => {
|
|
|
160
160
|
snapshot({ sevenDayReset: NOW + 6 * DAY }),
|
|
161
161
|
),
|
|
162
162
|
],
|
|
163
|
-
sessionsFor('
|
|
163
|
+
sessionsFor('nearlyUsedSevenDay', 5),
|
|
164
164
|
NOW,
|
|
165
165
|
SETTINGS,
|
|
166
166
|
);
|
|
167
167
|
|
|
168
168
|
const nearlyUsed = result.metrics.find(
|
|
169
|
-
(m) => m.name === '
|
|
169
|
+
(m) => m.name === 'nearlyUsedSevenDay',
|
|
170
170
|
);
|
|
171
|
-
expect(nearlyUsed?.
|
|
172
|
-
|
|
173
|
-
);
|
|
174
|
-
expect(result.selected?.name).toBe('soonResetNearlyUsedSevenDay');
|
|
171
|
+
expect(nearlyUsed?.eligible).toBe(false);
|
|
172
|
+
expect(nearlyUsed?.exclusionReason).toContain('7d window');
|
|
173
|
+
expect(result.selected?.name).toBe('distantResetIdle');
|
|
175
174
|
});
|
|
176
175
|
|
|
177
|
-
it('
|
|
176
|
+
it('excludes a token whose seven day window is below the minimum even when it resets within the hour', () => {
|
|
178
177
|
const result = useCase.run(
|
|
179
178
|
[
|
|
180
179
|
candidate(
|
|
181
|
-
'
|
|
180
|
+
'aboutToResetNearlyUsedSevenDay',
|
|
182
181
|
snapshot({
|
|
183
182
|
sevenDayReset: NOW + HOUR,
|
|
184
183
|
sevenDayUtilization: 0.9,
|
|
185
|
-
fiveHourUtilization: 0.5,
|
|
186
184
|
}),
|
|
187
185
|
),
|
|
188
186
|
candidate(
|
|
@@ -190,18 +188,16 @@ describe('LiveSessionOauthTokenSelectUseCase', () => {
|
|
|
190
188
|
snapshot({ sevenDayReset: NOW + 6 * DAY }),
|
|
191
189
|
),
|
|
192
190
|
],
|
|
193
|
-
sessionsFor('
|
|
191
|
+
sessionsFor('aboutToResetNearlyUsedSevenDay', 5),
|
|
194
192
|
NOW,
|
|
195
193
|
SETTINGS,
|
|
196
194
|
);
|
|
197
195
|
|
|
198
196
|
const aboutToReset = result.metrics.find(
|
|
199
|
-
(m) => m.name === '
|
|
197
|
+
(m) => m.name === 'aboutToResetNearlyUsedSevenDay',
|
|
200
198
|
);
|
|
201
|
-
expect(aboutToReset?.
|
|
202
|
-
|
|
203
|
-
);
|
|
204
|
-
expect(result.selected?.name).toBe('aboutToResetHalfFreeFiveHour');
|
|
199
|
+
expect(aboutToReset?.eligible).toBe(false);
|
|
200
|
+
expect(result.selected?.name).toBe('distantResetIdle');
|
|
205
201
|
});
|
|
206
202
|
|
|
207
203
|
it('still throttles a seven day window that resets within the hour once its five hour window falls below half free', () => {
|
|
@@ -509,6 +505,130 @@ describe('LiveSessionOauthTokenCandidateMetrics selectionWeight', () => {
|
|
|
509
505
|
});
|
|
510
506
|
});
|
|
511
507
|
|
|
508
|
+
describe('LiveSessionOauthTokenSelectUseCase minimum free ratio thresholds', () => {
|
|
509
|
+
const useCase = new LiveSessionOauthTokenSelectUseCase();
|
|
510
|
+
|
|
511
|
+
it('excludes a token whose five hour window has less than the minimum free ratio', () => {
|
|
512
|
+
const result = useCase.run(
|
|
513
|
+
[
|
|
514
|
+
candidate('narrowFiveHourMin', snapshot({ fiveHourUtilization: 0.5 })),
|
|
515
|
+
candidate('freeFiveHourMin', snapshot({})),
|
|
516
|
+
],
|
|
517
|
+
[],
|
|
518
|
+
NOW,
|
|
519
|
+
SETTINGS,
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
const narrow = result.metrics.find((m) => m.name === 'narrowFiveHourMin');
|
|
523
|
+
expect(narrow?.eligible).toBe(false);
|
|
524
|
+
expect(narrow?.exclusionReason).toContain('5h window');
|
|
525
|
+
expect(result.selected?.name).toBe('freeFiveHourMin');
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
it('excludes a token whose seven day window has less than the minimum free ratio', () => {
|
|
529
|
+
const result = useCase.run(
|
|
530
|
+
[
|
|
531
|
+
candidate(
|
|
532
|
+
'nearlyUsedSevenDayMin',
|
|
533
|
+
snapshot({ sevenDayUtilization: 0.9 }),
|
|
534
|
+
),
|
|
535
|
+
candidate('freeSevenDayMin', snapshot({})),
|
|
536
|
+
],
|
|
537
|
+
[],
|
|
538
|
+
NOW,
|
|
539
|
+
SETTINGS,
|
|
540
|
+
);
|
|
541
|
+
|
|
542
|
+
const nearlyUsed = result.metrics.find(
|
|
543
|
+
(m) => m.name === 'nearlyUsedSevenDayMin',
|
|
544
|
+
);
|
|
545
|
+
expect(nearlyUsed?.eligible).toBe(false);
|
|
546
|
+
expect(nearlyUsed?.exclusionReason).toContain('7d window');
|
|
547
|
+
expect(result.selected?.name).toBe('freeSevenDayMin');
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
it('selects a token whose ratios are at exactly the minimum free ratio thresholds', () => {
|
|
551
|
+
const result = useCase.run(
|
|
552
|
+
[
|
|
553
|
+
candidate(
|
|
554
|
+
'atThreshold',
|
|
555
|
+
snapshot({ fiveHourUtilization: 0.4, sevenDayUtilization: 0.86 }),
|
|
556
|
+
),
|
|
557
|
+
],
|
|
558
|
+
[],
|
|
559
|
+
NOW,
|
|
560
|
+
SETTINGS,
|
|
561
|
+
);
|
|
562
|
+
|
|
563
|
+
const atThreshold = result.metrics.find((m) => m.name === 'atThreshold');
|
|
564
|
+
expect(atThreshold?.eligible).toBe(true);
|
|
565
|
+
expect(result.selected?.name).toBe('atThreshold');
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
it('honours a fleet supplied minimum five hour free ratio', () => {
|
|
569
|
+
const result = useCase.run(
|
|
570
|
+
[
|
|
571
|
+
candidate(
|
|
572
|
+
'narrowForFleetFiveHour',
|
|
573
|
+
snapshot({ fiveHourUtilization: 0.15 }),
|
|
574
|
+
),
|
|
575
|
+
candidate('freeForFleetFiveHour', snapshot({})),
|
|
576
|
+
],
|
|
577
|
+
[],
|
|
578
|
+
NOW,
|
|
579
|
+
settingsWith({ minFiveHourFreeRatio: 0.9 }),
|
|
580
|
+
);
|
|
581
|
+
|
|
582
|
+
const narrow = result.metrics.find(
|
|
583
|
+
(m) => m.name === 'narrowForFleetFiveHour',
|
|
584
|
+
);
|
|
585
|
+
expect(narrow?.eligible).toBe(false);
|
|
586
|
+
expect(narrow?.exclusionReason).toContain('5h window');
|
|
587
|
+
expect(result.selected?.name).toBe('freeForFleetFiveHour');
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
it('honours a fleet supplied minimum seven day free ratio', () => {
|
|
591
|
+
const result = useCase.run(
|
|
592
|
+
[
|
|
593
|
+
candidate(
|
|
594
|
+
'narrowForFleetSevenDay',
|
|
595
|
+
snapshot({ sevenDayUtilization: 0.5 }),
|
|
596
|
+
),
|
|
597
|
+
candidate('freeForFleetSevenDay', snapshot({})),
|
|
598
|
+
],
|
|
599
|
+
[],
|
|
600
|
+
NOW,
|
|
601
|
+
settingsWith({ minSevenDayFreeRatio: 0.6 }),
|
|
602
|
+
);
|
|
603
|
+
|
|
604
|
+
const narrow = result.metrics.find(
|
|
605
|
+
(m) => m.name === 'narrowForFleetSevenDay',
|
|
606
|
+
);
|
|
607
|
+
expect(narrow?.eligible).toBe(false);
|
|
608
|
+
expect(narrow?.exclusionReason).toContain('7d window');
|
|
609
|
+
expect(result.selected?.name).toBe('freeForFleetSevenDay');
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
it('reports the five hour window exclusion reason when both windows are below their minimums', () => {
|
|
613
|
+
const result = useCase.run(
|
|
614
|
+
[
|
|
615
|
+
candidate(
|
|
616
|
+
'bothNarrow',
|
|
617
|
+
snapshot({ fiveHourUtilization: 0.5, sevenDayUtilization: 0.9 }),
|
|
618
|
+
),
|
|
619
|
+
],
|
|
620
|
+
[],
|
|
621
|
+
NOW,
|
|
622
|
+
SETTINGS,
|
|
623
|
+
);
|
|
624
|
+
|
|
625
|
+
const bothNarrow = result.metrics.find((m) => m.name === 'bothNarrow');
|
|
626
|
+
expect(bothNarrow?.eligible).toBe(false);
|
|
627
|
+
expect(bothNarrow?.exclusionReason).toContain('5h window');
|
|
628
|
+
expect(bothNarrow?.exclusionReason).not.toContain('7d window');
|
|
629
|
+
});
|
|
630
|
+
});
|
|
631
|
+
|
|
512
632
|
describe('liveSessionConcurrentLimitOf', () => {
|
|
513
633
|
it('gives the full limit while the five hour window is at or above the full speed free ratio', () => {
|
|
514
634
|
expect(liveSessionConcurrentLimitOf(1, 1, SETTINGS)).toBe(
|
|
@@ -8,12 +8,16 @@ import {
|
|
|
8
8
|
export type LiveSessionOauthTokenSelectionSettings = {
|
|
9
9
|
maxConcurrentSessionCount: number;
|
|
10
10
|
fullSpeedFiveHourFreeRatio: number;
|
|
11
|
+
minFiveHourFreeRatio: number;
|
|
12
|
+
minSevenDayFreeRatio: number;
|
|
11
13
|
};
|
|
12
14
|
|
|
13
15
|
export const DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS: LiveSessionOauthTokenSelectionSettings =
|
|
14
16
|
{
|
|
15
17
|
maxConcurrentSessionCount: 10,
|
|
16
18
|
fullSpeedFiveHourFreeRatio: 0.5,
|
|
19
|
+
minFiveHourFreeRatio: 0.6,
|
|
20
|
+
minSevenDayFreeRatio: 0.14,
|
|
17
21
|
};
|
|
18
22
|
|
|
19
23
|
export const liveSessionConcurrentLimitOf = (
|
|
@@ -80,6 +84,12 @@ export class LiveSessionOauthTokenSelectUseCase {
|
|
|
80
84
|
selectionWeightOf(candidate),
|
|
81
85
|
settings,
|
|
82
86
|
);
|
|
87
|
+
const exclusionReason = this.liveSessionExclusionReason(
|
|
88
|
+
rateLimitMetric.exclusionReason,
|
|
89
|
+
rateLimitMetric.fiveHourFreeRatio,
|
|
90
|
+
rateLimitMetric.sevenDayFreeRatio,
|
|
91
|
+
settings,
|
|
92
|
+
);
|
|
83
93
|
return {
|
|
84
94
|
candidate,
|
|
85
95
|
metric: {
|
|
@@ -90,8 +100,8 @@ export class LiveSessionOauthTokenSelectUseCase {
|
|
|
90
100
|
liveSessionCount,
|
|
91
101
|
concurrentSessionLimit,
|
|
92
102
|
hasConcurrencyHeadroom: liveSessionCount < concurrentSessionLimit,
|
|
93
|
-
eligible:
|
|
94
|
-
exclusionReason
|
|
103
|
+
eligible: exclusionReason === null,
|
|
104
|
+
exclusionReason,
|
|
95
105
|
selectionWeight: selectionWeightOf(candidate),
|
|
96
106
|
},
|
|
97
107
|
};
|
|
@@ -113,6 +123,24 @@ export class LiveSessionOauthTokenSelectUseCase {
|
|
|
113
123
|
return { selected: selected.candidate, metrics };
|
|
114
124
|
};
|
|
115
125
|
|
|
126
|
+
private liveSessionExclusionReason = (
|
|
127
|
+
rateLimitExclusionReason: string | null,
|
|
128
|
+
fiveHourFreeRatio: number,
|
|
129
|
+
sevenDayFreeRatio: number,
|
|
130
|
+
settings: LiveSessionOauthTokenSelectionSettings,
|
|
131
|
+
): string | null => {
|
|
132
|
+
if (rateLimitExclusionReason !== null) {
|
|
133
|
+
return rateLimitExclusionReason;
|
|
134
|
+
}
|
|
135
|
+
if (fiveHourFreeRatio < settings.minFiveHourFreeRatio) {
|
|
136
|
+
return `5h window only ${Math.round(fiveHourFreeRatio * 100)}% free (requires >= ${Math.round(settings.minFiveHourFreeRatio * 100)}% for live session selection)`;
|
|
137
|
+
}
|
|
138
|
+
if (sevenDayFreeRatio < settings.minSevenDayFreeRatio) {
|
|
139
|
+
return `7d window only ${Math.round(sevenDayFreeRatio * 100)}% free (requires >= ${Math.round(settings.minSevenDayFreeRatio * 100)}% for live session selection)`;
|
|
140
|
+
}
|
|
141
|
+
return null;
|
|
142
|
+
};
|
|
143
|
+
|
|
116
144
|
private preferred = (
|
|
117
145
|
candidateMetric: LiveSessionOauthTokenCandidateMetrics,
|
|
118
146
|
incumbentMetric: LiveSessionOauthTokenCandidateMetrics,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
|
|
2
3
|
OauthTokenCandidate,
|
|
3
4
|
OauthTokenSelectUseCase,
|
|
4
5
|
OauthTokenWindowSnapshot,
|
|
@@ -550,3 +551,101 @@ describe('sevenDayUrgencyFactor', () => {
|
|
|
550
551
|
expect(almostReset).toBe(SEVEN_DAY_WINDOW_HOURS);
|
|
551
552
|
});
|
|
552
553
|
});
|
|
554
|
+
|
|
555
|
+
describe('OauthTokenSelectUseCase with CL script thresholds', () => {
|
|
556
|
+
const useCase = new OauthTokenSelectUseCase();
|
|
557
|
+
|
|
558
|
+
it('excludes a token whose 5h window is less than 60% free when using CL script thresholds', () => {
|
|
559
|
+
const result = useCase.run(
|
|
560
|
+
[
|
|
561
|
+
candidate('busy5h', snapshot({ fiveHourUtilization: 0.41 })),
|
|
562
|
+
candidate('ok', snapshot({ fiveHourUtilization: 0.4 })),
|
|
563
|
+
],
|
|
564
|
+
NOW,
|
|
565
|
+
Math.random,
|
|
566
|
+
CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
|
|
567
|
+
);
|
|
568
|
+
|
|
569
|
+
expect(result.selected?.name).toBe('ok');
|
|
570
|
+
const busy = result.metrics.find((m) => m.name === 'busy5h');
|
|
571
|
+
expect(busy?.eligible).toBe(false);
|
|
572
|
+
expect(busy?.exclusionReason).toContain('5h window');
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
it('treats exactly 40% used 5h utilization as eligible under CL script thresholds (boundary)', () => {
|
|
576
|
+
const result = useCase.run(
|
|
577
|
+
[candidate('boundary', snapshot({ fiveHourUtilization: 0.4 }))],
|
|
578
|
+
NOW,
|
|
579
|
+
Math.random,
|
|
580
|
+
CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
|
|
581
|
+
);
|
|
582
|
+
|
|
583
|
+
expect(result.selected?.name).toBe('boundary');
|
|
584
|
+
const boundary = result.metrics.find((m) => m.name === 'boundary');
|
|
585
|
+
expect(boundary?.fiveHourFreeRatio).toBeCloseTo(0.6, 9);
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it('excludes a token whose 7d window is less than 14% free when using CL script thresholds', () => {
|
|
589
|
+
const result = useCase.run(
|
|
590
|
+
[
|
|
591
|
+
candidate('busy7d', snapshot({ sevenDayUtilization: 0.87 })),
|
|
592
|
+
candidate('ok', snapshot({ sevenDayUtilization: 0.86 })),
|
|
593
|
+
],
|
|
594
|
+
NOW,
|
|
595
|
+
Math.random,
|
|
596
|
+
CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
|
|
597
|
+
);
|
|
598
|
+
|
|
599
|
+
expect(result.selected?.name).toBe('ok');
|
|
600
|
+
const busy = result.metrics.find((m) => m.name === 'busy7d');
|
|
601
|
+
expect(busy?.eligible).toBe(false);
|
|
602
|
+
expect(busy?.exclusionReason).toContain('7d window');
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
it('treats exactly 86% used 7d utilization as eligible under CL script thresholds (boundary)', () => {
|
|
606
|
+
const result = useCase.run(
|
|
607
|
+
[candidate('boundary', snapshot({ sevenDayUtilization: 0.86 }))],
|
|
608
|
+
NOW,
|
|
609
|
+
Math.random,
|
|
610
|
+
CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
|
|
611
|
+
);
|
|
612
|
+
|
|
613
|
+
expect(result.selected?.name).toBe('boundary');
|
|
614
|
+
const boundary = result.metrics.find((m) => m.name === 'boundary');
|
|
615
|
+
expect(boundary?.sevenDayFreeRatio).toBeCloseTo(0.14, 9);
|
|
616
|
+
});
|
|
617
|
+
|
|
618
|
+
it('returns null when only a token with 59% 5h free is available under CL script thresholds', () => {
|
|
619
|
+
const result = useCase.run(
|
|
620
|
+
[candidate('marginal', snapshot({ fiveHourUtilization: 0.41 }))],
|
|
621
|
+
NOW,
|
|
622
|
+
Math.random,
|
|
623
|
+
CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
|
|
624
|
+
);
|
|
625
|
+
|
|
626
|
+
expect(result.selected).toBeNull();
|
|
627
|
+
});
|
|
628
|
+
|
|
629
|
+
it('returns null when only a token with 13% 7d free is available under CL script thresholds', () => {
|
|
630
|
+
const result = useCase.run(
|
|
631
|
+
[candidate('marginal', snapshot({ sevenDayUtilization: 0.87 }))],
|
|
632
|
+
NOW,
|
|
633
|
+
Math.random,
|
|
634
|
+
CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
|
|
635
|
+
);
|
|
636
|
+
|
|
637
|
+
expect(result.selected).toBeNull();
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
it('includes the correct threshold percentages in the exclusion reason message', () => {
|
|
641
|
+
const result = useCase.run(
|
|
642
|
+
[candidate('low5h', snapshot({ fiveHourUtilization: 0.5 }))],
|
|
643
|
+
NOW,
|
|
644
|
+
Math.random,
|
|
645
|
+
CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
|
|
646
|
+
);
|
|
647
|
+
|
|
648
|
+
const low5h = result.metrics.find((m) => m.name === 'low5h');
|
|
649
|
+
expect(low5h?.exclusionReason).toContain('60%');
|
|
650
|
+
});
|
|
651
|
+
});
|
|
@@ -39,12 +39,29 @@ export type OauthTokenSelectResult = {
|
|
|
39
39
|
metrics: OauthTokenCandidateMetrics[];
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
+
export type OauthTokenSelectionThresholds = {
|
|
43
|
+
fiveHourMinFreeRatio: number;
|
|
44
|
+
sevenDayMinFreeRatio: number;
|
|
45
|
+
};
|
|
46
|
+
|
|
42
47
|
const SECONDS_PER_DAY = 86400;
|
|
43
48
|
const SEVEN_DAYS_IN_SECONDS = 7 * SECONDS_PER_DAY;
|
|
44
49
|
|
|
45
50
|
export const FIVE_HOUR_MIN_FREE_RATIO = 0.25;
|
|
46
51
|
export const SEVEN_DAY_MIN_FREE_RATIO = 0.03;
|
|
47
52
|
|
|
53
|
+
export const DEFAULT_OAUTH_TOKEN_SELECTION_THRESHOLDS: OauthTokenSelectionThresholds =
|
|
54
|
+
{
|
|
55
|
+
fiveHourMinFreeRatio: FIVE_HOUR_MIN_FREE_RATIO,
|
|
56
|
+
sevenDayMinFreeRatio: SEVEN_DAY_MIN_FREE_RATIO,
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS: OauthTokenSelectionThresholds =
|
|
60
|
+
{
|
|
61
|
+
fiveHourMinFreeRatio: 0.6,
|
|
62
|
+
sevenDayMinFreeRatio: 0.14,
|
|
63
|
+
};
|
|
64
|
+
|
|
48
65
|
export const SEVEN_DAY_WINDOW_HOURS = 168;
|
|
49
66
|
export const MIN_HOURS_TO_RESET = 1;
|
|
50
67
|
const SECONDS_PER_HOUR = 3600;
|
|
@@ -91,9 +108,14 @@ export class OauthTokenSelectUseCase {
|
|
|
91
108
|
candidates: OauthTokenCandidate[],
|
|
92
109
|
nowEpochSeconds: number,
|
|
93
110
|
random: SelectionRandom = Math.random,
|
|
111
|
+
thresholds: OauthTokenSelectionThresholds = DEFAULT_OAUTH_TOKEN_SELECTION_THRESHOLDS,
|
|
94
112
|
): OauthTokenSelectResult => {
|
|
95
113
|
const evaluated = candidates.map((candidate) => {
|
|
96
|
-
const evaluatedMetric = this.evaluate(
|
|
114
|
+
const evaluatedMetric = this.evaluate(
|
|
115
|
+
candidate,
|
|
116
|
+
nowEpochSeconds,
|
|
117
|
+
thresholds,
|
|
118
|
+
);
|
|
97
119
|
return {
|
|
98
120
|
candidate,
|
|
99
121
|
metric: {
|
|
@@ -136,6 +158,7 @@ export class OauthTokenSelectUseCase {
|
|
|
136
158
|
private evaluate = (
|
|
137
159
|
candidate: OauthTokenCandidate,
|
|
138
160
|
nowEpochSeconds: number,
|
|
161
|
+
thresholds: OauthTokenSelectionThresholds,
|
|
139
162
|
): Omit<OauthTokenCandidateMetrics, 'drawWeight'> => {
|
|
140
163
|
const fiveHourFreeRatio = this.fiveHourFreeRatio(
|
|
141
164
|
candidate.snapshot,
|
|
@@ -156,6 +179,7 @@ export class OauthTokenSelectUseCase {
|
|
|
156
179
|
candidate.fableRejected,
|
|
157
180
|
fiveHourFreeRatio,
|
|
158
181
|
sevenDayFreeRatio,
|
|
182
|
+
thresholds,
|
|
159
183
|
);
|
|
160
184
|
|
|
161
185
|
return {
|
|
@@ -174,6 +198,7 @@ export class OauthTokenSelectUseCase {
|
|
|
174
198
|
fableRejected: boolean,
|
|
175
199
|
fiveHourFreeRatio: number,
|
|
176
200
|
sevenDayFreeRatio: number,
|
|
201
|
+
thresholds: OauthTokenSelectionThresholds,
|
|
177
202
|
): string | null => {
|
|
178
203
|
if (subscriptionDisabled) {
|
|
179
204
|
return 'organization has disabled Claude subscription access for Claude Code';
|
|
@@ -184,11 +209,11 @@ export class OauthTokenSelectUseCase {
|
|
|
184
209
|
if (fableRejected) {
|
|
185
210
|
return 'fable weekly limit exhausted (a fable request was rejected with HTTP 429)';
|
|
186
211
|
}
|
|
187
|
-
if (fiveHourFreeRatio <
|
|
188
|
-
return `5h window only ${this.toPercent(fiveHourFreeRatio)}% free (requires >= ${this.toPercent(
|
|
212
|
+
if (fiveHourFreeRatio < thresholds.fiveHourMinFreeRatio) {
|
|
213
|
+
return `5h window only ${this.toPercent(fiveHourFreeRatio)}% free (requires >= ${this.toPercent(thresholds.fiveHourMinFreeRatio)}%)`;
|
|
189
214
|
}
|
|
190
|
-
if (sevenDayFreeRatio <
|
|
191
|
-
return `7d window only ${this.toPercent(sevenDayFreeRatio)}% free (requires >= ${this.toPercent(
|
|
215
|
+
if (sevenDayFreeRatio < thresholds.sevenDayMinFreeRatio) {
|
|
216
|
+
return `7d window only ${this.toPercent(sevenDayFreeRatio)}% free (requires >= ${this.toPercent(thresholds.sevenDayMinFreeRatio)}%)`;
|
|
192
217
|
}
|
|
193
218
|
return null;
|
|
194
219
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fleetConfig.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/cli/fleetConfig.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,sCAAsC,EACvC,MAAM,6DAA6D,CAAC;AAErE,eAAO,MAAM,2CAA2C,sBAAsB,CAAC;AAE/E,eAAO,MAAM,8CAA8C,mCACzB,CAAC;AAEnC,eAAO,MAAM,0BAA0B,GACrC,UAAU,MAAM,GAAG,IAAI,KACtB,MAAM,GAAG,IAUX,CAAC;AAwDF,eAAO,MAAM,0CAA0C,GACrD,qBAAqB,MAAM,GAAG,IAAI,KACjC,
|
|
1
|
+
{"version":3,"file":"fleetConfig.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/cli/fleetConfig.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,sCAAsC,EACvC,MAAM,6DAA6D,CAAC;AAErE,eAAO,MAAM,2CAA2C,sBAAsB,CAAC;AAE/E,eAAO,MAAM,8CAA8C,mCACzB,CAAC;AAEnC,eAAO,MAAM,0BAA0B,GACrC,UAAU,MAAM,GAAG,IAAI,KACtB,MAAM,GAAG,IAUX,CAAC;AAwDF,eAAO,MAAM,0CAA0C,GACrD,qBAAqB,MAAM,GAAG,IAAI,KACjC,sCA0CF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OauthTokenSelectHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"OauthTokenSelectHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,uBAAuB,EACvB,KAAK,eAAe,EACrB,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;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,OAAO,GAAE,uBAAuD,EAChE,MAAM,GAAE,eAA6B;IAGxD,MAAM,GACJ,OAAO,4BAA4B,KAClC,6BAA6B,CAiE9B;IAEF,OAAO,CAAC,iBAAiB,CAuBvB;CACH"}
|
|
@@ -3,6 +3,8 @@ import { type OauthTokenCandidate, OauthTokenSelectUseCase } from './OauthTokenS
|
|
|
3
3
|
export type LiveSessionOauthTokenSelectionSettings = {
|
|
4
4
|
maxConcurrentSessionCount: number;
|
|
5
5
|
fullSpeedFiveHourFreeRatio: number;
|
|
6
|
+
minFiveHourFreeRatio: number;
|
|
7
|
+
minSevenDayFreeRatio: number;
|
|
6
8
|
};
|
|
7
9
|
export declare const DEFAULT_LIVE_SESSION_OAUTH_TOKEN_SELECTION_SETTINGS: LiveSessionOauthTokenSelectionSettings;
|
|
8
10
|
export declare const liveSessionConcurrentLimitOf: (fiveHourFreeRatio: number, selectionWeight: number, settings: LiveSessionOauthTokenSelectionSettings) => number;
|
|
@@ -26,6 +28,7 @@ export declare class LiveSessionOauthTokenSelectUseCase {
|
|
|
26
28
|
private readonly rateLimitSelectUseCase;
|
|
27
29
|
constructor(rateLimitSelectUseCase?: OauthTokenSelectUseCase);
|
|
28
30
|
run: (candidates: OauthTokenCandidate[], liveSessions: ClaudeLiveSession[], nowEpochSeconds: number, settings: LiveSessionOauthTokenSelectionSettings) => LiveSessionOauthTokenSelectResult;
|
|
31
|
+
private liveSessionExclusionReason;
|
|
29
32
|
private preferred;
|
|
30
33
|
private liveSessionCountByToken;
|
|
31
34
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveSessionOauthTokenSelectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AAC1F,OAAO,EACL,KAAK,mBAAmB,EACxB,uBAAuB,EAExB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,sCAAsC,GAAG;IACnD,yBAAyB,EAAE,MAAM,CAAC;IAClC,0BAA0B,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"LiveSessionOauthTokenSelectUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AAC1F,OAAO,EACL,KAAK,mBAAmB,EACxB,uBAAuB,EAExB,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,sCAAsC,GAAG;IACnD,yBAAyB,EAAE,MAAM,CAAC;IAClC,0BAA0B,EAAE,MAAM,CAAC;IACnC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,eAAO,MAAM,mDAAmD,EAAE,sCAM/D,CAAC;AAEJ,eAAO,MAAM,4BAA4B,GACvC,mBAAmB,MAAM,EACzB,iBAAiB,MAAM,EACvB,UAAU,sCAAsC,KAC/C,MAaF,CAAC;AAEF,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,sBAAsB,EAAE,MAAM,CAAC;IAC/B,sBAAsB,EAAE,OAAO,CAAC;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,eAAe,EAAE,MAAM,CAAC;CACzB,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,UAAU,sCAAsC,KAC/C,iCAAiC,CAsDlC;IAEF,OAAO,CAAC,0BAA0B,CAgBhC;IAEF,OAAO,CAAC,SAAS,CAgBf;IAEF,OAAO,CAAC,uBAAuB,CAe7B;CACH"}
|
|
@@ -29,14 +29,20 @@ export type OauthTokenSelectResult = {
|
|
|
29
29
|
selected: OauthTokenCandidate | null;
|
|
30
30
|
metrics: OauthTokenCandidateMetrics[];
|
|
31
31
|
};
|
|
32
|
+
export type OauthTokenSelectionThresholds = {
|
|
33
|
+
fiveHourMinFreeRatio: number;
|
|
34
|
+
sevenDayMinFreeRatio: number;
|
|
35
|
+
};
|
|
32
36
|
export declare const FIVE_HOUR_MIN_FREE_RATIO = 0.25;
|
|
33
37
|
export declare const SEVEN_DAY_MIN_FREE_RATIO = 0.03;
|
|
38
|
+
export declare const DEFAULT_OAUTH_TOKEN_SELECTION_THRESHOLDS: OauthTokenSelectionThresholds;
|
|
39
|
+
export declare const CL_SCRIPT_OAUTH_TOKEN_SELECTION_THRESHOLDS: OauthTokenSelectionThresholds;
|
|
34
40
|
export declare const SEVEN_DAY_WINDOW_HOURS = 168;
|
|
35
41
|
export declare const MIN_HOURS_TO_RESET = 1;
|
|
36
42
|
export declare const sevenDayUrgencyFactor: (sevenDayFreeRatio: number, sevenDayEndEpoch: number, nowEpochSeconds: number) => number;
|
|
37
43
|
export declare const selectWeightedCandidate: <Entry>(eligible: Entry[], weightOf: (entry: Entry) => number, deterministicBest: Entry, random: SelectionRandom) => Entry;
|
|
38
44
|
export declare class OauthTokenSelectUseCase {
|
|
39
|
-
run: (candidates: OauthTokenCandidate[], nowEpochSeconds: number, random?: SelectionRandom) => OauthTokenSelectResult;
|
|
45
|
+
run: (candidates: OauthTokenCandidate[], nowEpochSeconds: number, random?: SelectionRandom, thresholds?: OauthTokenSelectionThresholds) => OauthTokenSelectResult;
|
|
40
46
|
private evaluate;
|
|
41
47
|
private exclusionReason;
|
|
42
48
|
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;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;IAC/B,UAAU,EAAE,MAAM,CAAC;CACpB,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,OAAO,CAAC;AAC7C,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAGpC,eAAO,MAAM,qBAAqB,GAChC,mBAAmB,MAAM,EACzB,kBAAkB,MAAM,EACxB,iBAAiB,MAAM,KACtB,MAMF,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,KAAK,EAC3C,UAAU,KAAK,EAAE,EACjB,UAAU,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,EAClC,mBAAmB,KAAK,EACxB,QAAQ,eAAe,KACtB,KAkBF,CAAC;AAEF,qBAAa,uBAAuB;IAClC,GAAG,GACD,YAAY,mBAAmB,EAAE,EACjC,iBAAiB,MAAM,EACvB,SAAQ,eAA6B,
|
|
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;IAC/B,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACrC,OAAO,EAAE,0BAA0B,EAAE,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,oBAAoB,EAAE,MAAM,CAAC;IAC7B,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC;AAKF,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAE7C,eAAO,MAAM,wCAAwC,EAAE,6BAIpD,CAAC;AAEJ,eAAO,MAAM,0CAA0C,EAAE,6BAItD,CAAC;AAEJ,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAC1C,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAGpC,eAAO,MAAM,qBAAqB,GAChC,mBAAmB,MAAM,EACzB,kBAAkB,MAAM,EACxB,iBAAiB,MAAM,KACtB,MAMF,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,KAAK,EAC3C,UAAU,KAAK,EAAE,EACjB,UAAU,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,EAClC,mBAAmB,KAAK,EACxB,QAAQ,eAAe,KACtB,KAkBF,CAAC;AAEF,qBAAa,uBAAuB;IAClC,GAAG,GACD,YAAY,mBAAmB,EAAE,EACjC,iBAAiB,MAAM,EACvB,SAAQ,eAA6B,EACrC,aAAY,6BAAwE,KACnF,sBAAsB,CA4CvB;IAEF,OAAO,CAAC,QAAQ,CAmCd;IAEF,OAAO,CAAC,eAAe,CAwBrB;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"}
|