github-issue-tower-defence-management 1.55.0 → 1.56.1
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/CHANGELOG.md +14 -0
- package/README.md +23 -0
- package/bin/adapter/entry-points/cli/index.js +3 -1
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/entry-points/cli/projectConfig.js +5 -0
- package/bin/adapter/entry-points/cli/projectConfig.js.map +1 -1
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +3 -1
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/proxy/RateLimitCache.js +123 -0
- package/bin/adapter/proxy/RateLimitCache.js.map +1 -0
- package/bin/adapter/proxy/TokenListLoader.js +72 -0
- package/bin/adapter/proxy/TokenListLoader.js.map +1 -0
- package/bin/adapter/proxy/ensureProxyRunning.js +73 -0
- package/bin/adapter/proxy/ensureProxyRunning.js.map +1 -0
- package/bin/adapter/proxy/proxyEntry.js +96 -0
- package/bin/adapter/proxy/proxyEntry.js.map +1 -0
- package/bin/adapter/repositories/NodeLocalCommandRunner.js +10 -4
- package/bin/adapter/repositories/NodeLocalCommandRunner.js.map +1 -1
- package/bin/adapter/repositories/OauthProxyClaudeRepository.js +3 -0
- package/bin/adapter/repositories/OauthProxyClaudeRepository.js.map +1 -1
- package/bin/adapter/repositories/ProxyClaudeTokenUsageRepository.js +35 -0
- package/bin/adapter/repositories/ProxyClaudeTokenUsageRepository.js.map +1 -0
- package/bin/domain/entities/ClaudeTokenUsage.js +3 -0
- package/bin/domain/entities/ClaudeTokenUsage.js.map +1 -0
- package/bin/domain/usecases/StartPreparationUseCase.js +26 -2
- package/bin/domain/usecases/StartPreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/adapter-interfaces/ClaudeTokenUsageRepository.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/ClaudeTokenUsageRepository.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/cli/index.ts +5 -0
- package/src/adapter/entry-points/cli/projectConfig.ts +13 -0
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +5 -0
- package/src/adapter/proxy/RateLimitCache.test.ts +131 -0
- package/src/adapter/proxy/RateLimitCache.ts +112 -0
- package/src/adapter/proxy/TokenListLoader.test.ts +82 -0
- package/src/adapter/proxy/TokenListLoader.ts +35 -0
- package/src/adapter/proxy/ensureProxyRunning.test.ts +85 -0
- package/src/adapter/proxy/ensureProxyRunning.ts +41 -0
- package/src/adapter/proxy/proxyEntry.test.ts +48 -0
- package/src/adapter/proxy/proxyEntry.ts +69 -0
- package/src/adapter/repositories/NodeLocalCommandRunner.test.ts +3 -1
- package/src/adapter/repositories/NodeLocalCommandRunner.ts +18 -4
- package/src/adapter/repositories/OauthProxyClaudeRepository.test.ts +29 -0
- package/src/adapter/repositories/OauthProxyClaudeRepository.ts +5 -0
- package/src/adapter/repositories/ProxyClaudeTokenUsageRepository.test.ts +127 -0
- package/src/adapter/repositories/ProxyClaudeTokenUsageRepository.ts +36 -0
- package/src/domain/entities/ClaudeTokenUsage.ts +5 -0
- package/src/domain/usecases/StartPreparationUseCase.test.ts +308 -0
- package/src/domain/usecases/StartPreparationUseCase.ts +37 -1
- package/src/domain/usecases/adapter-interfaces/ClaudeTokenUsageRepository.ts +7 -0
- package/src/domain/usecases/adapter-interfaces/LocalCommandRunner.ts +5 -0
- package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
- package/types/adapter/entry-points/cli/projectConfig.d.ts +1 -0
- package/types/adapter/entry-points/cli/projectConfig.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/proxy/RateLimitCache.d.ts +14 -0
- package/types/adapter/proxy/RateLimitCache.d.ts.map +1 -0
- package/types/adapter/proxy/TokenListLoader.d.ts +2 -0
- package/types/adapter/proxy/TokenListLoader.d.ts.map +1 -0
- package/types/adapter/proxy/ensureProxyRunning.d.ts +2 -0
- package/types/adapter/proxy/ensureProxyRunning.d.ts.map +1 -0
- package/types/adapter/proxy/proxyEntry.d.ts +4 -0
- package/types/adapter/proxy/proxyEntry.d.ts.map +1 -0
- package/types/adapter/repositories/NodeLocalCommandRunner.d.ts +2 -2
- package/types/adapter/repositories/NodeLocalCommandRunner.d.ts.map +1 -1
- package/types/adapter/repositories/OauthProxyClaudeRepository.d.ts.map +1 -1
- package/types/adapter/repositories/ProxyClaudeTokenUsageRepository.d.ts +11 -0
- package/types/adapter/repositories/ProxyClaudeTokenUsageRepository.d.ts.map +1 -0
- package/types/domain/entities/ClaudeTokenUsage.d.ts +6 -0
- package/types/domain/entities/ClaudeTokenUsage.d.ts.map +1 -0
- package/types/domain/usecases/StartPreparationUseCase.d.ts +4 -1
- package/types/domain/usecases/StartPreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/ClaudeTokenUsageRepository.d.ts +7 -0
- package/types/domain/usecases/adapter-interfaces/ClaudeTokenUsageRepository.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/LocalCommandRunner.d.ts +4 -1
- package/types/domain/usecases/adapter-interfaces/LocalCommandRunner.d.ts.map +1 -1
|
@@ -5,12 +5,18 @@ const child_process_1 = require("child_process");
|
|
|
5
5
|
const util_1 = require("util");
|
|
6
6
|
const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
|
|
7
7
|
class NodeLocalCommandRunner {
|
|
8
|
-
async runCommand(program, args) {
|
|
8
|
+
async runCommand(program, args, options) {
|
|
9
|
+
const execOptions = {
|
|
10
|
+
encoding: 'utf8',
|
|
11
|
+
};
|
|
12
|
+
if (options?.env) {
|
|
13
|
+
execOptions.env = { ...process.env, ...options.env };
|
|
14
|
+
}
|
|
9
15
|
try {
|
|
10
|
-
const { stdout, stderr } = await execFileAsync(program, args);
|
|
16
|
+
const { stdout, stderr } = await execFileAsync(program, args, execOptions);
|
|
11
17
|
return {
|
|
12
|
-
stdout,
|
|
13
|
-
stderr,
|
|
18
|
+
stdout: String(stdout),
|
|
19
|
+
stderr: String(stderr),
|
|
14
20
|
exitCode: 0,
|
|
15
21
|
};
|
|
16
22
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeLocalCommandRunner.js","sourceRoot":"","sources":["../../../src/adapter/repositories/NodeLocalCommandRunner.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"NodeLocalCommandRunner.js","sourceRoot":"","sources":["../../../src/adapter/repositories/NodeLocalCommandRunner.ts"],"names":[],"mappings":";;;AAIA,iDAAyC;AACzC,+BAAiC;AAEjC,MAAM,aAAa,GAAG,IAAA,gBAAS,EAAC,wBAAQ,CAAC,CAAC;AAE1C,MAAa,sBAAsB;IACjC,KAAK,CAAC,UAAU,CACd,OAAe,EACf,IAAc,EACd,OAAmC;QAMnC,MAAM,WAAW,GAAwC;YACvD,QAAQ,EAAE,MAAM;SACjB,CAAC;QACF,IAAI,OAAO,EAAE,GAAG,EAAE,CAAC;YACjB,WAAW,CAAC,GAAG,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;QACvD,CAAC;QACD,IAAI,CAAC;YACH,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAC5C,OAAO,EACP,IAAI,EACJ,WAAW,CACZ,CAAC;YACF,OAAO;gBACL,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;gBACtB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;gBACtB,QAAQ,EAAE,CAAC;aACZ,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IACE,KAAK;gBACL,OAAO,KAAK,KAAK,QAAQ;gBACzB,QAAQ,IAAI,KAAK;gBACjB,QAAQ,IAAI,KAAK;gBACjB,MAAM,IAAI,KAAK,EACf,CAAC;gBACD,OAAO;oBACL,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;oBAC5B,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;oBAC5B,QAAQ,EAAE,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;iBAC1D,CAAC;YACJ,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF;AA5CD,wDA4CC"}
|
|
@@ -64,9 +64,12 @@ class OauthProxyClaudeRepository {
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
const overageStatus = headers['anthropic-ratelimit-unified-overage-status'];
|
|
67
|
+
const overageDisabledReason = headers['anthropic-ratelimit-unified-overage-disabled-reason'];
|
|
68
|
+
const isAdministrativeOverageDisable = overageDisabledReason?.startsWith('org_level_disabled') ?? false;
|
|
67
69
|
const baseQuotaAvailable = headers['anthropic-ratelimit-unified-5h-status'] === 'allowed' &&
|
|
68
70
|
headers['anthropic-ratelimit-unified-7d-status'] === 'allowed';
|
|
69
71
|
if (overageStatus === 'rejected' &&
|
|
72
|
+
!isAdministrativeOverageDisable &&
|
|
70
73
|
!baseQuotaAvailable &&
|
|
71
74
|
!usages.some((u) => u.utilizationPercentage >= 100)) {
|
|
72
75
|
usages.push({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OauthProxyClaudeRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/OauthProxyClaudeRepository.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"OauthProxyClaudeRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/OauthProxyClaudeRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProxyClaudeTokenUsageRepository = void 0;
|
|
4
|
+
const ensureProxyRunning_1 = require("../proxy/ensureProxyRunning");
|
|
5
|
+
const RateLimitCache_1 = require("../proxy/RateLimitCache");
|
|
6
|
+
const TokenListLoader_1 = require("../proxy/TokenListLoader");
|
|
7
|
+
class ProxyClaudeTokenUsageRepository {
|
|
8
|
+
constructor(tokenListJsonPath, port = RateLimitCache_1.PROXY_PORT) {
|
|
9
|
+
this.tokenListJsonPath = tokenListJsonPath;
|
|
10
|
+
this.port = port;
|
|
11
|
+
this.ensureObservable = async () => {
|
|
12
|
+
await (0, ensureProxyRunning_1.ensureProxyRunning)(this.port);
|
|
13
|
+
};
|
|
14
|
+
this.getAvailableTokenUsages = async () => {
|
|
15
|
+
if (this.tokenListJsonPath === null) {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
const tokens = (0, TokenListLoader_1.loadTokens)(this.tokenListJsonPath);
|
|
19
|
+
if (tokens === null) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
return tokens.map((token) => {
|
|
23
|
+
const snapshot = (0, RateLimitCache_1.readRateLimit)(token);
|
|
24
|
+
return {
|
|
25
|
+
token,
|
|
26
|
+
fiveHourUtilization: snapshot ? snapshot.fiveHourUtilization : 0,
|
|
27
|
+
blocked: snapshot?.blocked ?? false,
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
this.proxyBaseUrl = () => `http://127.0.0.1:${this.port}`;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.ProxyClaudeTokenUsageRepository = ProxyClaudeTokenUsageRepository;
|
|
35
|
+
//# sourceMappingURL=ProxyClaudeTokenUsageRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProxyClaudeTokenUsageRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/ProxyClaudeTokenUsageRepository.ts"],"names":[],"mappings":";;;AAEA,oEAAiE;AACjE,4DAAoE;AACpE,8DAAsD;AAEtD,MAAa,+BAA+B;IAC1C,YACmB,iBAAgC,EAChC,OAAe,2BAAU;QADzB,sBAAiB,GAAjB,iBAAiB,CAAe;QAChC,SAAI,GAAJ,IAAI,CAAqB;QAG5C,qBAAgB,GAAG,KAAK,IAAmB,EAAE;YAC3C,MAAM,IAAA,uCAAkB,EAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,CAAC,CAAC;QAEF,4BAAuB,GAAG,KAAK,IAAiC,EAAE;YAChE,IAAI,IAAI,CAAC,iBAAiB,KAAK,IAAI,EAAE,CAAC;gBACpC,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,MAAM,MAAM,GAAG,IAAA,4BAAU,EAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;YAClD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;gBACpB,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;gBAC1B,MAAM,QAAQ,GAAG,IAAA,8BAAa,EAAC,KAAK,CAAC,CAAC;gBACtC,OAAO;oBACL,KAAK;oBACL,mBAAmB,EAAE,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC;oBAChE,OAAO,EAAE,QAAQ,EAAE,OAAO,IAAI,KAAK;iBACpC,CAAC;YACJ,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QAEF,iBAAY,GAAG,GAAW,EAAE,CAAC,oBAAoB,IAAI,CAAC,IAAI,EAAE,CAAC;IAxB1D,CAAC;CAyBL;AA7BD,0EA6BC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClaudeTokenUsage.js","sourceRoot":"","sources":["../../../src/domain/entities/ClaudeTokenUsage.ts"],"names":[],"mappings":""}
|
|
@@ -3,11 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.StartPreparationUseCase = void 0;
|
|
4
4
|
const WorkflowStatus_1 = require("../entities/WorkflowStatus");
|
|
5
5
|
class StartPreparationUseCase {
|
|
6
|
-
constructor(projectRepository, issueRepository, claudeRepository, localCommandRunner) {
|
|
6
|
+
constructor(projectRepository, issueRepository, claudeRepository, localCommandRunner, claudeTokenUsageRepository) {
|
|
7
7
|
this.projectRepository = projectRepository;
|
|
8
8
|
this.issueRepository = issueRepository;
|
|
9
9
|
this.claudeRepository = claudeRepository;
|
|
10
10
|
this.localCommandRunner = localCommandRunner;
|
|
11
|
+
this.claudeTokenUsageRepository = claudeTokenUsageRepository;
|
|
12
|
+
this.selectRotationTokens = (tokenUsages) => tokenUsages
|
|
13
|
+
.filter((usage) => !usage.blocked)
|
|
14
|
+
.sort((a, b) => a.fiveHourUtilization - b.fiveHourUtilization)
|
|
15
|
+
.map((usage) => usage.token);
|
|
11
16
|
this.run = async (params) => {
|
|
12
17
|
const claudeUsages = await this.claudeRepository.getUsage();
|
|
13
18
|
const weeklyWindowHours = 168;
|
|
@@ -33,6 +38,17 @@ class StartPreparationUseCase {
|
|
|
33
38
|
console.warn(`Weekly Claude usage (${maxWeeklyUtilization}%) exceeds threshold (${params.utilizationPercentageThreshold}%). Reducing maximumPreparingIssuesCount to ${maximumPreparingIssuesCount}.`);
|
|
34
39
|
}
|
|
35
40
|
}
|
|
41
|
+
const tokenUsages = await this.claudeTokenUsageRepository.getAvailableTokenUsages();
|
|
42
|
+
let rotationTokens = null;
|
|
43
|
+
let proxyBaseUrl = null;
|
|
44
|
+
if (tokenUsages.length > 0) {
|
|
45
|
+
const ranked = this.selectRotationTokens(tokenUsages);
|
|
46
|
+
if (ranked.length > 0) {
|
|
47
|
+
await this.claudeTokenUsageRepository.ensureObservable();
|
|
48
|
+
rotationTokens = ranked;
|
|
49
|
+
proxyBaseUrl = this.claudeTokenUsageRepository.proxyBaseUrl();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
36
52
|
const project = await this.projectRepository.getByUrl(params.projectUrl);
|
|
37
53
|
const storyObjectMap = await this.issueRepository.getStoryObjectMap(project, params.allowIssueCacheMinutes);
|
|
38
54
|
const allOpenedIssues = Array.from(storyObjectMap.values()).flatMap((storyObject) => storyObject.issues);
|
|
@@ -152,7 +168,15 @@ class StartPreparationUseCase {
|
|
|
152
168
|
const codexHome = params.codexHomeCandidates[startedInThisRunCount % params.codexHomeCandidates.length];
|
|
153
169
|
awArgs.push('--codexHome', codexHome);
|
|
154
170
|
}
|
|
155
|
-
|
|
171
|
+
let spawnEnv;
|
|
172
|
+
if (rotationTokens !== null && proxyBaseUrl !== null) {
|
|
173
|
+
const selected = rotationTokens[startedInThisRunCount % rotationTokens.length];
|
|
174
|
+
spawnEnv = {
|
|
175
|
+
CLAUDE_CODE_OAUTH_TOKEN: selected,
|
|
176
|
+
ANTHROPIC_BASE_URL: proxyBaseUrl,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
await this.localCommandRunner.runCommand('aw', awArgs, spawnEnv ? { env: spawnEnv } : undefined);
|
|
156
180
|
startedInThisRunCount++;
|
|
157
181
|
updatedCurrentPreparationIssueCount++;
|
|
158
182
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StartPreparationUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/StartPreparationUseCase.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"StartPreparationUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/StartPreparationUseCase.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ClaudeTokenUsageRepository.js","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/ClaudeTokenUsageRepository.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
fetchProjectReadme,
|
|
17
17
|
} from './projectConfig';
|
|
18
18
|
import { StartPreparationUseCase } from '../../../domain/usecases/StartPreparationUseCase';
|
|
19
|
+
import { ProxyClaudeTokenUsageRepository } from '../../repositories/ProxyClaudeTokenUsageRepository';
|
|
19
20
|
import { NotifyFinishedIssuePreparationUseCase } from '../../../domain/usecases/NotifyFinishedIssuePreparationUseCase';
|
|
20
21
|
import { LocalStorageRepository } from '../../repositories/LocalStorageRepository';
|
|
21
22
|
import { GraphqlProjectRepository } from '../../repositories/GraphqlProjectRepository';
|
|
@@ -269,11 +270,15 @@ program
|
|
|
269
270
|
});
|
|
270
271
|
}
|
|
271
272
|
|
|
273
|
+
const claudeTokenUsageRepository = new ProxyClaudeTokenUsageRepository(
|
|
274
|
+
config.claudeCodeOauthTokenListJsonPath ?? null,
|
|
275
|
+
);
|
|
272
276
|
const useCase = new StartPreparationUseCase(
|
|
273
277
|
projectRepository,
|
|
274
278
|
issueRepository,
|
|
275
279
|
claudeRepository,
|
|
276
280
|
localCommandRunner,
|
|
281
|
+
claudeTokenUsageRepository,
|
|
277
282
|
);
|
|
278
283
|
|
|
279
284
|
const rawAllowedIssueAuthors = config.allowedIssueAuthors;
|
|
@@ -15,6 +15,7 @@ export type ConfigFile = {
|
|
|
15
15
|
projectName?: string;
|
|
16
16
|
preparationProcessCheckCommand?: string;
|
|
17
17
|
codexHomeCandidates?: string[];
|
|
18
|
+
claudeCodeOauthTokenListJsonPath?: string;
|
|
18
19
|
awLogDirectoryPath?: string;
|
|
19
20
|
awLogStaleThresholdMinutes?: number;
|
|
20
21
|
};
|
|
@@ -89,6 +90,10 @@ export const loadConfigFile = (configFilePath: string): ConfigFile => {
|
|
|
89
90
|
'preparationProcessCheckCommand',
|
|
90
91
|
),
|
|
91
92
|
codexHomeCandidates: getStringArrayValue(parsed, 'codexHomeCandidates'),
|
|
93
|
+
claudeCodeOauthTokenListJsonPath: getStringValue(
|
|
94
|
+
parsed,
|
|
95
|
+
'claudeCodeOauthTokenListJsonPath',
|
|
96
|
+
),
|
|
92
97
|
awLogDirectoryPath: getStringValue(parsed, 'awLogDirectoryPath'),
|
|
93
98
|
awLogStaleThresholdMinutes: getNumberValue(
|
|
94
99
|
parsed,
|
|
@@ -144,6 +149,10 @@ export const parseProjectReadmeConfig = (readme: string): ConfigFile => {
|
|
|
144
149
|
'preparationProcessCheckCommand',
|
|
145
150
|
),
|
|
146
151
|
codexHomeCandidates: getStringArrayValue(parsed, 'codexHomeCandidates'),
|
|
152
|
+
claudeCodeOauthTokenListJsonPath: getStringValue(
|
|
153
|
+
parsed,
|
|
154
|
+
'claudeCodeOauthTokenListJsonPath',
|
|
155
|
+
),
|
|
147
156
|
awLogDirectoryPath: getStringValue(parsed, 'awLogDirectoryPath'),
|
|
148
157
|
awLogStaleThresholdMinutes: getNumberValue(
|
|
149
158
|
parsed,
|
|
@@ -207,6 +216,10 @@ export const mergeConfigs = (
|
|
|
207
216
|
readmeOverrides.codexHomeCandidates ??
|
|
208
217
|
cliOverrides.codexHomeCandidates ??
|
|
209
218
|
configFile.codexHomeCandidates,
|
|
219
|
+
claudeCodeOauthTokenListJsonPath:
|
|
220
|
+
readmeOverrides.claudeCodeOauthTokenListJsonPath ??
|
|
221
|
+
cliOverrides.claudeCodeOauthTokenListJsonPath ??
|
|
222
|
+
configFile.claudeCodeOauthTokenListJsonPath,
|
|
210
223
|
awLogDirectoryPath:
|
|
211
224
|
readmeOverrides.awLogDirectoryPath ??
|
|
212
225
|
cliOverrides.awLogDirectoryPath ??
|
|
@@ -35,6 +35,7 @@ import { UpdateIssueStatusByLabelUseCase } from '../../../domain/usecases/Update
|
|
|
35
35
|
import { StartPreparationUseCase } from '../../../domain/usecases/StartPreparationUseCase';
|
|
36
36
|
import { NodeLocalCommandRunner } from '../../repositories/NodeLocalCommandRunner';
|
|
37
37
|
import { OauthAPIProxyClaudeRepository } from '../../repositories/OauthAPIProxyClaudeRepository';
|
|
38
|
+
import { ProxyClaudeTokenUsageRepository } from '../../repositories/ProxyClaudeTokenUsageRepository';
|
|
38
39
|
import { RevertOrphanedPreparationUseCase } from '../../../domain/usecases/RevertOrphanedPreparationUseCase';
|
|
39
40
|
import { RevertNotReadyAwaitingQualityCheckUseCase } from '../../../domain/usecases/RevertNotReadyAwaitingQualityCheckUseCase';
|
|
40
41
|
import { GitHubIssueCommentRepository } from '../../repositories/GitHubIssueCommentRepository';
|
|
@@ -217,11 +218,15 @@ export class HandleScheduledEventUseCaseHandler {
|
|
|
217
218
|
);
|
|
218
219
|
const nodeLocalCommandRunner = new NodeLocalCommandRunner();
|
|
219
220
|
const claudeRepository = new OauthAPIProxyClaudeRepository();
|
|
221
|
+
const claudeTokenUsageRepository = new ProxyClaudeTokenUsageRepository(
|
|
222
|
+
null,
|
|
223
|
+
);
|
|
220
224
|
const startPreparationUseCase = new StartPreparationUseCase(
|
|
221
225
|
projectRepository,
|
|
222
226
|
issueRepository,
|
|
223
227
|
claudeRepository,
|
|
224
228
|
nodeLocalCommandRunner,
|
|
229
|
+
claudeTokenUsageRepository,
|
|
225
230
|
);
|
|
226
231
|
const issueCommentRepository = new GitHubIssueCommentRepository(
|
|
227
232
|
input.credentials.bot.github.token,
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as os from 'os';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import {
|
|
5
|
+
cacheDir,
|
|
6
|
+
cachePathForToken,
|
|
7
|
+
hashToken,
|
|
8
|
+
readRateLimit,
|
|
9
|
+
writeRateLimit,
|
|
10
|
+
} from './RateLimitCache';
|
|
11
|
+
|
|
12
|
+
describe('RateLimitCache', () => {
|
|
13
|
+
let tempDir: string;
|
|
14
|
+
const originalXdg = process.env.XDG_CACHE_HOME;
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'ratelimit-cache-'));
|
|
18
|
+
process.env.XDG_CACHE_HOME = tempDir;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
if (originalXdg === undefined) {
|
|
23
|
+
delete process.env.XDG_CACHE_HOME;
|
|
24
|
+
} else {
|
|
25
|
+
process.env.XDG_CACHE_HOME = originalXdg;
|
|
26
|
+
}
|
|
27
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('hashToken', () => {
|
|
31
|
+
it('should be deterministic for the same input', () => {
|
|
32
|
+
expect(hashToken('token-a')).toBe(hashToken('token-a'));
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should differ for different inputs', () => {
|
|
36
|
+
expect(hashToken('token-a')).not.toBe(hashToken('token-b'));
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should return a 64-character hex string for sha256', () => {
|
|
40
|
+
const hash = hashToken('token-a');
|
|
41
|
+
expect(hash).toMatch(/^[0-9a-f]{64}$/);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('cacheDir', () => {
|
|
46
|
+
it('should honor XDG_CACHE_HOME', () => {
|
|
47
|
+
expect(cacheDir()).toBe(path.join(tempDir, 'tdpm', 'ratelimit'));
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should default to ~/.cache when XDG_CACHE_HOME is unset', () => {
|
|
51
|
+
delete process.env.XDG_CACHE_HOME;
|
|
52
|
+
expect(cacheDir()).toBe(
|
|
53
|
+
path.join(os.homedir(), '.cache', 'tdpm', 'ratelimit'),
|
|
54
|
+
);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('cachePathForToken', () => {
|
|
59
|
+
it('should place the file under cacheDir with the token hash as filename', () => {
|
|
60
|
+
const token = 'token-x';
|
|
61
|
+
expect(cachePathForToken(token)).toBe(
|
|
62
|
+
path.join(cacheDir(), `${hashToken(token)}.json`),
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe('writeRateLimit and readRateLimit', () => {
|
|
68
|
+
it('should round-trip rate limit headers', () => {
|
|
69
|
+
const token = 'roundtrip-token';
|
|
70
|
+
writeRateLimit(token, {
|
|
71
|
+
'anthropic-ratelimit-unified-status': 'allowed',
|
|
72
|
+
'anthropic-ratelimit-unified-5h-status': 'allowed',
|
|
73
|
+
'anthropic-ratelimit-unified-5h-reset': '1700000000',
|
|
74
|
+
'anthropic-ratelimit-unified-5h-utilization': '42',
|
|
75
|
+
'anthropic-ratelimit-unified-7d-status': 'allowed',
|
|
76
|
+
'anthropic-ratelimit-unified-7d-reset': '1700100000',
|
|
77
|
+
'anthropic-ratelimit-unified-7d-utilization': '17',
|
|
78
|
+
});
|
|
79
|
+
const snapshot = readRateLimit(token);
|
|
80
|
+
expect(snapshot).not.toBeNull();
|
|
81
|
+
if (snapshot === null) return;
|
|
82
|
+
expect(snapshot.fiveHourUtilization).toBe(42);
|
|
83
|
+
expect(snapshot.fiveHourReset).toBe(1700000000);
|
|
84
|
+
expect(snapshot.sevenDayUtilization).toBe(17);
|
|
85
|
+
expect(snapshot.sevenDayReset).toBe(1700100000);
|
|
86
|
+
expect(snapshot.blocked).toBe(false);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should mark snapshot as blocked when status header is blocked', () => {
|
|
90
|
+
const token = 'blocked-token';
|
|
91
|
+
writeRateLimit(token, {
|
|
92
|
+
'anthropic-ratelimit-unified-status': 'blocked',
|
|
93
|
+
'anthropic-ratelimit-unified-5h-status': 'allowed',
|
|
94
|
+
'anthropic-ratelimit-unified-5h-reset': '1700000000',
|
|
95
|
+
'anthropic-ratelimit-unified-5h-utilization': '100',
|
|
96
|
+
'anthropic-ratelimit-unified-7d-status': 'allowed',
|
|
97
|
+
'anthropic-ratelimit-unified-7d-reset': '1700100000',
|
|
98
|
+
'anthropic-ratelimit-unified-7d-utilization': '99',
|
|
99
|
+
});
|
|
100
|
+
const snapshot = readRateLimit(token);
|
|
101
|
+
expect(snapshot?.blocked).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should return null when file does not exist', () => {
|
|
105
|
+
expect(readRateLimit('never-written-token')).toBeNull();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should return null when file content is malformed JSON', () => {
|
|
109
|
+
const token = 'malformed-token';
|
|
110
|
+
fs.mkdirSync(cacheDir(), { recursive: true });
|
|
111
|
+
fs.writeFileSync(cachePathForToken(token), '{not json');
|
|
112
|
+
expect(readRateLimit(token)).toBeNull();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should treat array header values as the first element', () => {
|
|
116
|
+
const token = 'array-token';
|
|
117
|
+
writeRateLimit(token, {
|
|
118
|
+
'anthropic-ratelimit-unified-status': 'allowed',
|
|
119
|
+
'anthropic-ratelimit-unified-5h-status': 'allowed',
|
|
120
|
+
'anthropic-ratelimit-unified-5h-reset': ['1700000000', 'ignored'],
|
|
121
|
+
'anthropic-ratelimit-unified-5h-utilization': ['55', 'ignored'],
|
|
122
|
+
'anthropic-ratelimit-unified-7d-status': 'allowed',
|
|
123
|
+
'anthropic-ratelimit-unified-7d-reset': '1700100000',
|
|
124
|
+
'anthropic-ratelimit-unified-7d-utilization': '10',
|
|
125
|
+
});
|
|
126
|
+
const snapshot = readRateLimit(token);
|
|
127
|
+
expect(snapshot?.fiveHourUtilization).toBe(55);
|
|
128
|
+
expect(snapshot?.fiveHourReset).toBe(1700000000);
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as crypto from 'crypto';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
|
|
6
|
+
export interface RateLimitSnapshot {
|
|
7
|
+
fiveHourUtilization: number;
|
|
8
|
+
fiveHourReset: number;
|
|
9
|
+
sevenDayUtilization: number;
|
|
10
|
+
sevenDayReset: number;
|
|
11
|
+
blocked: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const PROXY_PORT = 8787;
|
|
15
|
+
|
|
16
|
+
const HASH_ALGORITHM = 'sha256';
|
|
17
|
+
|
|
18
|
+
export const cacheDir = (): string => {
|
|
19
|
+
const base = process.env.XDG_CACHE_HOME ?? path.join(os.homedir(), '.cache');
|
|
20
|
+
return path.join(base, 'tdpm', 'ratelimit');
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const hashToken = (token: string): string =>
|
|
24
|
+
crypto.createHash(HASH_ALGORITHM).update(token).digest('hex');
|
|
25
|
+
|
|
26
|
+
export const cachePathForToken = (token: string): string =>
|
|
27
|
+
path.join(cacheDir(), `${hashToken(token)}.json`);
|
|
28
|
+
|
|
29
|
+
export const writeRateLimit = (
|
|
30
|
+
token: string,
|
|
31
|
+
headers: Record<string, string | string[] | undefined>,
|
|
32
|
+
): void => {
|
|
33
|
+
const dir = cacheDir();
|
|
34
|
+
if (!fs.existsSync(dir)) {
|
|
35
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
const pick = (key: string): string | undefined => {
|
|
38
|
+
const value = headers[key];
|
|
39
|
+
if (Array.isArray(value)) return value[0];
|
|
40
|
+
return value;
|
|
41
|
+
};
|
|
42
|
+
const payload = {
|
|
43
|
+
ts: Date.now() / 1000,
|
|
44
|
+
headers: {
|
|
45
|
+
'anthropic-ratelimit-unified-status': pick(
|
|
46
|
+
'anthropic-ratelimit-unified-status',
|
|
47
|
+
),
|
|
48
|
+
'anthropic-ratelimit-unified-5h-status': pick(
|
|
49
|
+
'anthropic-ratelimit-unified-5h-status',
|
|
50
|
+
),
|
|
51
|
+
'anthropic-ratelimit-unified-5h-reset': pick(
|
|
52
|
+
'anthropic-ratelimit-unified-5h-reset',
|
|
53
|
+
),
|
|
54
|
+
'anthropic-ratelimit-unified-5h-utilization': pick(
|
|
55
|
+
'anthropic-ratelimit-unified-5h-utilization',
|
|
56
|
+
),
|
|
57
|
+
'anthropic-ratelimit-unified-7d-status': pick(
|
|
58
|
+
'anthropic-ratelimit-unified-7d-status',
|
|
59
|
+
),
|
|
60
|
+
'anthropic-ratelimit-unified-7d-reset': pick(
|
|
61
|
+
'anthropic-ratelimit-unified-7d-reset',
|
|
62
|
+
),
|
|
63
|
+
'anthropic-ratelimit-unified-7d-utilization': pick(
|
|
64
|
+
'anthropic-ratelimit-unified-7d-utilization',
|
|
65
|
+
),
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
const filePath = path.join(dir, `${hashToken(token)}.json`);
|
|
69
|
+
fs.writeFileSync(filePath, JSON.stringify(payload));
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const readRateLimit = (token: string): RateLimitSnapshot | null => {
|
|
73
|
+
const filePath = cachePathForToken(token);
|
|
74
|
+
if (!fs.existsSync(filePath)) return null;
|
|
75
|
+
try {
|
|
76
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
77
|
+
const parsed: unknown = JSON.parse(raw);
|
|
78
|
+
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
79
|
+
value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
80
|
+
if (!isRecord(parsed)) return null;
|
|
81
|
+
const headersUnknown = parsed.headers;
|
|
82
|
+
const headers: Record<string, string> = {};
|
|
83
|
+
if (isRecord(headersUnknown)) {
|
|
84
|
+
for (const [key, value] of Object.entries(headersUnknown)) {
|
|
85
|
+
if (typeof value === 'string') {
|
|
86
|
+
headers[key] = value;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const num = (key: string): number => {
|
|
91
|
+
const value = headers[key];
|
|
92
|
+
if (typeof value !== 'string') return 0;
|
|
93
|
+
const parsedValue = Number(value);
|
|
94
|
+
return Number.isFinite(parsedValue) ? parsedValue : 0;
|
|
95
|
+
};
|
|
96
|
+
const status = headers['anthropic-ratelimit-unified-status'];
|
|
97
|
+
const fiveHourStatus = headers['anthropic-ratelimit-unified-5h-status'];
|
|
98
|
+
const sevenDayStatus = headers['anthropic-ratelimit-unified-7d-status'];
|
|
99
|
+
return {
|
|
100
|
+
fiveHourUtilization: num('anthropic-ratelimit-unified-5h-utilization'),
|
|
101
|
+
fiveHourReset: num('anthropic-ratelimit-unified-5h-reset'),
|
|
102
|
+
sevenDayUtilization: num('anthropic-ratelimit-unified-7d-utilization'),
|
|
103
|
+
sevenDayReset: num('anthropic-ratelimit-unified-7d-reset'),
|
|
104
|
+
blocked:
|
|
105
|
+
status === 'blocked' ||
|
|
106
|
+
fiveHourStatus === 'blocked' ||
|
|
107
|
+
sevenDayStatus === 'blocked',
|
|
108
|
+
};
|
|
109
|
+
} catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as os from 'os';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { loadTokens } from './TokenListLoader';
|
|
5
|
+
|
|
6
|
+
describe('TokenListLoader', () => {
|
|
7
|
+
let tempDir: string;
|
|
8
|
+
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'token-list-loader-'));
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
afterEach(() => {
|
|
14
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('should return tokens in the order they appear in the JSON file', () => {
|
|
18
|
+
const filePath = path.join(tempDir, 'tokens.json');
|
|
19
|
+
fs.writeFileSync(
|
|
20
|
+
filePath,
|
|
21
|
+
JSON.stringify([
|
|
22
|
+
{ name: 'first', token: 'token-1' },
|
|
23
|
+
{ name: 'second', token: 'token-2' },
|
|
24
|
+
]),
|
|
25
|
+
);
|
|
26
|
+
expect(loadTokens(filePath)).toEqual(['token-1', 'token-2']);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should return null when the file does not exist', () => {
|
|
30
|
+
expect(loadTokens(path.join(tempDir, 'missing.json'))).toBeNull();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should return null when the JSON root is not an array', () => {
|
|
34
|
+
const filePath = path.join(tempDir, 'object.json');
|
|
35
|
+
fs.writeFileSync(filePath, JSON.stringify({ token: 'token-1' }));
|
|
36
|
+
expect(loadTokens(filePath)).toBeNull();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should return null when JSON parsing fails', () => {
|
|
40
|
+
const filePath = path.join(tempDir, 'malformed.json');
|
|
41
|
+
fs.writeFileSync(filePath, '{not json');
|
|
42
|
+
expect(loadTokens(filePath)).toBeNull();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should skip entries that do not contain a string token', () => {
|
|
46
|
+
const filePath = path.join(tempDir, 'mixed.json');
|
|
47
|
+
fs.writeFileSync(
|
|
48
|
+
filePath,
|
|
49
|
+
JSON.stringify([
|
|
50
|
+
{ name: 'no-token' },
|
|
51
|
+
{ name: 'first', token: 'token-1' },
|
|
52
|
+
{ name: 'numeric', token: 123 },
|
|
53
|
+
{ name: 'second', token: 'token-2' },
|
|
54
|
+
]),
|
|
55
|
+
);
|
|
56
|
+
expect(loadTokens(filePath)).toEqual(['token-1', 'token-2']);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should return null when every entry is invalid', () => {
|
|
60
|
+
const filePath = path.join(tempDir, 'no-valid-entries.json');
|
|
61
|
+
fs.writeFileSync(
|
|
62
|
+
filePath,
|
|
63
|
+
JSON.stringify([{ name: 'no-token' }, { token: 42 }]),
|
|
64
|
+
);
|
|
65
|
+
expect(loadTokens(filePath)).toBeNull();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should expand a leading tilde to the home directory', () => {
|
|
69
|
+
const home = os.homedir();
|
|
70
|
+
const fileName = `tdpm-loader-${process.pid}-${Date.now()}.json`;
|
|
71
|
+
const targetPath = path.join(home, fileName);
|
|
72
|
+
fs.writeFileSync(
|
|
73
|
+
targetPath,
|
|
74
|
+
JSON.stringify([{ name: 'first', token: 'home-token' }]),
|
|
75
|
+
);
|
|
76
|
+
try {
|
|
77
|
+
expect(loadTokens(`~/${fileName}`)).toEqual(['home-token']);
|
|
78
|
+
} finally {
|
|
79
|
+
fs.unlinkSync(targetPath);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|