github-issue-tower-defence-management 2.6.0 → 2.6.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 +7 -0
- package/bin/adapter/entry-points/handlers/tokenExhaustionHandover.js +2 -1
- package/bin/adapter/entry-points/handlers/tokenExhaustionHandover.js.map +1 -1
- package/bin/adapter/repositories/GitHubIssueCheckpointRepository.js +37 -0
- package/bin/adapter/repositories/GitHubIssueCheckpointRepository.js.map +1 -0
- package/bin/domain/usecases/TokenExhaustionHandoverUseCase.js +10 -2
- package/bin/domain/usecases/TokenExhaustionHandoverUseCase.js.map +1 -1
- package/bin/domain/usecases/adapter-interfaces/IssueCheckpointRepository.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/IssueCheckpointRepository.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/handlers/tokenExhaustionHandover.ts +2 -0
- package/src/adapter/repositories/GitHubIssueCheckpointRepository.test.ts +51 -0
- package/src/adapter/repositories/GitHubIssueCheckpointRepository.ts +47 -0
- package/src/domain/usecases/TokenExhaustionHandoverUseCase.test.ts +87 -3
- package/src/domain/usecases/TokenExhaustionHandoverUseCase.ts +14 -1
- package/src/domain/usecases/adapter-interfaces/IssueCheckpointRepository.ts +3 -0
- package/types/adapter/entry-points/handlers/tokenExhaustionHandover.d.ts.map +1 -1
- package/types/adapter/repositories/GitHubIssueCheckpointRepository.d.ts +8 -0
- package/types/adapter/repositories/GitHubIssueCheckpointRepository.d.ts.map +1 -0
- package/types/domain/usecases/TokenExhaustionHandoverUseCase.d.ts +3 -1
- package/types/domain/usecases/TokenExhaustionHandoverUseCase.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/IssueCheckpointRepository.d.ts +4 -0
- package/types/domain/usecases/adapter-interfaces/IssueCheckpointRepository.d.ts.map +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [2.6.1](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v2.6.0...v2.6.1) (2026-08-29)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **token-exhaustion:** post checkpoint comment on task issue before SIGTERMing impl subagent ([#1858](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1858)) ([9e6d05a](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/9e6d05abaf3e55060d6dc56a3fdd778b198c4f17)), closes [#1823](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1823)
|
|
7
|
+
|
|
1
8
|
# [2.6.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v2.5.1...v2.6.0) (2026-08-29)
|
|
2
9
|
|
|
3
10
|
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.handleTokenExhaustionHandover = void 0;
|
|
4
4
|
const TokenExhaustionHandoverUseCase_1 = require("../../../domain/usecases/TokenExhaustionHandoverUseCase");
|
|
5
|
+
const GitHubIssueCheckpointRepository_1 = require("../../repositories/GitHubIssueCheckpointRepository");
|
|
5
6
|
const NodeTmuxSessionRepository_1 = require("../../repositories/NodeTmuxSessionRepository");
|
|
6
7
|
const RateLimitSnapshotRepository_1 = require("../../repositories/RateLimitSnapshotRepository");
|
|
7
8
|
const ProcClaudeHandoverSessionRepository_1 = require("../../repositories/ProcClaudeHandoverSessionRepository");
|
|
@@ -15,7 +16,7 @@ const handleTokenExhaustionHandover = async (params) => {
|
|
|
15
16
|
}
|
|
16
17
|
const snapshotRepository = new RateLimitSnapshotRepository_1.RateLimitSnapshotRepository(tokenListJsonPath, tokenRateLimitSnapshotBaseDir ?? undefined);
|
|
17
18
|
const stateRepository = new FileHandoverStateRepository_1.FileHandoverStateRepository(stateFilePath ?? (0, FileHandoverStateRepository_1.defaultHandoverStateFilePath)());
|
|
18
|
-
const useCase = new TokenExhaustionHandoverUseCase_1.TokenExhaustionHandoverUseCase(new ProcClaudeHandoverSessionRepository_1.ProcClaudeHandoverSessionRepository(), snapshotRepository, new NodeTmuxSessionRepository_1.NodeTmuxSessionRepository(localCommandRunner), new NodeProcessSignalRepository_1.NodeProcessSignalRepository());
|
|
19
|
+
const useCase = new TokenExhaustionHandoverUseCase_1.TokenExhaustionHandoverUseCase(new ProcClaudeHandoverSessionRepository_1.ProcClaudeHandoverSessionRepository(), snapshotRepository, new NodeTmuxSessionRepository_1.NodeTmuxSessionRepository(localCommandRunner), new NodeProcessSignalRepository_1.NodeProcessSignalRepository(), new GitHubIssueCheckpointRepository_1.GitHubIssueCheckpointRepository(process.env.GH_TOKEN ?? ''));
|
|
19
20
|
const result = await useCase.run({
|
|
20
21
|
enabled,
|
|
21
22
|
issueUrlLeaderMessage: handoverMessage ?? TokenExhaustionHandoverUseCase_1.DEFAULT_TOKEN_EXHAUSTION_HANDOVER_MESSAGE,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenExhaustionHandover.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/tokenExhaustionHandover.ts"],"names":[],"mappings":";;;AACA,4GAKiE;AACjE,4FAAyF;AACzF,gGAA6F;AAC7F,gHAA6G;AAC7G,gGAA6F;AAC7F,gGAGwD;AAcjD,MAAM,6BAA6B,GAAG,KAAK,EAChD,MAAqC,EACtB,EAAE;IACjB,MAAM,EACJ,OAAO,EACP,iBAAiB,EACjB,eAAe,EACf,6BAA6B,EAC7B,6BAA6B,EAC7B,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,GAAG,GACJ,GAAG,MAAM,CAAC;IAEX,IAAI,iBAAiB,KAAK,IAAI,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,CACT,sFAAsF,CACvF,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,kBAAkB,GAAG,IAAI,yDAA2B,CACxD,iBAAiB,EACjB,6BAA6B,IAAI,SAAS,CAC3C,CAAC;IACF,MAAM,eAAe,GAAG,IAAI,yDAA2B,CACrD,aAAa,IAAI,IAAA,0DAA4B,GAAE,CAChD,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,+DAA8B,CAChD,IAAI,yEAAmC,EAAE,EACzC,kBAAkB,EAClB,IAAI,qDAAyB,CAAC,kBAAkB,CAAC,EACjD,IAAI,yDAA2B,EAAE,
|
|
1
|
+
{"version":3,"file":"tokenExhaustionHandover.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/tokenExhaustionHandover.ts"],"names":[],"mappings":";;;AACA,4GAKiE;AACjE,wGAAqG;AACrG,4FAAyF;AACzF,gGAA6F;AAC7F,gHAA6G;AAC7G,gGAA6F;AAC7F,gGAGwD;AAcjD,MAAM,6BAA6B,GAAG,KAAK,EAChD,MAAqC,EACtB,EAAE;IACjB,MAAM,EACJ,OAAO,EACP,iBAAiB,EACjB,eAAe,EACf,6BAA6B,EAC7B,6BAA6B,EAC7B,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,GAAG,GACJ,GAAG,MAAM,CAAC;IAEX,IAAI,iBAAiB,KAAK,IAAI,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,CACT,sFAAsF,CACvF,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,kBAAkB,GAAG,IAAI,yDAA2B,CACxD,iBAAiB,EACjB,6BAA6B,IAAI,SAAS,CAC3C,CAAC;IACF,MAAM,eAAe,GAAG,IAAI,yDAA2B,CACrD,aAAa,IAAI,IAAA,0DAA4B,GAAE,CAChD,CAAC;IACF,MAAM,OAAO,GAAG,IAAI,+DAA8B,CAChD,IAAI,yEAAmC,EAAE,EACzC,kBAAkB,EAClB,IAAI,qDAAyB,CAAC,kBAAkB,CAAC,EACjD,IAAI,yDAA2B,EAAE,EACjC,IAAI,iEAA+B,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,CAChE,CAAC;IAEF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC/B,OAAO;QACP,qBAAqB,EACnB,eAAe,IAAI,0EAAyC;QAC9D,qBAAqB,EACnB,6BAA6B;YAC7B,2FAA0D;QAC5D,kBAAkB,EAChB,kBAAkB,IAAI,8EAA6C;QACrE,KAAK,EAAE,eAAe,CAAC,IAAI,EAAE;QAC7B,GAAG;KACJ,CAAC,CAAC;IAEH,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACrC,CAAC,CAAC;AAnDW,QAAA,6BAA6B,iCAmDxC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GitHubIssueCheckpointRepository = void 0;
|
|
4
|
+
const CHECKPOINT_COMMENT_BODY = 'From: :robot: preparation-daemon (-)\n\n```json\n{"pullRequestRequired": false}\n```\n\nThis implementation session was interrupted by the preparation daemon due to token near exhaustion. The task will be re-dispatched.';
|
|
5
|
+
class GitHubIssueCheckpointRepository {
|
|
6
|
+
constructor(ghToken) {
|
|
7
|
+
this.ghToken = ghToken;
|
|
8
|
+
this.postCheckpoint = async (issueUrl) => {
|
|
9
|
+
const { owner, repo, issueNumber } = this.parseIssueUrl(issueUrl);
|
|
10
|
+
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}/comments`, {
|
|
11
|
+
method: 'POST',
|
|
12
|
+
headers: {
|
|
13
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
14
|
+
Accept: 'application/vnd.github+json',
|
|
15
|
+
'Content-Type': 'application/json',
|
|
16
|
+
},
|
|
17
|
+
body: JSON.stringify({ body: CHECKPOINT_COMMENT_BODY }),
|
|
18
|
+
});
|
|
19
|
+
if (!response.ok) {
|
|
20
|
+
throw new Error(`Failed to post checkpoint comment to ${issueUrl}: ${response.status} ${response.statusText}`);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
this.parseIssueUrl = (issueUrl) => {
|
|
24
|
+
const match = issueUrl.match(/github\.com\/([^/]+)\/([^/]+)\/(issues|pull)\/(\d+)/);
|
|
25
|
+
if (!match) {
|
|
26
|
+
throw new Error(`Invalid GitHub issue URL: ${issueUrl}`);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
owner: match[1],
|
|
30
|
+
repo: match[2],
|
|
31
|
+
issueNumber: parseInt(match[4], 10),
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.GitHubIssueCheckpointRepository = GitHubIssueCheckpointRepository;
|
|
37
|
+
//# sourceMappingURL=GitHubIssueCheckpointRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GitHubIssueCheckpointRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/GitHubIssueCheckpointRepository.ts"],"names":[],"mappings":";;;AAEA,MAAM,uBAAuB,GAC3B,6NAA6N,CAAC;AAEhO,MAAa,+BAA+B;IAC1C,YAA6B,OAAe;QAAf,YAAO,GAAP,OAAO,CAAQ;QAE5C,mBAAc,GAAG,KAAK,EAAE,QAAgB,EAAiB,EAAE;YACzD,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC;YAElE,MAAM,QAAQ,GAAG,MAAM,KAAK,CAC1B,gCAAgC,KAAK,IAAI,IAAI,WAAW,WAAW,WAAW,EAC9E;gBACE,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,aAAa,EAAE,UAAU,IAAI,CAAC,OAAO,EAAE;oBACvC,MAAM,EAAE,6BAA6B;oBACrC,cAAc,EAAE,kBAAkB;iBACnC;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,uBAAuB,EAAE,CAAC;aACxD,CACF,CAAC;YAEF,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CACb,wCAAwC,QAAQ,KAAK,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAC9F,CAAC;YACJ,CAAC;QACH,CAAC,CAAC;QAEM,kBAAa,GAAG,CACtB,QAAgB,EACsC,EAAE;YACxD,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAC1B,qDAAqD,CACtD,CAAC;YACF,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CAAC,6BAA6B,QAAQ,EAAE,CAAC,CAAC;YAC3D,CAAC;YACD,OAAO;gBACL,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;gBACf,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;gBACd,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;aACpC,CAAC;QACJ,CAAC,CAAC;IAvC6C,CAAC;CAwCjD;AAzCD,0EAyCC"}
|
|
@@ -11,11 +11,12 @@ exports.TOKEN_EXHAUSTION_SEVEN_DAY_FREE_THRESHOLD = 0.05;
|
|
|
11
11
|
exports.TOKEN_EXHAUSTION_FIVE_HOUR_WARNING_FREE_THRESHOLD = 0.25;
|
|
12
12
|
exports.TOKEN_EXHAUSTION_SEVEN_DAY_WARNING_FREE_THRESHOLD = 0.15;
|
|
13
13
|
class TokenExhaustionHandoverUseCase {
|
|
14
|
-
constructor(handoverSessionRepository, snapshotRepository, tmuxSessionRepository, processSignalRepository) {
|
|
14
|
+
constructor(handoverSessionRepository, snapshotRepository, tmuxSessionRepository, processSignalRepository, issueCheckpointRepository) {
|
|
15
15
|
this.handoverSessionRepository = handoverSessionRepository;
|
|
16
16
|
this.snapshotRepository = snapshotRepository;
|
|
17
17
|
this.tmuxSessionRepository = tmuxSessionRepository;
|
|
18
18
|
this.processSignalRepository = processSignalRepository;
|
|
19
|
+
this.issueCheckpointRepository = issueCheckpointRepository;
|
|
19
20
|
this.run = async (input) => {
|
|
20
21
|
const nowEpochSeconds = Math.floor(input.now.getTime() / 1000);
|
|
21
22
|
const sessions = this.handoverSessionRepository.listHandoverSessions();
|
|
@@ -130,7 +131,13 @@ class TokenExhaustionHandoverUseCase {
|
|
|
130
131
|
};
|
|
131
132
|
this.sendHandover = async (session, input) => {
|
|
132
133
|
if (session.kind === 'implSubagent') {
|
|
133
|
-
|
|
134
|
+
if (session.issueUrl !== null) {
|
|
135
|
+
await this.issueCheckpointRepository.postCheckpoint(session.issueUrl);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
console.log(`Token exhaustion handover: sending SIGTERM to impl subagent pid=${session.pid} (no issue URL, cannot post checkpoint)`);
|
|
139
|
+
this.processSignalRepository.terminateProcess(session.pid);
|
|
140
|
+
}
|
|
134
141
|
return;
|
|
135
142
|
}
|
|
136
143
|
if (session.sessionName === null) {
|
|
@@ -143,6 +150,7 @@ class TokenExhaustionHandoverUseCase {
|
|
|
143
150
|
};
|
|
144
151
|
this.forceKill = async (session, entry) => {
|
|
145
152
|
if (session.kind === 'implSubagent') {
|
|
153
|
+
this.processSignalRepository.terminateProcess(entry.pid);
|
|
146
154
|
this.processSignalRepository.killProcess(entry.pid);
|
|
147
155
|
return;
|
|
148
156
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenExhaustionHandoverUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/TokenExhaustionHandoverUseCase.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"TokenExhaustionHandoverUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/TokenExhaustionHandoverUseCase.ts"],"names":[],"mappings":";;;AAea,QAAA,yCAAyC,GACpD,obAAob,CAAC;AAE1a,QAAA,0DAA0D,GACrE,sjBAAsjB,CAAC;AAE5iB,QAAA,6CAA6C,GAAG,GAAG,CAAC;AACpD,QAAA,iDAAiD,GAAG,GAAG,CAAC;AACxD,QAAA,sDAAsD,GAAG,IAAI,CAAC;AAC9D,QAAA,yCAAyC,GAAG,GAAG,CAAC;AAChD,QAAA,yCAAyC,GAAG,IAAI,CAAC;AACjD,QAAA,iDAAiD,GAAG,IAAI,CAAC;AACzD,QAAA,iDAAiD,GAAG,IAAI,CAAC;AA+BtE,MAAa,8BAA8B;IACzC,YACmB,yBAGhB,EACgB,kBAGhB,EACgB,qBAMhB,EACgB,uBAAgD,EAChD,yBAGhB;QAnBgB,8BAAyB,GAAzB,yBAAyB,CAGzC;QACgB,uBAAkB,GAAlB,kBAAkB,CAGlC;QACgB,0BAAqB,GAArB,qBAAqB,CAMrC;QACgB,4BAAuB,GAAvB,uBAAuB,CAAyB;QAChD,8BAAyB,GAAzB,yBAAyB,CAGzC;QAGH,QAAG,GAAG,KAAK,EACT,KAAmC,EACK,EAAE;YAC1C,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;YAC/D,MAAM,QAAQ,GAAG,IAAI,CAAC,yBAAyB,CAAC,oBAAoB,EAAE,CAAC;YACvE,MAAM,SAAS,GAAG,IAAI,CAAC,kBAAkB,CAAC,aAAa,EAAE,CAAC;YAC1D,MAAM,eAAe,GAAG,IAAI,GAAG,CAC7B,SAAS,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CACxD,CAAC;YACF,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAC9B,MAAM,IAAI,CAAC,qBAAqB,CAAC,oBAAoB,EAAE,CACxD,CAAC;YAEF,MAAM,WAAW,GAAsD;gBACrE,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO;aACvB,CAAC;YACF,MAAM,6BAA6B,GAAa,EAAE,CAAC;YACnD,MAAM,kBAAkB,GAAa,EAAE,CAAC;YACxC,MAAM,cAAc,GAAa,EAAE,CAAC;YACpC,MAAM,qBAAqB,GAAa,EAAE,CAAC;YAC3C,MAAM,qBAAqB,GAAa,EAAE,CAAC;YAC3C,MAAM,uCAAuC,GAAa,EAAE,CAAC;YAE7D,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,IAAI,CAAC;oBACH,IAAI,OAAO,CAAC,mCAAmC,EAAE,CAAC;wBAChD,OAAO,CAAC,GAAG,CACT,uCAAuC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,SAAS,OAAO,CAAC,IAAI,+EAA+E,CACrK,CAAC;wBACF,uCAAuC,CAAC,IAAI,CAC1C,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAC1B,CAAC;wBACF,OAAO,WAAW,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;wBAC9C,SAAS;oBACX,CAAC;oBACD,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;oBACpD,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;wBAC3B,SAAS;oBACX,CAAC;oBACD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,KAAK,cAAc,CAAC;oBAChD,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,CACnC,QAAQ,EACR,eAAe,EACf,OAAO,CACR,CAAC;oBACF,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;oBAE3C,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;wBAClB,SAAS;oBACX,CAAC;oBACD,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;wBACvB,OAAO,WAAW,CAAC,QAAQ,CAAC,CAAC;wBAC7B,SAAS;oBACX,CAAC;oBACD,IACE,CAAC,IAAI,CAAC,uBAAuB,CAC3B,OAAO,CAAC,KAAK,EACb,SAAS,EACT,eAAe,CAChB,EACD,CAAC;wBACD,OAAO,CAAC,GAAG,CACT,sCAAsC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,oEAAoE,CACpI,CAAC;wBACF,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;wBACtD,SAAS;oBACX,CAAC;oBAED,MAAM,KAAK,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;oBACpC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;wBACxB,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;4BAClB,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;wBAC1C,CAAC;wBACD,OAAO,CAAC,GAAG,CACT,uCAAuC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,SAAS,OAAO,CAAC,IAAI,WAAW,OAAO,CAAC,MAAM,YAAY,KAAK,CAAC,OAAO,EAAE,CAC1I,CAAC;wBACF,WAAW,CAAC,QAAQ,CAAC,GAAG;4BACtB,eAAe,EAAE,eAAe;4BAChC,GAAG,EAAE,OAAO,CAAC,GAAG;yBACjB,CAAC;wBACF,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;wBAC9D,SAAS;oBACX,CAAC;oBAED,IACE,eAAe,GAAG,KAAK,CAAC,eAAe;wBACvC,KAAK,CAAC,kBAAkB,EACxB,CAAC;wBACD,OAAO,CAAC,GAAG,CACT,2DAA2D,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,qBAAqB,KAAK,CAAC,kBAAkB,GAAG,CAAC,eAAe,GAAG,KAAK,CAAC,eAAe,CAAC,EAAE,CAChL,CAAC;wBACF,SAAS;oBACX,CAAC;oBAED,MAAM,KAAK,GAAG,OAAO;wBACnB,CAAC,CAAC,OAAO,CAAC,WAAW,KAAK,IAAI;4BAC5B,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC;wBAC3C,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBAC3D,IAAI,CAAC,KAAK,EAAE,CAAC;wBACX,IAAI,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;4BACjD,MAAM,IAAI,CAAC,sBAAsB,CAAC,OAAO,EAAE,qBAAqB,CAAC,CAAC;wBACpE,CAAC;wBACD,OAAO,WAAW,CAAC,QAAQ,CAAC,CAAC;wBAC7B,SAAS;oBACX,CAAC;oBAED,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;wBACnB,OAAO,CAAC,GAAG,CACT,+CAA+C,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,SAAS,OAAO,CAAC,IAAI,2BAA2B,CACzH,CAAC;wBACF,OAAO,WAAW,CAAC,QAAQ,CAAC,CAAC;wBAC7B,SAAS;oBACX,CAAC;oBAED,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;oBACrC,IAAI,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;wBAChC,MAAM,IAAI,CAAC,sBAAsB,CAAC,OAAO,EAAE,qBAAqB,CAAC,CAAC;oBACpE,CAAC;oBACD,OAAO,CAAC,GAAG,CACT,2CAA2C,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,SAAS,OAAO,CAAC,IAAI,EAAE,CAC5F,CAAC;oBACF,IAAI,OAAO,EAAE,CAAC;wBACZ,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;oBACrD,CAAC;yBAAM,CAAC;wBACN,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBACjC,CAAC;oBACD,OAAO,WAAW,CAAC,QAAQ,CAAC,CAAC;gBAC/B,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,KAAK,CACX,+CAA+C,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,KACtE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CACvD,EAAE,CACH,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,OAAO,CAAC,GAAG,CACT,sDAAsD,QAAQ,CAAC,MAAM,YAAY,KAAK,CAAC,OAAO,aAAa,6BAA6B,CAAC,MAAM,WAAW,kBAAkB,CAAC,MAAM,mBAAmB,cAAc,CAAC,MAAM,eAAe,qBAAqB,CAAC,MAAM,cAAc,qBAAqB,CAAC,MAAM,gCAAgC,uCAAuC,CAAC,MAAM,EAAE,CACjY,CAAC;YAEF,OAAO;gBACL,6BAA6B;gBAC7B,kBAAkB;gBAClB,cAAc;gBACd,qBAAqB;gBACrB,qBAAqB;gBACrB,uCAAuC;gBACvC,KAAK,EAAE,EAAE,OAAO,EAAE,WAAW,EAAE;aAChC,CAAC;QACJ,CAAC,CAAC;QAEM,kBAAa,GAAG,CAAC,OAA8B,EAAW,EAAE,CAClE,OAAO,CAAC,IAAI,KAAK,gBAAgB,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,CAAC;QAErD,2BAAsB,GAAG,KAAK,EACpC,OAA8B,EAC9B,qBAA+B,EAChB,EAAE;YACjB,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;gBAC1B,OAAO;YACT,CAAC;YACD,MAAM,IAAI,CAAC,qBAAqB,CAAC,2BAA2B,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC3E,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC3C,CAAC,CAAC;QAEM,iBAAY,GAAG,KAAK,EAC1B,OAA8B,EAC9B,KAAmC,EACpB,EAAE;YACjB,IAAI,OAAO,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBACpC,IAAI,OAAO,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;oBAC9B,MAAM,IAAI,CAAC,yBAAyB,CAAC,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;gBACxE,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,GAAG,CACT,mEAAmE,OAAO,CAAC,GAAG,yCAAyC,CACxH,CAAC;oBACF,IAAI,CAAC,uBAAuB,CAAC,gBAAgB,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAC7D,CAAC;gBACD,OAAO;YACT,CAAC;YACD,IAAI,OAAO,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;gBACjC,OAAO;YACT,CAAC;YACD,MAAM,OAAO,GACX,OAAO,CAAC,IAAI,KAAK,gBAAgB;gBAC/B,CAAC,CAAC,KAAK,CAAC,qBAAqB;gBAC7B,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC;YAClC,MAAM,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QAC1E,CAAC,CAAC;QAEM,cAAS,GAAG,KAAK,EACvB,OAA8B,EAC9B,KAAwC,EACzB,EAAE;YACjB,IAAI,OAAO,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBACpC,IAAI,CAAC,uBAAuB,CAAC,gBAAgB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBACzD,IAAI,CAAC,uBAAuB,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBACpD,OAAO;YACT,CAAC;YACD,IAAI,OAAO,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;gBACjC,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,qBAAqB,CAAC,WAAW,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;gBACpE,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,KAAK,CACX,qDAAqD,OAAO,CAAC,WAAW,KACtE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CACvD,EAAE,CACH,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,IAAI,CAAC,uBAAuB,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACtD,CAAC,CAAC;QAEM,gBAAW,GAAG,CAAC,OAA8B,EAAU,EAAE,CAC/D,OAAO,CAAC,IAAI,KAAK,cAAc;YAC7B,CAAC,CAAC,OAAO,OAAO,CAAC,GAAG,EAAE;YACtB,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,IAAI,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAE5C,gBAAW,GAAG,CAAC,OAA8B,EAAU,EAAE,CAC/D,OAAO,CAAC,WAAW,IAAI,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC;QAEtC,4BAAuB,GAAG,CAChC,YAAoB,EACpB,SAAmC,EACnC,eAAuB,EACd,EAAE;YACX,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;gBACjC,IAAI,QAAQ,CAAC,KAAK,KAAK,YAAY,EAAE,CAAC;oBACpC,SAAS;gBACX,CAAC;gBACD,MAAM,OAAO,GAAG,IAAI,CAAC,gBAAgB,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,CAAC,CAAC;gBACvE,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;oBACzC,OAAO,IAAI,CAAC;gBACd,CAAC;YACH,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC,CAAC;QAEM,qBAAgB,GAAG,CACzB,QAAgC,EAChC,eAAuB,EACvB,OAAgB,EACC,EAAE;YACnB,MAAM,GAAG,GAAG,eAAe,GAAG,QAAQ,CAAC,gBAAgB,CAAC;YACxD,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CACjC,QAAQ,CAAC,mBAAmB,EAC5B,QAAQ,CAAC,aAAa,EACtB,eAAe,CAChB,CAAC;YACF,MAAM,YAAY,GAAG,IAAI,CAAC,SAAS,CACjC,QAAQ,CAAC,mBAAmB,EAC5B,QAAQ,CAAC,aAAa,EACtB,eAAe,CAChB,CAAC;YACF,MAAM,OAAO,GACX,QAAQ,CAAC,OAAO;gBAChB,QAAQ,CAAC,QAAQ;gBACjB,QAAQ,CAAC,iBAAiB,GAAG,eAAe,CAAC;YAC/C,MAAM,YAAY,GAAG,IAAI,CAAC,cAAc,CACtC,QAAQ,CAAC,iBAAiB,EAC1B,eAAe,CAChB,CAAC;YAEF,IAAI,GAAG,GAAG,8DAAsD,EAAE,CAAC;gBACjE,OAAO;oBACL,KAAK,EAAE,IAAI;oBACX,SAAS,EAAE,KAAK;oBAChB,MAAM,EAAE,YAAY;oBACpB,YAAY;oBACZ,YAAY;oBACZ,OAAO;oBACP,YAAY;iBACb,CAAC;YACJ,CAAC;YAED,MAAM,aAAa,GACjB,YAAY,GAAG,yDAAiD;gBAChE,YAAY,GAAG,yDAAiD;gBAChE,OAAO;gBACP,YAAY,CAAC;YACf,IACE,GAAG,GAAG,yDAAiD;gBACvD,CAAC,aAAa,EACd,CAAC;gBACD,OAAO;oBACL,KAAK,EAAE,IAAI;oBACX,SAAS,EAAE,KAAK;oBAChB,MAAM,EAAE,eAAe;oBACvB,YAAY;oBACZ,YAAY;oBACZ,OAAO;oBACP,YAAY;iBACb,CAAC;YACJ,CAAC;YAED,MAAM,SAAS,GACb,YAAY,GAAG,iDAAyC;gBACxD,CAAC,OAAO,IAAI,YAAY,GAAG,iDAAyC,CAAC;gBACrE,OAAO;gBACP,YAAY,CAAC;YACf,MAAM,MAAM,GAAG,YAAY;gBACzB,CAAC,CAAC,0BAA0B;gBAC5B,CAAC,CAAC,YAAY,GAAG,iDAAyC;oBACxD,CAAC,CAAC,gCAAgC;oBAClC,CAAC,CAAC,OAAO,IAAI,YAAY,GAAG,iDAAyC;wBACnE,CAAC,CAAC,gCAAgC;wBAClC,CAAC,CAAC,OAAO;4BACP,CAAC,CAAC,qBAAqB;4BACvB,CAAC,CAAC,SAAS,CAAC;YACpB,OAAO;gBACL,KAAK,EAAE,KAAK;gBACZ,SAAS;gBACT,MAAM;gBACN,YAAY;gBACZ,YAAY;gBACZ,OAAO;gBACP,YAAY;aACb,CAAC;QACJ,CAAC,CAAC;QAEM,cAAS,GAAG,CAClB,WAAmB,EACnB,UAAkB,EAClB,eAAuB,EACf,EAAE;YACV,IAAI,UAAU,GAAG,CAAC,IAAI,eAAe,GAAG,UAAU,EAAE,CAAC;gBACnD,OAAO,CAAC,CAAC;YACX,CAAC;YACD,OAAO,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACvC,CAAC,CAAC;QAEM,YAAO,GAAG,CAAC,KAAa,EAAU,EAAE;YAC1C,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACd,OAAO,CAAC,CAAC;YACX,CAAC;YACD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACd,OAAO,CAAC,CAAC;YACX,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC,CAAC;QAEM,mBAAc,GAAG,CACvB,iBAA0C,EAC1C,eAAuB,EACd,EAAE;YACX,KAAK,MAAM,KAAK,IAAI,iBAAiB,EAAE,CAAC;gBACtC,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;oBACpB,SAAS;gBACX,CAAC;gBACD,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,IAAI,eAAe,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC;oBAC3D,SAAS;gBACX,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC,CAAC;IArWC,CAAC;CAsWL;AA5XD,wEA4XC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IssueCheckpointRepository.js","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/IssueCheckpointRepository.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
DEFAULT_TOKEN_EXHAUSTION_HANDOVER_MESSAGE_BARE_NAME_LEADER,
|
|
6
6
|
DEFAULT_TOKEN_EXHAUSTION_GRACE_PERIOD_SECONDS,
|
|
7
7
|
} from '../../../domain/usecases/TokenExhaustionHandoverUseCase';
|
|
8
|
+
import { GitHubIssueCheckpointRepository } from '../../repositories/GitHubIssueCheckpointRepository';
|
|
8
9
|
import { NodeTmuxSessionRepository } from '../../repositories/NodeTmuxSessionRepository';
|
|
9
10
|
import { RateLimitSnapshotRepository } from '../../repositories/RateLimitSnapshotRepository';
|
|
10
11
|
import { ProcClaudeHandoverSessionRepository } from '../../repositories/ProcClaudeHandoverSessionRepository';
|
|
@@ -60,6 +61,7 @@ export const handleTokenExhaustionHandover = async (
|
|
|
60
61
|
snapshotRepository,
|
|
61
62
|
new NodeTmuxSessionRepository(localCommandRunner),
|
|
62
63
|
new NodeProcessSignalRepository(),
|
|
64
|
+
new GitHubIssueCheckpointRepository(process.env.GH_TOKEN ?? ''),
|
|
63
65
|
);
|
|
64
66
|
|
|
65
67
|
const result = await useCase.run({
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { GitHubIssueCheckpointRepository } from './GitHubIssueCheckpointRepository';
|
|
2
|
+
|
|
3
|
+
const VALID_URL = 'https://github.com/owner/repo/issues/42';
|
|
4
|
+
const EXPECTED_API_URL =
|
|
5
|
+
'https://api.github.com/repos/owner/repo/issues/42/comments';
|
|
6
|
+
const EXPECTED_BODY = JSON.stringify({
|
|
7
|
+
body: 'From: :robot: preparation-daemon (-)\n\n```json\n{"pullRequestRequired": false}\n```\n\nThis implementation session was interrupted by the preparation daemon due to token near exhaustion. The task will be re-dispatched.',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
describe('GitHubIssueCheckpointRepository', () => {
|
|
11
|
+
let repository: GitHubIssueCheckpointRepository;
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
jest.restoreAllMocks();
|
|
15
|
+
repository = new GitHubIssueCheckpointRepository('test-token');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('posts to the correct GitHub API URL with correct headers and body', async () => {
|
|
19
|
+
const fetchSpy = jest
|
|
20
|
+
.spyOn(global, 'fetch')
|
|
21
|
+
.mockResolvedValue(new Response('{}', { status: 201 }));
|
|
22
|
+
|
|
23
|
+
await repository.postCheckpoint(VALID_URL);
|
|
24
|
+
|
|
25
|
+
expect(fetchSpy).toHaveBeenCalledWith(EXPECTED_API_URL, {
|
|
26
|
+
method: 'POST',
|
|
27
|
+
headers: {
|
|
28
|
+
Authorization: 'Bearer test-token',
|
|
29
|
+
Accept: 'application/vnd.github+json',
|
|
30
|
+
'Content-Type': 'application/json',
|
|
31
|
+
},
|
|
32
|
+
body: EXPECTED_BODY,
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('throws when the response is not 2xx', async () => {
|
|
37
|
+
jest
|
|
38
|
+
.spyOn(global, 'fetch')
|
|
39
|
+
.mockResolvedValue(
|
|
40
|
+
new Response('Forbidden', { status: 403, statusText: 'Forbidden' }),
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
await expect(repository.postCheckpoint(VALID_URL)).rejects.toThrow('403');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('throws when the URL does not match the expected GitHub issue pattern', async () => {
|
|
47
|
+
await expect(
|
|
48
|
+
repository.postCheckpoint('https://example.com/not-an-issue'),
|
|
49
|
+
).rejects.toThrow('Invalid GitHub issue URL');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { IssueCheckpointRepository } from '../../domain/usecases/adapter-interfaces/IssueCheckpointRepository';
|
|
2
|
+
|
|
3
|
+
const CHECKPOINT_COMMENT_BODY =
|
|
4
|
+
'From: :robot: preparation-daemon (-)\n\n```json\n{"pullRequestRequired": false}\n```\n\nThis implementation session was interrupted by the preparation daemon due to token near exhaustion. The task will be re-dispatched.';
|
|
5
|
+
|
|
6
|
+
export class GitHubIssueCheckpointRepository implements IssueCheckpointRepository {
|
|
7
|
+
constructor(private readonly ghToken: string) {}
|
|
8
|
+
|
|
9
|
+
postCheckpoint = async (issueUrl: string): Promise<void> => {
|
|
10
|
+
const { owner, repo, issueNumber } = this.parseIssueUrl(issueUrl);
|
|
11
|
+
|
|
12
|
+
const response = await fetch(
|
|
13
|
+
`https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}/comments`,
|
|
14
|
+
{
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers: {
|
|
17
|
+
Authorization: `Bearer ${this.ghToken}`,
|
|
18
|
+
Accept: 'application/vnd.github+json',
|
|
19
|
+
'Content-Type': 'application/json',
|
|
20
|
+
},
|
|
21
|
+
body: JSON.stringify({ body: CHECKPOINT_COMMENT_BODY }),
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
`Failed to post checkpoint comment to ${issueUrl}: ${response.status} ${response.statusText}`,
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
private parseIssueUrl = (
|
|
33
|
+
issueUrl: string,
|
|
34
|
+
): { owner: string; repo: string; issueNumber: number } => {
|
|
35
|
+
const match = issueUrl.match(
|
|
36
|
+
/github\.com\/([^/]+)\/([^/]+)\/(issues|pull)\/(\d+)/,
|
|
37
|
+
);
|
|
38
|
+
if (!match) {
|
|
39
|
+
throw new Error(`Invalid GitHub issue URL: ${issueUrl}`);
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
owner: match[1],
|
|
43
|
+
repo: match[2],
|
|
44
|
+
issueNumber: parseInt(match[4], 10),
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
import { ClaudeHandoverSession } from '../entities/ClaudeHandoverSession';
|
|
10
10
|
import { TokenExhaustionHandoverState } from '../entities/TokenExhaustionHandoverState';
|
|
11
11
|
import { ClaudeHandoverSessionRepository } from './adapter-interfaces/ClaudeHandoverSessionRepository';
|
|
12
|
+
import { IssueCheckpointRepository } from './adapter-interfaces/IssueCheckpointRepository';
|
|
12
13
|
import { ProcessSignalRepository } from './adapter-interfaces/ProcessSignalRepository';
|
|
13
14
|
import { TmuxSessionRepository } from './adapter-interfaces/TmuxSessionRepository';
|
|
14
15
|
import {
|
|
@@ -121,6 +122,9 @@ describe('TokenExhaustionHandoverUseCase', () => {
|
|
|
121
122
|
>
|
|
122
123
|
>;
|
|
123
124
|
let processSignalRepository: Mocked<ProcessSignalRepository>;
|
|
125
|
+
let issueCheckpointRepository: Mocked<
|
|
126
|
+
Pick<IssueCheckpointRepository, 'postCheckpoint'>
|
|
127
|
+
>;
|
|
124
128
|
|
|
125
129
|
const exhaustedFiveHour = (): TokenModelWeeklyLimit[] => [];
|
|
126
130
|
|
|
@@ -146,12 +150,16 @@ describe('TokenExhaustionHandoverUseCase', () => {
|
|
|
146
150
|
terminateProcess: jest.fn(),
|
|
147
151
|
killProcess: jest.fn(),
|
|
148
152
|
};
|
|
153
|
+
issueCheckpointRepository = {
|
|
154
|
+
postCheckpoint: jest.fn().mockResolvedValue(undefined),
|
|
155
|
+
};
|
|
149
156
|
|
|
150
157
|
useCase = new TokenExhaustionHandoverUseCase(
|
|
151
158
|
handoverSessionRepository,
|
|
152
159
|
snapshotRepository,
|
|
153
160
|
tmuxSessionRepository,
|
|
154
161
|
processSignalRepository,
|
|
162
|
+
issueCheckpointRepository,
|
|
155
163
|
);
|
|
156
164
|
});
|
|
157
165
|
|
|
@@ -330,7 +338,7 @@ describe('TokenExhaustionHandoverUseCase', () => {
|
|
|
330
338
|
);
|
|
331
339
|
});
|
|
332
340
|
|
|
333
|
-
it('
|
|
341
|
+
it('posts a checkpoint comment to the task issue on first detection of an impl subagent with a known issue URL', async () => {
|
|
334
342
|
handoverSessionRepository.listHandoverSessions.mockReturnValue([
|
|
335
343
|
implSubagentSession(),
|
|
336
344
|
]);
|
|
@@ -341,10 +349,78 @@ describe('TokenExhaustionHandoverUseCase', () => {
|
|
|
341
349
|
|
|
342
350
|
const result = await useCase.run(defaultInput());
|
|
343
351
|
|
|
352
|
+
expect(issueCheckpointRepository.postCheckpoint).toHaveBeenCalledWith(
|
|
353
|
+
ISSUE_URL,
|
|
354
|
+
);
|
|
355
|
+
expect(processSignalRepository.terminateProcess).not.toHaveBeenCalled();
|
|
356
|
+
expect(tmuxSessionRepository.sendKeys).not.toHaveBeenCalled();
|
|
357
|
+
expect(result.state.entries[`pid:${IMPL_PID}`]).toEqual({
|
|
358
|
+
signaledAtEpoch: nowEpochSeconds,
|
|
359
|
+
pid: IMPL_PID,
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('sends SIGTERM immediately when the impl subagent has no issue URL', async () => {
|
|
364
|
+
const noUrlSession: ClaudeHandoverSession = {
|
|
365
|
+
...implSubagentSession(),
|
|
366
|
+
issueUrl: null,
|
|
367
|
+
};
|
|
368
|
+
handoverSessionRepository.listHandoverSessions.mockReturnValue([
|
|
369
|
+
noUrlSession,
|
|
370
|
+
]);
|
|
371
|
+
snapshotRepository.listSnapshots.mockReturnValue([
|
|
372
|
+
snapshot(TOKEN_EXHAUSTED, { fiveHourUtilization: 0.95 }),
|
|
373
|
+
snapshot(TOKEN_FRESH),
|
|
374
|
+
]);
|
|
375
|
+
const logSpy = jest.spyOn(console, 'log');
|
|
376
|
+
|
|
377
|
+
const result = await useCase.run(defaultInput());
|
|
378
|
+
|
|
344
379
|
expect(processSignalRepository.terminateProcess).toHaveBeenCalledWith(
|
|
345
380
|
IMPL_PID,
|
|
346
381
|
);
|
|
347
|
-
expect(
|
|
382
|
+
expect(issueCheckpointRepository.postCheckpoint).not.toHaveBeenCalled();
|
|
383
|
+
expect(logSpy).toHaveBeenCalledWith(
|
|
384
|
+
expect.stringContaining('no issue URL'),
|
|
385
|
+
);
|
|
386
|
+
expect(result.state.entries[`pid:${IMPL_PID}`]).toEqual({
|
|
387
|
+
signaledAtEpoch: nowEpochSeconds,
|
|
388
|
+
pid: IMPL_PID,
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('does not create a state entry when postCheckpoint throws, so the next cycle retries', async () => {
|
|
393
|
+
handoverSessionRepository.listHandoverSessions.mockReturnValue([
|
|
394
|
+
implSubagentSession(),
|
|
395
|
+
]);
|
|
396
|
+
snapshotRepository.listSnapshots.mockReturnValue([
|
|
397
|
+
snapshot(TOKEN_EXHAUSTED, { fiveHourUtilization: 0.95 }),
|
|
398
|
+
snapshot(TOKEN_FRESH),
|
|
399
|
+
]);
|
|
400
|
+
issueCheckpointRepository.postCheckpoint.mockRejectedValue(
|
|
401
|
+
new Error('network error'),
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
const result = await useCase.run(defaultInput());
|
|
405
|
+
|
|
406
|
+
expect(result.state.entries[`pid:${IMPL_PID}`]).toBeUndefined();
|
|
407
|
+
expect(result.newlyHandoverSentSessionNames).toEqual([]);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
it('creates a state entry after postCheckpoint succeeds', async () => {
|
|
411
|
+
handoverSessionRepository.listHandoverSessions.mockReturnValue([
|
|
412
|
+
implSubagentSession(),
|
|
413
|
+
]);
|
|
414
|
+
snapshotRepository.listSnapshots.mockReturnValue([
|
|
415
|
+
snapshot(TOKEN_EXHAUSTED, { fiveHourUtilization: 0.95 }),
|
|
416
|
+
snapshot(TOKEN_FRESH),
|
|
417
|
+
]);
|
|
418
|
+
|
|
419
|
+
const result = await useCase.run(defaultInput());
|
|
420
|
+
|
|
421
|
+
expect(issueCheckpointRepository.postCheckpoint).toHaveBeenCalledWith(
|
|
422
|
+
ISSUE_URL,
|
|
423
|
+
);
|
|
348
424
|
expect(result.state.entries[`pid:${IMPL_PID}`]).toEqual({
|
|
349
425
|
signaledAtEpoch: nowEpochSeconds,
|
|
350
426
|
pid: IMPL_PID,
|
|
@@ -521,7 +597,7 @@ describe('TokenExhaustionHandoverUseCase', () => {
|
|
|
521
597
|
expect(result.killedSessionNames).toEqual([BARE_NAME]);
|
|
522
598
|
});
|
|
523
599
|
|
|
524
|
-
it('SIGKILLs an impl subagent that is still alive after the grace period', async () => {
|
|
600
|
+
it('SIGTERMs then SIGKILLs an impl subagent that is still alive after the grace period', async () => {
|
|
525
601
|
handoverSessionRepository.listHandoverSessions.mockReturnValue([
|
|
526
602
|
implSubagentSession(),
|
|
527
603
|
]);
|
|
@@ -547,6 +623,14 @@ describe('TokenExhaustionHandoverUseCase', () => {
|
|
|
547
623
|
}),
|
|
548
624
|
);
|
|
549
625
|
|
|
626
|
+
const terminateOrder =
|
|
627
|
+
processSignalRepository.terminateProcess.mock.invocationCallOrder[0];
|
|
628
|
+
const killOrder =
|
|
629
|
+
processSignalRepository.killProcess.mock.invocationCallOrder[0];
|
|
630
|
+
expect(terminateOrder).toBeLessThan(killOrder);
|
|
631
|
+
expect(processSignalRepository.terminateProcess).toHaveBeenCalledWith(
|
|
632
|
+
IMPL_PID,
|
|
633
|
+
);
|
|
550
634
|
expect(processSignalRepository.killProcess).toHaveBeenCalledWith(IMPL_PID);
|
|
551
635
|
expect(result.terminatedPids).toEqual([IMPL_PID]);
|
|
552
636
|
});
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
TokenExhaustionHandoverStateEntry,
|
|
5
5
|
} from '../entities/TokenExhaustionHandoverState';
|
|
6
6
|
import { ClaudeHandoverSessionRepository } from './adapter-interfaces/ClaudeHandoverSessionRepository';
|
|
7
|
+
import { IssueCheckpointRepository } from './adapter-interfaces/IssueCheckpointRepository';
|
|
7
8
|
import { ProcessSignalRepository } from './adapter-interfaces/ProcessSignalRepository';
|
|
8
9
|
import { TmuxSessionRepository } from './adapter-interfaces/TmuxSessionRepository';
|
|
9
10
|
import {
|
|
@@ -73,6 +74,10 @@ export class TokenExhaustionHandoverUseCase {
|
|
|
73
74
|
| 'launchBareNameLeaderSession'
|
|
74
75
|
>,
|
|
75
76
|
private readonly processSignalRepository: ProcessSignalRepository,
|
|
77
|
+
private readonly issueCheckpointRepository: Pick<
|
|
78
|
+
IssueCheckpointRepository,
|
|
79
|
+
'postCheckpoint'
|
|
80
|
+
>,
|
|
76
81
|
) {}
|
|
77
82
|
|
|
78
83
|
run = async (
|
|
@@ -245,7 +250,14 @@ export class TokenExhaustionHandoverUseCase {
|
|
|
245
250
|
input: TokenExhaustionHandoverInput,
|
|
246
251
|
): Promise<void> => {
|
|
247
252
|
if (session.kind === 'implSubagent') {
|
|
248
|
-
|
|
253
|
+
if (session.issueUrl !== null) {
|
|
254
|
+
await this.issueCheckpointRepository.postCheckpoint(session.issueUrl);
|
|
255
|
+
} else {
|
|
256
|
+
console.log(
|
|
257
|
+
`Token exhaustion handover: sending SIGTERM to impl subagent pid=${session.pid} (no issue URL, cannot post checkpoint)`,
|
|
258
|
+
);
|
|
259
|
+
this.processSignalRepository.terminateProcess(session.pid);
|
|
260
|
+
}
|
|
249
261
|
return;
|
|
250
262
|
}
|
|
251
263
|
if (session.sessionName === null) {
|
|
@@ -263,6 +275,7 @@ export class TokenExhaustionHandoverUseCase {
|
|
|
263
275
|
entry: TokenExhaustionHandoverStateEntry,
|
|
264
276
|
): Promise<void> => {
|
|
265
277
|
if (session.kind === 'implSubagent') {
|
|
278
|
+
this.processSignalRepository.terminateProcess(entry.pid);
|
|
266
279
|
this.processSignalRepository.killProcess(entry.pid);
|
|
267
280
|
return;
|
|
268
281
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenExhaustionHandover.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/tokenExhaustionHandover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAC;
|
|
1
|
+
{"version":3,"file":"tokenExhaustionHandover.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/tokenExhaustionHandover.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAC;AAiBpG,MAAM,MAAM,6BAA6B,GAAG;IAC1C,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,6BAA6B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,6BAA6B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAEF,eAAO,MAAM,6BAA6B,GACxC,QAAQ,6BAA6B,KACpC,OAAO,CAAC,IAAI,CAiDd,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { IssueCheckpointRepository } from '../../domain/usecases/adapter-interfaces/IssueCheckpointRepository';
|
|
2
|
+
export declare class GitHubIssueCheckpointRepository implements IssueCheckpointRepository {
|
|
3
|
+
private readonly ghToken;
|
|
4
|
+
constructor(ghToken: string);
|
|
5
|
+
postCheckpoint: (issueUrl: string) => Promise<void>;
|
|
6
|
+
private parseIssueUrl;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=GitHubIssueCheckpointRepository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GitHubIssueCheckpointRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/GitHubIssueCheckpointRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,oEAAoE,CAAC;AAK/G,qBAAa,+BAAgC,YAAW,yBAAyB;IACnE,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,MAAM;IAE5C,cAAc,GAAU,UAAU,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CAqBtD;IAEF,OAAO,CAAC,aAAa,CAcnB;CACH"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { TokenExhaustionHandoverState } from '../entities/TokenExhaustionHandoverState';
|
|
2
2
|
import { ClaudeHandoverSessionRepository } from './adapter-interfaces/ClaudeHandoverSessionRepository';
|
|
3
|
+
import { IssueCheckpointRepository } from './adapter-interfaces/IssueCheckpointRepository';
|
|
3
4
|
import { ProcessSignalRepository } from './adapter-interfaces/ProcessSignalRepository';
|
|
4
5
|
import { TmuxSessionRepository } from './adapter-interfaces/TmuxSessionRepository';
|
|
5
6
|
import { TokenRateLimitSnapshotRepository } from './adapter-interfaces/TokenRateLimitSnapshotRepository';
|
|
@@ -34,7 +35,8 @@ export declare class TokenExhaustionHandoverUseCase {
|
|
|
34
35
|
private readonly snapshotRepository;
|
|
35
36
|
private readonly tmuxSessionRepository;
|
|
36
37
|
private readonly processSignalRepository;
|
|
37
|
-
|
|
38
|
+
private readonly issueCheckpointRepository;
|
|
39
|
+
constructor(handoverSessionRepository: Pick<ClaudeHandoverSessionRepository, 'listHandoverSessions'>, snapshotRepository: Pick<TokenRateLimitSnapshotRepository, 'listSnapshots'>, tmuxSessionRepository: Pick<TmuxSessionRepository, 'sendKeys' | 'killSession' | 'listLiveSessionNames' | 'launchBareNameLeaderSession'>, processSignalRepository: ProcessSignalRepository, issueCheckpointRepository: Pick<IssueCheckpointRepository, 'postCheckpoint'>);
|
|
38
40
|
run: (input: TokenExhaustionHandoverInput) => Promise<TokenExhaustionHandoverResult>;
|
|
39
41
|
private needsRelaunch;
|
|
40
42
|
private relaunchBareNameLeader;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TokenExhaustionHandoverUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/TokenExhaustionHandoverUseCase.ts"],"names":[],"mappings":"AACA,OAAO,EACL,4BAA4B,EAE7B,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,+BAA+B,EAAE,MAAM,sDAAsD,CAAC;AACvG,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AACvF,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACnF,OAAO,EAGL,gCAAgC,EACjC,MAAM,uDAAuD,CAAC;AAE/D,eAAO,MAAM,yCAAyC,ubACgY,CAAC;AAEvb,eAAO,MAAM,0DAA0D,yjBACif,CAAC;AAEzjB,eAAO,MAAM,6CAA6C,MAAM,CAAC;AACjE,eAAO,MAAM,iDAAiD,MAAM,CAAC;AACrE,eAAO,MAAM,sDAAsD,OAAO,CAAC;AAC3E,eAAO,MAAM,yCAAyC,MAAM,CAAC;AAC7D,eAAO,MAAM,yCAAyC,OAAO,CAAC;AAC9D,eAAO,MAAM,iDAAiD,OAAO,CAAC;AACtE,eAAO,MAAM,iDAAiD,OAAO,CAAC;AAYtE,MAAM,MAAM,4BAA4B,GAAG;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,4BAA4B,CAAC;IACpC,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,6BAA6B,EAAE,MAAM,EAAE,CAAC;IACxC,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,uCAAuC,EAAE,MAAM,EAAE,CAAC;IAClD,KAAK,EAAE,4BAA4B,CAAC;CACrC,CAAC;AAEF,qBAAa,8BAA8B;IAEvC,OAAO,CAAC,QAAQ,CAAC,yBAAyB;IAI1C,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IAInC,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IAOtC,OAAO,CAAC,QAAQ,CAAC,uBAAuB;
|
|
1
|
+
{"version":3,"file":"TokenExhaustionHandoverUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/TokenExhaustionHandoverUseCase.ts"],"names":[],"mappings":"AACA,OAAO,EACL,4BAA4B,EAE7B,MAAM,0CAA0C,CAAC;AAClD,OAAO,EAAE,+BAA+B,EAAE,MAAM,sDAAsD,CAAC;AACvG,OAAO,EAAE,yBAAyB,EAAE,MAAM,gDAAgD,CAAC;AAC3F,OAAO,EAAE,uBAAuB,EAAE,MAAM,8CAA8C,CAAC;AACvF,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AACnF,OAAO,EAGL,gCAAgC,EACjC,MAAM,uDAAuD,CAAC;AAE/D,eAAO,MAAM,yCAAyC,ubACgY,CAAC;AAEvb,eAAO,MAAM,0DAA0D,yjBACif,CAAC;AAEzjB,eAAO,MAAM,6CAA6C,MAAM,CAAC;AACjE,eAAO,MAAM,iDAAiD,MAAM,CAAC;AACrE,eAAO,MAAM,sDAAsD,OAAO,CAAC;AAC3E,eAAO,MAAM,yCAAyC,MAAM,CAAC;AAC7D,eAAO,MAAM,yCAAyC,OAAO,CAAC;AAC9D,eAAO,MAAM,iDAAiD,OAAO,CAAC;AACtE,eAAO,MAAM,iDAAiD,OAAO,CAAC;AAYtE,MAAM,MAAM,4BAA4B,GAAG;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,KAAK,EAAE,4BAA4B,CAAC;IACpC,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,6BAA6B,EAAE,MAAM,EAAE,CAAC;IACxC,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,uCAAuC,EAAE,MAAM,EAAE,CAAC;IAClD,KAAK,EAAE,4BAA4B,CAAC;CACrC,CAAC;AAEF,qBAAa,8BAA8B;IAEvC,OAAO,CAAC,QAAQ,CAAC,yBAAyB;IAI1C,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IAInC,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IAOtC,OAAO,CAAC,QAAQ,CAAC,uBAAuB;IACxC,OAAO,CAAC,QAAQ,CAAC,yBAAyB;gBAhBzB,yBAAyB,EAAE,IAAI,CAC9C,+BAA+B,EAC/B,sBAAsB,CACvB,EACgB,kBAAkB,EAAE,IAAI,CACvC,gCAAgC,EAChC,eAAe,CAChB,EACgB,qBAAqB,EAAE,IAAI,CAC1C,qBAAqB,EACnB,UAAU,GACV,aAAa,GACb,sBAAsB,GACtB,6BAA6B,CAChC,EACgB,uBAAuB,EAAE,uBAAuB,EAChD,yBAAyB,EAAE,IAAI,CAC9C,yBAAyB,EACzB,gBAAgB,CACjB;IAGH,GAAG,GACD,OAAO,4BAA4B,KAClC,OAAO,CAAC,6BAA6B,CAAC,CAmJvC;IAEF,OAAO,CAAC,aAAa,CACwC;IAE7D,OAAO,CAAC,sBAAsB,CAS5B;IAEF,OAAO,CAAC,YAAY,CAuBlB;IAEF,OAAO,CAAC,SAAS,CAqBf;IAEF,OAAO,CAAC,WAAW,CAGiC;IAEpD,OAAO,CAAC,WAAW,CAC2B;IAE9C,OAAO,CAAC,uBAAuB,CAe7B;IAEF,OAAO,CAAC,gBAAgB,CAgFtB;IAEF,OAAO,CAAC,SAAS,CASf;IAEF,OAAO,CAAC,OAAO,CAQb;IAEF,OAAO,CAAC,cAAc,CAcpB;CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IssueCheckpointRepository.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/IssueCheckpointRepository.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,yBAAyB;IACxC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjD"}
|