github-issue-tower-defence-management 1.60.2 → 1.63.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/.github/workflows/publish.yml +13 -0
- package/.github/workflows/test.yml +0 -4
- package/CHANGELOG.md +7 -0
- package/README.md +53 -10
- package/bin/adapter/entry-points/cli/index.js +11 -11
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.js +3 -22
- package/bin/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +8 -22
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/rotationOrderFileWriter.js +56 -0
- package/bin/adapter/entry-points/handlers/rotationOrderFileWriter.js.map +1 -0
- package/bin/adapter/entry-points/handlers/situationFileWriter.js +5 -0
- package/bin/adapter/entry-points/handlers/situationFileWriter.js.map +1 -1
- package/bin/adapter/proxy/TokenListLoader.js +21 -6
- package/bin/adapter/proxy/TokenListLoader.js.map +1 -1
- package/bin/adapter/proxy/proxyEntry.js +1 -0
- package/bin/adapter/proxy/proxyEntry.js.map +1 -1
- package/bin/adapter/repositories/BaseGitHubRepository.js +1 -113
- package/bin/adapter/repositories/BaseGitHubRepository.js.map +1 -1
- package/bin/adapter/repositories/ProxyClaudeTokenUsageRepository.js +5 -3
- package/bin/adapter/repositories/ProxyClaudeTokenUsageRepository.js.map +1 -1
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +8 -7
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
- package/bin/domain/usecases/HandleScheduledEventUseCase.js +14 -3
- package/bin/domain/usecases/HandleScheduledEventUseCase.js.map +1 -1
- package/bin/domain/usecases/IssueRejectionEvaluator.js +8 -1
- package/bin/domain/usecases/IssueRejectionEvaluator.js.map +1 -1
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js +5 -1
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js +1 -1
- package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.js +32 -1
- package/bin/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.js.map +1 -1
- package/bin/domain/usecases/StartPreparationUseCase.js +91 -12
- package/bin/domain/usecases/StartPreparationUseCase.js.map +1 -1
- package/package.json +1 -4
- package/src/adapter/entry-points/cli/index.test.ts +16 -16
- package/src/adapter/entry-points/cli/index.ts +8 -11
- package/src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.test.ts +2 -55
- package/src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.ts +1 -11
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.test.ts +6 -56
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +7 -11
- package/src/adapter/entry-points/handlers/rotationOrderFileWriter.test.ts +177 -0
- package/src/adapter/entry-points/handlers/rotationOrderFileWriter.ts +20 -0
- package/src/adapter/entry-points/handlers/situationFileWriter.test.ts +36 -0
- package/src/adapter/entry-points/handlers/situationFileWriter.ts +8 -0
- package/src/adapter/proxy/TokenListLoader.test.ts +50 -1
- package/src/adapter/proxy/TokenListLoader.ts +25 -5
- package/src/adapter/proxy/proxyEntry.test.ts +270 -1
- package/src/adapter/proxy/proxyEntry.ts +2 -1
- package/src/adapter/repositories/BaseGitHubRepository.test.ts +1 -186
- package/src/adapter/repositories/BaseGitHubRepository.ts +1 -139
- package/src/adapter/repositories/GraphqlProjectRepository.errorHandling.test.ts +0 -1
- package/src/adapter/repositories/GraphqlProjectRepository.fetchProjectId.test.ts +4 -1
- package/src/adapter/repositories/ProxyClaudeTokenUsageRepository.test.ts +60 -19
- package/src/adapter/repositories/ProxyClaudeTokenUsageRepository.ts +6 -4
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +23 -13
- package/src/adapter/repositories/issue/ApiV3IssueRepository.test.ts +0 -1
- package/src/adapter/repositories/issue/GraphqlProjectItemRepository.test.ts +0 -8
- package/src/adapter/repositories/issue/RestIssueRepository.test.ts +0 -1
- package/src/domain/entities/ClaudeTokenUsage.ts +1 -0
- package/src/domain/usecases/HandleScheduledEventUseCase.test.ts +4 -0
- package/src/domain/usecases/HandleScheduledEventUseCase.ts +20 -5
- package/src/domain/usecases/IssueRejectionEvaluator.test.ts +153 -0
- package/src/domain/usecases/IssueRejectionEvaluator.ts +8 -0
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +175 -31
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +7 -1
- package/src/domain/usecases/RevertNotReadyAwaitingQualityCheckUseCase.test.ts +32 -0
- package/src/domain/usecases/RevertOrphanedPreparationUseCase.test.ts +39 -5
- package/src/domain/usecases/RevertOrphanedPreparationUseCase.ts +1 -1
- package/src/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.test.ts +139 -20
- package/src/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.ts +62 -2
- package/src/domain/usecases/StartPreparationUseCase.test.ts +404 -21
- package/src/domain/usecases/StartPreparationUseCase.ts +152 -16
- package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +16 -0
- package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/rotationOrderFileWriter.d.ts +3 -0
- package/types/adapter/entry-points/handlers/rotationOrderFileWriter.d.ts.map +1 -0
- package/types/adapter/entry-points/handlers/situationFileWriter.d.ts +1 -0
- package/types/adapter/entry-points/handlers/situationFileWriter.d.ts.map +1 -1
- package/types/adapter/proxy/TokenListLoader.d.ts +5 -0
- package/types/adapter/proxy/TokenListLoader.d.ts.map +1 -1
- package/types/adapter/proxy/proxyEntry.d.ts +2 -1
- package/types/adapter/proxy/proxyEntry.d.ts.map +1 -1
- package/types/adapter/repositories/BaseGitHubRepository.d.ts +1 -23
- package/types/adapter/repositories/BaseGitHubRepository.d.ts.map +1 -1
- package/types/adapter/repositories/ProxyClaudeTokenUsageRepository.d.ts.map +1 -1
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts +14 -5
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
- package/types/domain/entities/ClaudeTokenUsage.d.ts +1 -0
- package/types/domain/entities/ClaudeTokenUsage.d.ts.map +1 -1
- package/types/domain/usecases/HandleScheduledEventUseCase.d.ts +5 -2
- package/types/domain/usecases/HandleScheduledEventUseCase.d.ts.map +1 -1
- package/types/domain/usecases/IssueRejectionEvaluator.d.ts +1 -1
- package/types/domain/usecases/IssueRejectionEvaluator.d.ts.map +1 -1
- package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.d.ts +5 -2
- package/types/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.d.ts.map +1 -1
- package/types/domain/usecases/StartPreparationUseCase.d.ts +15 -1
- package/types/domain/usecases/StartPreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts +14 -0
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts.map +1 -1
- package/bin/adapter/repositories/issue/CheerioIssueRepository.js +0 -136
- package/bin/adapter/repositories/issue/CheerioIssueRepository.js.map +0 -1
- package/bin/adapter/repositories/issue/InternalGraphqlIssueRepository.js +0 -1606
- package/bin/adapter/repositories/issue/InternalGraphqlIssueRepository.js.map +0 -1
- package/src/adapter/repositories/issue/CheerioIssueRepository.test.ts +0 -6552
- package/src/adapter/repositories/issue/CheerioIssueRepository.ts +0 -142
- package/src/adapter/repositories/issue/InternalGraphqlIssueRepository.test.ts +0 -118
- package/src/adapter/repositories/issue/InternalGraphqlIssueRepository.ts +0 -584
- package/types/adapter/repositories/issue/CheerioIssueRepository.d.ts +0 -40
- package/types/adapter/repositories/issue/CheerioIssueRepository.d.ts.map +0 -1
- package/types/adapter/repositories/issue/InternalGraphqlIssueRepository.d.ts +0 -220
- package/types/adapter/repositories/issue/InternalGraphqlIssueRepository.d.ts.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const mockEnsureProxyRunning = jest.fn();
|
|
2
2
|
const mockReadRateLimit = jest.fn();
|
|
3
|
-
const
|
|
3
|
+
const mockLoadTokenEntries = jest.fn();
|
|
4
4
|
|
|
5
5
|
jest.mock('../proxy/ensureProxyRunning', () => ({
|
|
6
6
|
ensureProxyRunning: mockEnsureProxyRunning,
|
|
@@ -12,7 +12,7 @@ jest.mock('../proxy/RateLimitCache', () => ({
|
|
|
12
12
|
}));
|
|
13
13
|
|
|
14
14
|
jest.mock('../proxy/TokenListLoader', () => ({
|
|
15
|
-
|
|
15
|
+
loadTokenEntries: mockLoadTokenEntries,
|
|
16
16
|
}));
|
|
17
17
|
|
|
18
18
|
import { ProxyClaudeTokenUsageRepository } from './ProxyClaudeTokenUsageRepository';
|
|
@@ -52,24 +52,27 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
52
52
|
const result = await repository.getAvailableTokenUsages();
|
|
53
53
|
|
|
54
54
|
expect(result).toEqual([]);
|
|
55
|
-
expect(
|
|
55
|
+
expect(mockLoadTokenEntries.mock.calls).toHaveLength(0);
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
it('should return an empty list when the token list cannot be loaded', async () => {
|
|
59
|
-
|
|
59
|
+
mockLoadTokenEntries.mockReturnValue(null);
|
|
60
60
|
const repository = new ProxyClaudeTokenUsageRepository('/tokens.json');
|
|
61
61
|
|
|
62
62
|
const result = await repository.getAvailableTokenUsages();
|
|
63
63
|
|
|
64
64
|
expect(result).toEqual([]);
|
|
65
|
-
expect(
|
|
65
|
+
expect(mockLoadTokenEntries.mock.calls).toEqual([['/tokens.json']]);
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
const futureReset = Math.floor(Date.now() / 1000) + 3600;
|
|
69
69
|
const pastReset = Math.floor(Date.now() / 1000) - 3600;
|
|
70
70
|
|
|
71
|
-
it('should map each token to its cached utilization', async () => {
|
|
72
|
-
|
|
71
|
+
it('should map each token to its cached utilization and include name', async () => {
|
|
72
|
+
mockLoadTokenEntries.mockReturnValue([
|
|
73
|
+
{ name: 'alice', token: 'token-a' },
|
|
74
|
+
{ name: 'bob', token: 'token-b' },
|
|
75
|
+
]);
|
|
73
76
|
mockReadRateLimit.mockImplementation((token: string) => {
|
|
74
77
|
if (token === 'token-a') {
|
|
75
78
|
return {
|
|
@@ -93,6 +96,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
93
96
|
|
|
94
97
|
expect(result).toEqual([
|
|
95
98
|
{
|
|
99
|
+
name: 'alice',
|
|
96
100
|
token: 'token-a',
|
|
97
101
|
fiveHourUtilization: 42,
|
|
98
102
|
blocked: false,
|
|
@@ -100,6 +104,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
100
104
|
modelWeeklyLimits: {},
|
|
101
105
|
},
|
|
102
106
|
{
|
|
107
|
+
name: 'bob',
|
|
103
108
|
token: 'token-b',
|
|
104
109
|
fiveHourUtilization: 0,
|
|
105
110
|
blocked: false,
|
|
@@ -110,7 +115,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
110
115
|
});
|
|
111
116
|
|
|
112
117
|
it('should propagate the blocked status from the cache', async () => {
|
|
113
|
-
|
|
118
|
+
mockLoadTokenEntries.mockReturnValue([
|
|
119
|
+
{ name: 'alice', token: 'token-a' },
|
|
120
|
+
]);
|
|
114
121
|
mockReadRateLimit.mockReturnValue({
|
|
115
122
|
fiveHourUtilization: 5,
|
|
116
123
|
fiveHourReset: futureReset,
|
|
@@ -129,6 +136,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
129
136
|
|
|
130
137
|
expect(result).toEqual([
|
|
131
138
|
{
|
|
139
|
+
name: 'alice',
|
|
132
140
|
token: 'token-a',
|
|
133
141
|
fiveHourUtilization: 5,
|
|
134
142
|
blocked: true,
|
|
@@ -139,7 +147,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
139
147
|
});
|
|
140
148
|
|
|
141
149
|
it('should propagate the rejected status from the cache', async () => {
|
|
142
|
-
|
|
150
|
+
mockLoadTokenEntries.mockReturnValue([
|
|
151
|
+
{ name: 'alice', token: 'token-a' },
|
|
152
|
+
]);
|
|
143
153
|
mockReadRateLimit.mockReturnValue({
|
|
144
154
|
fiveHourUtilization: 100,
|
|
145
155
|
fiveHourReset: futureReset,
|
|
@@ -158,6 +168,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
158
168
|
|
|
159
169
|
expect(result).toEqual([
|
|
160
170
|
{
|
|
171
|
+
name: 'alice',
|
|
161
172
|
token: 'token-a',
|
|
162
173
|
fiveHourUtilization: 100,
|
|
163
174
|
blocked: false,
|
|
@@ -168,7 +179,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
168
179
|
});
|
|
169
180
|
|
|
170
181
|
it('should normalize fiveHourUtilization to 0 when the 5h reset has passed', async () => {
|
|
171
|
-
|
|
182
|
+
mockLoadTokenEntries.mockReturnValue([
|
|
183
|
+
{ name: 'alice', token: 'token-a' },
|
|
184
|
+
]);
|
|
172
185
|
mockReadRateLimit.mockReturnValue({
|
|
173
186
|
fiveHourUtilization: 100,
|
|
174
187
|
fiveHourReset: pastReset,
|
|
@@ -187,6 +200,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
187
200
|
|
|
188
201
|
expect(result).toEqual([
|
|
189
202
|
{
|
|
203
|
+
name: 'alice',
|
|
190
204
|
token: 'token-a',
|
|
191
205
|
fiveHourUtilization: 0,
|
|
192
206
|
blocked: false,
|
|
@@ -197,7 +211,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
197
211
|
});
|
|
198
212
|
|
|
199
213
|
it('should keep fiveHourUtilization when the 5h reset is in the future', async () => {
|
|
200
|
-
|
|
214
|
+
mockLoadTokenEntries.mockReturnValue([
|
|
215
|
+
{ name: 'alice', token: 'token-a' },
|
|
216
|
+
]);
|
|
201
217
|
mockReadRateLimit.mockReturnValue({
|
|
202
218
|
fiveHourUtilization: 95,
|
|
203
219
|
fiveHourReset: futureReset,
|
|
@@ -216,6 +232,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
216
232
|
|
|
217
233
|
expect(result).toEqual([
|
|
218
234
|
{
|
|
235
|
+
name: 'alice',
|
|
219
236
|
token: 'token-a',
|
|
220
237
|
fiveHourUtilization: 95,
|
|
221
238
|
blocked: false,
|
|
@@ -226,7 +243,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
226
243
|
});
|
|
227
244
|
|
|
228
245
|
it('should clear a 5h-origin rejection once the 5h reset has passed', async () => {
|
|
229
|
-
|
|
246
|
+
mockLoadTokenEntries.mockReturnValue([
|
|
247
|
+
{ name: 'alice', token: 'token-a' },
|
|
248
|
+
]);
|
|
230
249
|
mockReadRateLimit.mockReturnValue({
|
|
231
250
|
fiveHourUtilization: 100,
|
|
232
251
|
fiveHourReset: pastReset,
|
|
@@ -245,6 +264,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
245
264
|
|
|
246
265
|
expect(result).toEqual([
|
|
247
266
|
{
|
|
267
|
+
name: 'alice',
|
|
248
268
|
token: 'token-a',
|
|
249
269
|
fiveHourUtilization: 0,
|
|
250
270
|
blocked: false,
|
|
@@ -255,7 +275,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
255
275
|
});
|
|
256
276
|
|
|
257
277
|
it('should clear a 7d-origin rejection once the 7d reset has passed', async () => {
|
|
258
|
-
|
|
278
|
+
mockLoadTokenEntries.mockReturnValue([
|
|
279
|
+
{ name: 'alice', token: 'token-a' },
|
|
280
|
+
]);
|
|
259
281
|
mockReadRateLimit.mockReturnValue({
|
|
260
282
|
fiveHourUtilization: 10,
|
|
261
283
|
fiveHourReset: futureReset,
|
|
@@ -274,6 +296,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
274
296
|
|
|
275
297
|
expect(result).toEqual([
|
|
276
298
|
{
|
|
299
|
+
name: 'alice',
|
|
277
300
|
token: 'token-a',
|
|
278
301
|
fiveHourUtilization: 10,
|
|
279
302
|
blocked: false,
|
|
@@ -284,7 +307,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
284
307
|
});
|
|
285
308
|
|
|
286
309
|
it('should keep a 5h-origin rejection while the 5h reset is in the future', async () => {
|
|
287
|
-
|
|
310
|
+
mockLoadTokenEntries.mockReturnValue([
|
|
311
|
+
{ name: 'alice', token: 'token-a' },
|
|
312
|
+
]);
|
|
288
313
|
mockReadRateLimit.mockReturnValue({
|
|
289
314
|
fiveHourUtilization: 100,
|
|
290
315
|
fiveHourReset: futureReset,
|
|
@@ -303,6 +328,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
303
328
|
|
|
304
329
|
expect(result).toEqual([
|
|
305
330
|
{
|
|
331
|
+
name: 'alice',
|
|
306
332
|
token: 'token-a',
|
|
307
333
|
fiveHourUtilization: 100,
|
|
308
334
|
blocked: false,
|
|
@@ -313,7 +339,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
313
339
|
});
|
|
314
340
|
|
|
315
341
|
it('should keep a still-active 7d rejection after the 5h reset has passed', async () => {
|
|
316
|
-
|
|
342
|
+
mockLoadTokenEntries.mockReturnValue([
|
|
343
|
+
{ name: 'alice', token: 'token-a' },
|
|
344
|
+
]);
|
|
317
345
|
mockReadRateLimit.mockReturnValue({
|
|
318
346
|
fiveHourUtilization: 100,
|
|
319
347
|
fiveHourReset: pastReset,
|
|
@@ -332,6 +360,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
332
360
|
|
|
333
361
|
expect(result).toEqual([
|
|
334
362
|
{
|
|
363
|
+
name: 'alice',
|
|
335
364
|
token: 'token-a',
|
|
336
365
|
fiveHourUtilization: 0,
|
|
337
366
|
blocked: false,
|
|
@@ -342,7 +371,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
342
371
|
});
|
|
343
372
|
|
|
344
373
|
it('should clear a unified rejection once the 5h reset has passed', async () => {
|
|
345
|
-
|
|
374
|
+
mockLoadTokenEntries.mockReturnValue([
|
|
375
|
+
{ name: 'alice', token: 'token-a' },
|
|
376
|
+
]);
|
|
346
377
|
mockReadRateLimit.mockReturnValue({
|
|
347
378
|
fiveHourUtilization: 100,
|
|
348
379
|
fiveHourReset: pastReset,
|
|
@@ -361,6 +392,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
361
392
|
|
|
362
393
|
expect(result).toEqual([
|
|
363
394
|
{
|
|
395
|
+
name: 'alice',
|
|
364
396
|
token: 'token-a',
|
|
365
397
|
fiveHourUtilization: 0,
|
|
366
398
|
blocked: false,
|
|
@@ -371,7 +403,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
371
403
|
});
|
|
372
404
|
|
|
373
405
|
it('should default rejected to false when no snapshot exists', async () => {
|
|
374
|
-
|
|
406
|
+
mockLoadTokenEntries.mockReturnValue([
|
|
407
|
+
{ name: 'alice', token: 'token-a' },
|
|
408
|
+
]);
|
|
375
409
|
mockReadRateLimit.mockReturnValue(null);
|
|
376
410
|
const repository = new ProxyClaudeTokenUsageRepository('/tokens.json');
|
|
377
411
|
|
|
@@ -379,6 +413,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
379
413
|
|
|
380
414
|
expect(result).toEqual([
|
|
381
415
|
{
|
|
416
|
+
name: 'alice',
|
|
382
417
|
token: 'token-a',
|
|
383
418
|
fiveHourUtilization: 0,
|
|
384
419
|
blocked: false,
|
|
@@ -389,7 +424,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
389
424
|
});
|
|
390
425
|
|
|
391
426
|
it('should keep a model weekly rejection while its reset is in the future', async () => {
|
|
392
|
-
|
|
427
|
+
mockLoadTokenEntries.mockReturnValue([
|
|
428
|
+
{ name: 'alice', token: 'token-a' },
|
|
429
|
+
]);
|
|
393
430
|
mockReadRateLimit.mockReturnValue({
|
|
394
431
|
fiveHourUtilization: 5,
|
|
395
432
|
fiveHourReset: futureReset,
|
|
@@ -410,6 +447,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
410
447
|
|
|
411
448
|
expect(result).toEqual([
|
|
412
449
|
{
|
|
450
|
+
name: 'alice',
|
|
413
451
|
token: 'token-a',
|
|
414
452
|
fiveHourUtilization: 5,
|
|
415
453
|
blocked: false,
|
|
@@ -422,7 +460,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
422
460
|
});
|
|
423
461
|
|
|
424
462
|
it('should clear a model weekly rejection once its reset has passed', async () => {
|
|
425
|
-
|
|
463
|
+
mockLoadTokenEntries.mockReturnValue([
|
|
464
|
+
{ name: 'alice', token: 'token-a' },
|
|
465
|
+
]);
|
|
426
466
|
mockReadRateLimit.mockReturnValue({
|
|
427
467
|
fiveHourUtilization: 5,
|
|
428
468
|
fiveHourReset: futureReset,
|
|
@@ -443,6 +483,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
|
|
|
443
483
|
|
|
444
484
|
expect(result).toEqual([
|
|
445
485
|
{
|
|
486
|
+
name: 'alice',
|
|
446
487
|
token: 'token-a',
|
|
447
488
|
fiveHourUtilization: 5,
|
|
448
489
|
blocked: false,
|
|
@@ -2,7 +2,7 @@ import { ClaudeTokenUsage } from '../../domain/entities/ClaudeTokenUsage';
|
|
|
2
2
|
import { ClaudeTokenUsageRepository } from '../../domain/usecases/adapter-interfaces/ClaudeTokenUsageRepository';
|
|
3
3
|
import { ensureProxyRunning } from '../proxy/ensureProxyRunning';
|
|
4
4
|
import { PROXY_PORT, readRateLimit } from '../proxy/RateLimitCache';
|
|
5
|
-
import {
|
|
5
|
+
import { loadTokenEntries } from '../proxy/TokenListLoader';
|
|
6
6
|
|
|
7
7
|
export class ProxyClaudeTokenUsageRepository implements ClaudeTokenUsageRepository {
|
|
8
8
|
constructor(
|
|
@@ -18,15 +18,16 @@ export class ProxyClaudeTokenUsageRepository implements ClaudeTokenUsageReposito
|
|
|
18
18
|
if (this.tokenListJsonPath === null) {
|
|
19
19
|
return [];
|
|
20
20
|
}
|
|
21
|
-
const
|
|
22
|
-
if (
|
|
21
|
+
const entries = loadTokenEntries(this.tokenListJsonPath);
|
|
22
|
+
if (entries === null) {
|
|
23
23
|
return [];
|
|
24
24
|
}
|
|
25
25
|
const nowEpochSeconds = Date.now() / 1000;
|
|
26
|
-
return
|
|
26
|
+
return entries.map(({ name, token }) => {
|
|
27
27
|
const snapshot = readRateLimit(token);
|
|
28
28
|
if (snapshot === null) {
|
|
29
29
|
return {
|
|
30
|
+
name,
|
|
30
31
|
token,
|
|
31
32
|
fiveHourUtilization: 0,
|
|
32
33
|
blocked: false,
|
|
@@ -63,6 +64,7 @@ export class ProxyClaudeTokenUsageRepository implements ClaudeTokenUsageReposito
|
|
|
63
64
|
};
|
|
64
65
|
}
|
|
65
66
|
return {
|
|
67
|
+
name,
|
|
66
68
|
token,
|
|
67
69
|
fiveHourUtilization,
|
|
68
70
|
blocked: snapshot.blocked,
|
|
@@ -27,6 +27,7 @@ type TimelineItem = {
|
|
|
27
27
|
number?: number;
|
|
28
28
|
state?: string;
|
|
29
29
|
createdAt?: string;
|
|
30
|
+
isDraft?: boolean;
|
|
30
31
|
mergeable?: string;
|
|
31
32
|
headRefName?: string;
|
|
32
33
|
baseRefName?: string;
|
|
@@ -117,6 +118,7 @@ type IssueTimelineResponse = {
|
|
|
117
118
|
};
|
|
118
119
|
|
|
119
120
|
type PrStatusComputationData = {
|
|
121
|
+
isDraft?: boolean;
|
|
120
122
|
mergeable?: string;
|
|
121
123
|
baseRepository?: {
|
|
122
124
|
branchProtectionRules?: {
|
|
@@ -293,21 +295,9 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
293
295
|
'getLatest' | 'set'
|
|
294
296
|
>,
|
|
295
297
|
readonly localStorageRepository: LocalStorageRepository,
|
|
296
|
-
readonly jsonFilePath: string = './tmp/github.com.cookies.json',
|
|
297
298
|
readonly ghToken: string = process.env.GH_TOKEN || 'dummy',
|
|
298
|
-
readonly ghUserName: string | undefined = process.env.GH_USER_NAME,
|
|
299
|
-
readonly ghUserPassword: string | undefined = process.env.GH_USER_PASSWORD,
|
|
300
|
-
readonly ghAuthenticatorKey: string | undefined = process.env
|
|
301
|
-
.GH_AUTHENTICATOR_KEY,
|
|
302
299
|
) {
|
|
303
|
-
super(
|
|
304
|
-
localStorageRepository,
|
|
305
|
-
jsonFilePath,
|
|
306
|
-
ghToken,
|
|
307
|
-
ghUserName,
|
|
308
|
-
ghUserPassword,
|
|
309
|
-
ghAuthenticatorKey,
|
|
310
|
-
);
|
|
300
|
+
super(localStorageRepository, ghToken);
|
|
311
301
|
}
|
|
312
302
|
|
|
313
303
|
updateStatus: (
|
|
@@ -474,6 +464,23 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
474
464
|
labels,
|
|
475
465
|
);
|
|
476
466
|
};
|
|
467
|
+
searchIssue = async (query: {
|
|
468
|
+
owner: string;
|
|
469
|
+
repositoryName: string;
|
|
470
|
+
type?: 'issue' | 'pr';
|
|
471
|
+
state?: 'open' | 'closed' | 'all';
|
|
472
|
+
title?: string;
|
|
473
|
+
createdFrom?: string;
|
|
474
|
+
assignee?: string;
|
|
475
|
+
}): Promise<
|
|
476
|
+
{
|
|
477
|
+
url: string;
|
|
478
|
+
title: string;
|
|
479
|
+
number: string;
|
|
480
|
+
}[]
|
|
481
|
+
> => {
|
|
482
|
+
return await this.apiV3IssueRepository.searchIssue(query);
|
|
483
|
+
};
|
|
477
484
|
updateIssue = async (issue: Issue): Promise<void> => {
|
|
478
485
|
await this.restIssueRepository.updateIssue(issue);
|
|
479
486
|
};
|
|
@@ -732,6 +739,7 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
732
739
|
url: prUrl,
|
|
733
740
|
branchName: headRefName ?? null,
|
|
734
741
|
createdAt: new Date(0),
|
|
742
|
+
isDraft: data.isDraft === true,
|
|
735
743
|
isConflicted,
|
|
736
744
|
isPassedAllCiJob,
|
|
737
745
|
isCiStateSuccess,
|
|
@@ -771,6 +779,7 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
771
779
|
number
|
|
772
780
|
state
|
|
773
781
|
createdAt
|
|
782
|
+
isDraft
|
|
774
783
|
mergeable
|
|
775
784
|
headRefName
|
|
776
785
|
baseRefName
|
|
@@ -959,6 +968,7 @@ export class ApiV3CheerioRestIssueRepository
|
|
|
959
968
|
pullRequest(number: $prNumber) {
|
|
960
969
|
url
|
|
961
970
|
state
|
|
971
|
+
isDraft
|
|
962
972
|
headRefName
|
|
963
973
|
baseRefName
|
|
964
974
|
mergeable
|
|
@@ -83,7 +83,6 @@ describe('GraphqlProjectItemRepository', () => {
|
|
|
83
83
|
const localStorageRepository = new LocalStorageRepository();
|
|
84
84
|
const repository = new GraphqlProjectItemRepository(
|
|
85
85
|
localStorageRepository,
|
|
86
|
-
'',
|
|
87
86
|
'dummy-token',
|
|
88
87
|
);
|
|
89
88
|
|
|
@@ -109,7 +108,6 @@ describe('GraphqlProjectItemRepository', () => {
|
|
|
109
108
|
const localStorageRepository = new LocalStorageRepository();
|
|
110
109
|
const repository = new GraphqlProjectItemRepository(
|
|
111
110
|
localStorageRepository,
|
|
112
|
-
'',
|
|
113
111
|
'dummy-token',
|
|
114
112
|
);
|
|
115
113
|
|
|
@@ -157,7 +155,6 @@ describe('GraphqlProjectItemRepository', () => {
|
|
|
157
155
|
const localStorageRepository = new LocalStorageRepository();
|
|
158
156
|
const repository = new GraphqlProjectItemRepository(
|
|
159
157
|
localStorageRepository,
|
|
160
|
-
'',
|
|
161
158
|
'dummy-token',
|
|
162
159
|
);
|
|
163
160
|
|
|
@@ -176,7 +173,6 @@ describe('GraphqlProjectItemRepository', () => {
|
|
|
176
173
|
const localStorageRepository = new LocalStorageRepository();
|
|
177
174
|
const repository = new GraphqlProjectItemRepository(
|
|
178
175
|
localStorageRepository,
|
|
179
|
-
'',
|
|
180
176
|
'dummy-token',
|
|
181
177
|
);
|
|
182
178
|
|
|
@@ -195,7 +191,6 @@ describe('GraphqlProjectItemRepository', () => {
|
|
|
195
191
|
const localStorageRepository = new LocalStorageRepository();
|
|
196
192
|
const repository = new GraphqlProjectItemRepository(
|
|
197
193
|
localStorageRepository,
|
|
198
|
-
'',
|
|
199
194
|
'dummy-token',
|
|
200
195
|
);
|
|
201
196
|
|
|
@@ -244,7 +239,6 @@ describe('GraphqlProjectItemRepository', () => {
|
|
|
244
239
|
const localStorageRepository = new LocalStorageRepository();
|
|
245
240
|
const repository = new GraphqlProjectItemRepository(
|
|
246
241
|
localStorageRepository,
|
|
247
|
-
'',
|
|
248
242
|
'dummy-token',
|
|
249
243
|
);
|
|
250
244
|
|
|
@@ -277,7 +271,6 @@ describe('GraphqlProjectItemRepository', () => {
|
|
|
277
271
|
const localStorageRepository = new LocalStorageRepository();
|
|
278
272
|
const repository = new GraphqlProjectItemRepository(
|
|
279
273
|
localStorageRepository,
|
|
280
|
-
'',
|
|
281
274
|
'dummy-token',
|
|
282
275
|
);
|
|
283
276
|
|
|
@@ -305,7 +298,6 @@ describe('GraphqlProjectItemRepository', () => {
|
|
|
305
298
|
const localStorageRepository = new LocalStorageRepository();
|
|
306
299
|
const repository = new GraphqlProjectItemRepository(
|
|
307
300
|
localStorageRepository,
|
|
308
|
-
'',
|
|
309
301
|
'dummy-token',
|
|
310
302
|
);
|
|
311
303
|
|
|
@@ -159,6 +159,9 @@ describe('HandleScheduledEventUseCase', () => {
|
|
|
159
159
|
['LastExecutionDateTime'],
|
|
160
160
|
['2024-01-01T00:00:00Z'],
|
|
161
161
|
]);
|
|
162
|
+
mockStartPreparationUseCase.run.mockResolvedValue({
|
|
163
|
+
rotationOrder: null,
|
|
164
|
+
});
|
|
162
165
|
});
|
|
163
166
|
|
|
164
167
|
it('should call AnalyzeProblemByIssueUseCase with correct parameters', async () => {
|
|
@@ -377,6 +380,7 @@ describe('HandleScheduledEventUseCase', () => {
|
|
|
377
380
|
});
|
|
378
381
|
mockStartPreparationUseCase.run.mockImplementation(async () => {
|
|
379
382
|
callOrder.push('startPreparation');
|
|
383
|
+
return { rotationOrder: null };
|
|
380
384
|
});
|
|
381
385
|
|
|
382
386
|
const input = {
|
|
@@ -19,7 +19,10 @@ import { SetNoStoryIssueToStoryUseCase } from './SetNoStoryIssueToStoryUseCase';
|
|
|
19
19
|
import { CreateNewStoryByLabelUseCase } from './CreateNewStoryByLabelUseCase';
|
|
20
20
|
import { AssignNoAssigneeIssueToManagerUseCase } from './AssignNoAssigneeIssueToManagerUseCase';
|
|
21
21
|
import { UpdateIssueStatusByLabelUseCase } from './UpdateIssueStatusByLabelUseCase';
|
|
22
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
RotationOrderEntry,
|
|
24
|
+
StartPreparationUseCase,
|
|
25
|
+
} from './StartPreparationUseCase';
|
|
23
26
|
import { RevertOrphanedPreparationUseCase } from './RevertOrphanedPreparationUseCase';
|
|
24
27
|
import { RevertNotReadyAwaitingQualityCheckUseCase } from './RevertNotReadyAwaitingQualityCheckUseCase';
|
|
25
28
|
import { SetupTowerDefenceProjectUseCase } from './SetupTowerDefenceProjectUseCase';
|
|
@@ -96,6 +99,7 @@ export class HandleScheduledEventUseCase {
|
|
|
96
99
|
cacheUsed: boolean;
|
|
97
100
|
targetDateTimes: Date[];
|
|
98
101
|
storyIssues: StoryObjectMap;
|
|
102
|
+
rotationOrder: RotationOrderEntry[] | null;
|
|
99
103
|
} | null> => {
|
|
100
104
|
if (input.disabled) {
|
|
101
105
|
return null;
|
|
@@ -205,8 +209,9 @@ export class HandleScheduledEventUseCase {
|
|
|
205
209
|
now,
|
|
206
210
|
);
|
|
207
211
|
|
|
212
|
+
let rotationOrder: RotationOrderEntry[] | null = null;
|
|
208
213
|
try {
|
|
209
|
-
await this.runEachUseCases(
|
|
214
|
+
const useCaseResult = await this.runEachUseCases(
|
|
210
215
|
input,
|
|
211
216
|
project,
|
|
212
217
|
issues,
|
|
@@ -215,6 +220,7 @@ export class HandleScheduledEventUseCase {
|
|
|
215
220
|
storyIssues,
|
|
216
221
|
runSlowSweep,
|
|
217
222
|
);
|
|
223
|
+
rotationOrder = useCaseResult.rotationOrder;
|
|
218
224
|
} catch (e) {
|
|
219
225
|
if (!(e instanceof Error)) {
|
|
220
226
|
throw e;
|
|
@@ -238,7 +244,14 @@ ${JSON.stringify(e)}
|
|
|
238
244
|
throw e;
|
|
239
245
|
}
|
|
240
246
|
|
|
241
|
-
return {
|
|
247
|
+
return {
|
|
248
|
+
project,
|
|
249
|
+
issues,
|
|
250
|
+
cacheUsed,
|
|
251
|
+
targetDateTimes,
|
|
252
|
+
storyIssues,
|
|
253
|
+
rotationOrder,
|
|
254
|
+
};
|
|
242
255
|
};
|
|
243
256
|
runEachUseCases = async (
|
|
244
257
|
input: Parameters<HandleScheduledEventUseCase['run']>[0],
|
|
@@ -248,7 +261,7 @@ ${JSON.stringify(e)}
|
|
|
248
261
|
targetDateTimes: Date[],
|
|
249
262
|
storyObjectMap: StoryObjectMap,
|
|
250
263
|
runSlowSweep: boolean,
|
|
251
|
-
): Promise<
|
|
264
|
+
): Promise<{ rotationOrder: RotationOrderEntry[] | null }> => {
|
|
252
265
|
if (runSlowSweep) {
|
|
253
266
|
await this.runSlowSweepUseCases(
|
|
254
267
|
input,
|
|
@@ -284,7 +297,7 @@ ${JSON.stringify(e)}
|
|
|
284
297
|
input.startPreparation.awaitingQualityCheckStatus ?? undefined,
|
|
285
298
|
});
|
|
286
299
|
}
|
|
287
|
-
await this.startPreparationUseCase.run({
|
|
300
|
+
const preparationResult = await this.startPreparationUseCase.run({
|
|
288
301
|
projectUrl: input.projectUrl,
|
|
289
302
|
defaultAgentName: input.startPreparation.defaultAgentName,
|
|
290
303
|
defaultLlmModelName: input.startPreparation.defaultLlmModelName ?? null,
|
|
@@ -298,7 +311,9 @@ ${JSON.stringify(e)}
|
|
|
298
311
|
codexHomeCandidates: input.startPreparation.codexHomeCandidates ?? null,
|
|
299
312
|
allowIssueCacheMinutes: input.allowIssueCacheMinutes,
|
|
300
313
|
});
|
|
314
|
+
return { rotationOrder: preparationResult.rotationOrder };
|
|
301
315
|
}
|
|
316
|
+
return { rotationOrder: null };
|
|
302
317
|
};
|
|
303
318
|
runSlowSweepUseCases = async (
|
|
304
319
|
input: Parameters<HandleScheduledEventUseCase['run']>[0],
|