github-issue-tower-defence-management 1.105.0 → 1.106.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +13 -0
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/staleTmuxSessionCleaner.js +18 -0
- package/bin/adapter/entry-points/handlers/staleTmuxSessionCleaner.js.map +1 -0
- package/bin/adapter/repositories/NodeTmuxSessionRepository.js +22 -0
- package/bin/adapter/repositories/NodeTmuxSessionRepository.js.map +1 -1
- package/bin/domain/entities/LiveTmuxSession.js +3 -0
- package/bin/domain/entities/LiveTmuxSession.js.map +1 -0
- package/bin/domain/usecases/StaleTmuxSessionKillUseCase.js +58 -0
- package/bin/domain/usecases/StaleTmuxSessionKillUseCase.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +17 -0
- package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.test.ts +170 -0
- package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.ts +40 -0
- package/src/adapter/repositories/NodeTmuxSessionRepository.test.ts +81 -0
- package/src/adapter/repositories/NodeTmuxSessionRepository.ts +35 -0
- package/src/domain/entities/LiveTmuxSession.ts +4 -0
- package/src/domain/usecases/StaleTmuxSessionKillUseCase.test.ts +286 -0
- package/src/domain/usecases/StaleTmuxSessionKillUseCase.ts +102 -0
- package/src/domain/usecases/adapter-interfaces/TmuxSessionRepository.ts +4 -0
- package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.test.ts +6 -0
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/staleTmuxSessionCleaner.d.ts +12 -0
- package/types/adapter/entry-points/handlers/staleTmuxSessionCleaner.d.ts.map +1 -0
- package/types/adapter/repositories/NodeTmuxSessionRepository.d.ts +3 -0
- package/types/adapter/repositories/NodeTmuxSessionRepository.d.ts.map +1 -1
- package/types/domain/entities/LiveTmuxSession.d.ts +5 -0
- package/types/domain/entities/LiveTmuxSession.d.ts.map +1 -0
- package/types/domain/usecases/StaleTmuxSessionKillUseCase.d.ts +19 -0
- package/types/domain/usecases/StaleTmuxSessionKillUseCase.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/TmuxSessionRepository.d.ts +3 -0
- package/types/domain/usecases/adapter-interfaces/TmuxSessionRepository.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [1.106.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.105.0...v1.106.0) (2026-06-26)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **schedule:** run stale tmux session cleanup in the scheduled cycle ([#1001](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1001)) ([d6c9759](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/d6c975999c9ccee48fc743149e440d4c2e80e7b3))
|
|
7
|
+
|
|
1
8
|
# [1.105.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.104.4...v1.105.0) (2026-06-26)
|
|
2
9
|
|
|
3
10
|
|
|
@@ -49,6 +49,7 @@ const machineStatusWriter_1 = require("./machineStatusWriter");
|
|
|
49
49
|
const tokenStatusWriter_1 = require("./tokenStatusWriter");
|
|
50
50
|
const inTmuxByHumanDataWriter_1 = require("./inTmuxByHumanDataWriter");
|
|
51
51
|
const inTmuxByHumanSessionReconciler_1 = require("./inTmuxByHumanSessionReconciler");
|
|
52
|
+
const staleTmuxSessionCleaner_1 = require("./staleTmuxSessionCleaner");
|
|
52
53
|
const rotationOrderFileWriter_1 = require("./rotationOrderFileWriter");
|
|
53
54
|
const projectConfig_1 = require("../cli/projectConfig");
|
|
54
55
|
const SystemDateRepository_1 = require("../../repositories/SystemDateRepository");
|
|
@@ -669,6 +670,18 @@ class HandleScheduledEventUseCaseHandler {
|
|
|
669
670
|
catch (error) {
|
|
670
671
|
console.error(`Failed to reconcile in-tmux-by-human sessions: ${error instanceof Error ? error.message : String(error)}`);
|
|
671
672
|
}
|
|
673
|
+
try {
|
|
674
|
+
await (0, staleTmuxSessionCleaner_1.cleanStaleTmuxSessions)({
|
|
675
|
+
project: result.project,
|
|
676
|
+
allowCacheMinutes: mergedInput.allowIssueCacheMinutes,
|
|
677
|
+
issueRepository,
|
|
678
|
+
localCommandRunner: nodeLocalCommandRunner,
|
|
679
|
+
now: inTmuxNow,
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
catch (error) {
|
|
683
|
+
console.error(`Failed to clean stale tmux sessions: ${error instanceof Error ? error.message : String(error)}`);
|
|
684
|
+
}
|
|
672
685
|
}
|
|
673
686
|
return result;
|
|
674
687
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HandleScheduledEventUseCaseHandler.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"HandleScheduledEventUseCaseHandler.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.cleanStaleTmuxSessions = void 0;
|
|
4
|
+
const StaleTmuxSessionKillUseCase_1 = require("../../../domain/usecases/StaleTmuxSessionKillUseCase");
|
|
5
|
+
const NodeTmuxSessionRepository_1 = require("../../repositories/NodeTmuxSessionRepository");
|
|
6
|
+
const cleanStaleTmuxSessions = async (params) => {
|
|
7
|
+
const { project, allowCacheMinutes, issueRepository, localCommandRunner, now, } = params;
|
|
8
|
+
const useCase = new StaleTmuxSessionKillUseCase_1.StaleTmuxSessionKillUseCase(issueRepository, new NodeTmuxSessionRepository_1.NodeTmuxSessionRepository(localCommandRunner));
|
|
9
|
+
await useCase.run({
|
|
10
|
+
project,
|
|
11
|
+
allowCacheMinutes,
|
|
12
|
+
excludedStatus: StaleTmuxSessionKillUseCase_1.DEFAULT_EXCLUDED_STATUS,
|
|
13
|
+
idleThresholdSeconds: StaleTmuxSessionKillUseCase_1.DEFAULT_IDLE_THRESHOLD_SECONDS,
|
|
14
|
+
now,
|
|
15
|
+
});
|
|
16
|
+
};
|
|
17
|
+
exports.cleanStaleTmuxSessions = cleanStaleTmuxSessions;
|
|
18
|
+
//# sourceMappingURL=staleTmuxSessionCleaner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staleTmuxSessionCleaner.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/staleTmuxSessionCleaner.ts"],"names":[],"mappings":";;;AAGA,sGAI8D;AAC9D,4FAAyF;AAUlF,MAAM,sBAAsB,GAAG,KAAK,EACzC,MAAoC,EACrB,EAAE;IACjB,MAAM,EACJ,OAAO,EACP,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,GAAG,GACJ,GAAG,MAAM,CAAC;IACX,MAAM,OAAO,GAAG,IAAI,yDAA2B,CAC7C,eAAe,EACf,IAAI,qDAAyB,CAAC,kBAAkB,CAAC,CAClD,CAAC;IACF,MAAM,OAAO,CAAC,GAAG,CAAC;QAChB,OAAO;QACP,iBAAiB;QACjB,cAAc,EAAE,qDAAuB;QACvC,oBAAoB,EAAE,4DAA8B;QACpD,GAAG;KACJ,CAAC,CAAC;AACL,CAAC,CAAC;AArBW,QAAA,sBAAsB,0BAqBjC"}
|
|
@@ -14,6 +14,22 @@ class NodeTmuxSessionRepository {
|
|
|
14
14
|
.map((line) => line.trim())
|
|
15
15
|
.filter((line) => line.length > 0);
|
|
16
16
|
};
|
|
17
|
+
this.listLiveSessionsWithActivity = async () => {
|
|
18
|
+
const { stdout, exitCode } = await this.localCommandRunner.runCommand('tmux', ['list-sessions', '-F', '#{session_name} #{session_activity}']);
|
|
19
|
+
if (exitCode !== 0) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
return stdout
|
|
23
|
+
.split('\n')
|
|
24
|
+
.map((line) => line.trim())
|
|
25
|
+
.filter((line) => line.length > 0)
|
|
26
|
+
.map((line) => {
|
|
27
|
+
const separatorIndex = line.lastIndexOf(' ');
|
|
28
|
+
const sessionName = line.slice(0, separatorIndex);
|
|
29
|
+
const activityEpochSeconds = Number(line.slice(separatorIndex + 1));
|
|
30
|
+
return { sessionName, activityEpochSeconds };
|
|
31
|
+
});
|
|
32
|
+
};
|
|
17
33
|
this.listInteractiveProcessCommandLines = async () => {
|
|
18
34
|
const { stdout, exitCode } = await this.localCommandRunner.runCommand('ps', ['-eo', 'args=']);
|
|
19
35
|
if (exitCode !== 0) {
|
|
@@ -39,6 +55,12 @@ class NodeTmuxSessionRepository {
|
|
|
39
55
|
issueUrl,
|
|
40
56
|
]);
|
|
41
57
|
};
|
|
58
|
+
this.killSession = async (sessionName) => {
|
|
59
|
+
const { stderr, exitCode } = await this.localCommandRunner.runCommand('tmux', ['kill-session', '-t', sessionName]);
|
|
60
|
+
if (exitCode !== 0) {
|
|
61
|
+
throw new Error(`Failed to kill tmux session "${sessionName}": exit code ${exitCode}${stderr ? `: ${stderr}` : ''}`);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
42
64
|
}
|
|
43
65
|
}
|
|
44
66
|
exports.NodeTmuxSessionRepository = NodeTmuxSessionRepository;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeTmuxSessionRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/NodeTmuxSessionRepository.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"NodeTmuxSessionRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/NodeTmuxSessionRepository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LiveTmuxSession.js","sourceRoot":"","sources":["../../../src/domain/entities/LiveTmuxSession.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StaleTmuxSessionKillUseCase = exports.DEFAULT_IDLE_THRESHOLD_SECONDS = exports.DEFAULT_EXCLUDED_STATUS = void 0;
|
|
4
|
+
const WorkflowStatus_1 = require("../entities/WorkflowStatus");
|
|
5
|
+
const InTmuxByHumanSessionReconcileUseCase_1 = require("./intmux/InTmuxByHumanSessionReconcileUseCase");
|
|
6
|
+
exports.DEFAULT_EXCLUDED_STATUS = WorkflowStatus_1.IN_TMUX_STATUS_NAME;
|
|
7
|
+
exports.DEFAULT_IDLE_THRESHOLD_SECONDS = 24 * 60 * 60;
|
|
8
|
+
class StaleTmuxSessionKillUseCase {
|
|
9
|
+
constructor(issueRepository, tmuxSessionRepository) {
|
|
10
|
+
this.issueRepository = issueRepository;
|
|
11
|
+
this.tmuxSessionRepository = tmuxSessionRepository;
|
|
12
|
+
this.run = async (params) => {
|
|
13
|
+
const liveSessions = await this.tmuxSessionRepository.listLiveSessionsWithActivity();
|
|
14
|
+
const openIssues = await this.issueRepository.getAllOpened(params.project, params.allowCacheMinutes);
|
|
15
|
+
const issueBySessionName = new Map();
|
|
16
|
+
for (const issue of openIssues) {
|
|
17
|
+
issueBySessionName.set((0, InTmuxByHumanSessionReconcileUseCase_1.toTmuxSessionName)(issue.url), issue);
|
|
18
|
+
}
|
|
19
|
+
const nowEpochSeconds = Math.floor(params.now.getTime() / 1000);
|
|
20
|
+
const killCandidates = [];
|
|
21
|
+
for (const session of liveSessions) {
|
|
22
|
+
const reason = this.evaluateKillReason(session, issueBySessionName.get(session.sessionName) ?? null, nowEpochSeconds, params.excludedStatus, params.idleThresholdSeconds);
|
|
23
|
+
if (reason !== null) {
|
|
24
|
+
killCandidates.push({ sessionName: session.sessionName, reason });
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
console.log(`Stale tmux session cleanup: ${killCandidates.length} kill candidate(s) of ${liveSessions.length} live session(s).`);
|
|
28
|
+
for (const candidate of killCandidates) {
|
|
29
|
+
console.log(`Kill candidate: ${candidate.sessionName} (${candidate.reason})`);
|
|
30
|
+
}
|
|
31
|
+
for (const candidate of killCandidates) {
|
|
32
|
+
await this.tmuxSessionRepository.killSession(candidate.sessionName);
|
|
33
|
+
console.log(`Killed tmux session: ${candidate.sessionName} (${candidate.reason})`);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
this.evaluateKillReason = (session, issue, nowEpochSeconds, excludedStatus, idleThresholdSeconds) => {
|
|
37
|
+
if (issue !== null) {
|
|
38
|
+
if (issue.status !== excludedStatus) {
|
|
39
|
+
return `mapped to open issue ${issue.url} with status "${issue.status ?? 'null'}" which is not the excluded status "${excludedStatus}"`;
|
|
40
|
+
}
|
|
41
|
+
if (issue.nextActionDate !== null) {
|
|
42
|
+
return `mapped to open issue ${issue.url} which has a next action date set`;
|
|
43
|
+
}
|
|
44
|
+
if (issue.nextActionHour !== null) {
|
|
45
|
+
return `mapped to open issue ${issue.url} which has a next action hour set`;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const idleSeconds = nowEpochSeconds - session.activityEpochSeconds;
|
|
50
|
+
if (idleSeconds >= idleThresholdSeconds) {
|
|
51
|
+
return `maps to no open issue and has been idle for ${idleSeconds} seconds (threshold ${idleThresholdSeconds} seconds)`;
|
|
52
|
+
}
|
|
53
|
+
return null;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.StaleTmuxSessionKillUseCase = StaleTmuxSessionKillUseCase;
|
|
58
|
+
//# sourceMappingURL=StaleTmuxSessionKillUseCase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StaleTmuxSessionKillUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/StaleTmuxSessionKillUseCase.ts"],"names":[],"mappings":";;;AAGA,+DAAiE;AAGjE,wGAAkF;AAErE,QAAA,uBAAuB,GAAG,oCAAmB,CAAC;AAC9C,QAAA,8BAA8B,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC;AAO3D,MAAa,2BAA2B;IACtC,YACmB,eAAsD,EACtD,qBAGhB;QAJgB,oBAAe,GAAf,eAAe,CAAuC;QACtD,0BAAqB,GAArB,qBAAqB,CAGrC;QAGH,QAAG,GAAG,KAAK,EAAE,MAMZ,EAAiB,EAAE;YAClB,MAAM,YAAY,GAChB,MAAM,IAAI,CAAC,qBAAqB,CAAC,4BAA4B,EAAE,CAAC;YAClE,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,YAAY,CACxD,MAAM,CAAC,OAAO,EACd,MAAM,CAAC,iBAAiB,CACzB,CAAC;YACF,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAiB,CAAC;YACpD,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;gBAC/B,kBAAkB,CAAC,GAAG,CAAC,IAAA,wDAAiB,EAAC,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;YAC9D,CAAC;YAED,MAAM,eAAe,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;YAChE,MAAM,cAAc,GAAoB,EAAE,CAAC;YAC3C,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;gBACnC,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,CACpC,OAAO,EACP,kBAAkB,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,IAAI,EACnD,eAAe,EACf,MAAM,CAAC,cAAc,EACrB,MAAM,CAAC,oBAAoB,CAC5B,CAAC;gBACF,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;oBACpB,cAAc,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC,CAAC;gBACpE,CAAC;YACH,CAAC;YAED,OAAO,CAAC,GAAG,CACT,+BAA+B,cAAc,CAAC,MAAM,yBAAyB,YAAY,CAAC,MAAM,mBAAmB,CACpH,CAAC;YACF,KAAK,MAAM,SAAS,IAAI,cAAc,EAAE,CAAC;gBACvC,OAAO,CAAC,GAAG,CACT,mBAAmB,SAAS,CAAC,WAAW,KAAK,SAAS,CAAC,MAAM,GAAG,CACjE,CAAC;YACJ,CAAC;YAED,KAAK,MAAM,SAAS,IAAI,cAAc,EAAE,CAAC;gBACvC,MAAM,IAAI,CAAC,qBAAqB,CAAC,WAAW,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;gBACpE,OAAO,CAAC,GAAG,CACT,wBAAwB,SAAS,CAAC,WAAW,KAAK,SAAS,CAAC,MAAM,GAAG,CACtE,CAAC;YACJ,CAAC;QACH,CAAC,CAAC;QAEM,uBAAkB,GAAG,CAC3B,OAAwB,EACxB,KAAmB,EACnB,eAAuB,EACvB,cAAsB,EACtB,oBAA4B,EACb,EAAE;YACjB,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;gBACnB,IAAI,KAAK,CAAC,MAAM,KAAK,cAAc,EAAE,CAAC;oBACpC,OAAO,wBAAwB,KAAK,CAAC,GAAG,iBAAiB,KAAK,CAAC,MAAM,IAAI,MAAM,uCAAuC,cAAc,GAAG,CAAC;gBAC1I,CAAC;gBACD,IAAI,KAAK,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;oBAClC,OAAO,wBAAwB,KAAK,CAAC,GAAG,mCAAmC,CAAC;gBAC9E,CAAC;gBACD,IAAI,KAAK,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;oBAClC,OAAO,wBAAwB,KAAK,CAAC,GAAG,mCAAmC,CAAC;gBAC9E,CAAC;gBACD,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,WAAW,GAAG,eAAe,GAAG,OAAO,CAAC,oBAAoB,CAAC;YACnE,IAAI,WAAW,IAAI,oBAAoB,EAAE,CAAC;gBACxC,OAAO,+CAA+C,WAAW,uBAAuB,oBAAoB,WAAW,CAAC;YAC1H,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC,CAAC;IA7EC,CAAC;CA8EL;AArFD,kEAqFC"}
|
package/package.json
CHANGED
|
@@ -8,6 +8,7 @@ import { writeMachineStatus } from './machineStatusWriter';
|
|
|
8
8
|
import { writeTokenStatus } from './tokenStatusWriter';
|
|
9
9
|
import { writeInTmuxByHumanData } from './inTmuxByHumanDataWriter';
|
|
10
10
|
import { reconcileInTmuxByHumanSessions } from './inTmuxByHumanSessionReconciler';
|
|
11
|
+
import { cleanStaleTmuxSessions } from './staleTmuxSessionCleaner';
|
|
11
12
|
import { writeRotationOrderFile } from './rotationOrderFileWriter';
|
|
12
13
|
import {
|
|
13
14
|
fetchProjectReadme,
|
|
@@ -507,6 +508,22 @@ export class HandleScheduledEventUseCaseHandler {
|
|
|
507
508
|
}`,
|
|
508
509
|
);
|
|
509
510
|
}
|
|
511
|
+
|
|
512
|
+
try {
|
|
513
|
+
await cleanStaleTmuxSessions({
|
|
514
|
+
project: result.project,
|
|
515
|
+
allowCacheMinutes: mergedInput.allowIssueCacheMinutes,
|
|
516
|
+
issueRepository,
|
|
517
|
+
localCommandRunner: nodeLocalCommandRunner,
|
|
518
|
+
now: inTmuxNow,
|
|
519
|
+
});
|
|
520
|
+
} catch (error) {
|
|
521
|
+
console.error(
|
|
522
|
+
`Failed to clean stale tmux sessions: ${
|
|
523
|
+
error instanceof Error ? error.message : String(error)
|
|
524
|
+
}`,
|
|
525
|
+
);
|
|
526
|
+
}
|
|
510
527
|
}
|
|
511
528
|
return result;
|
|
512
529
|
};
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { Issue } from '../../../domain/entities/Issue';
|
|
2
|
+
import { Project } from '../../../domain/entities/Project';
|
|
3
|
+
import { LocalCommandRunner } from '../../../domain/usecases/adapter-interfaces/LocalCommandRunner';
|
|
4
|
+
import { IssueRepository } from '../../../domain/usecases/adapter-interfaces/IssueRepository';
|
|
5
|
+
import { IN_TMUX_STATUS_NAME } from '../../../domain/entities/WorkflowStatus';
|
|
6
|
+
import { cleanStaleTmuxSessions } from './staleTmuxSessionCleaner';
|
|
7
|
+
|
|
8
|
+
const NOW = new Date('2026-06-26T00:00:00.000Z');
|
|
9
|
+
const NOW_EPOCH_SECONDS = Math.floor(NOW.getTime() / 1000);
|
|
10
|
+
const ALLOW_CACHE_MINUTES = 10;
|
|
11
|
+
|
|
12
|
+
const makeProject = (): Project => ({
|
|
13
|
+
id: 'project-1',
|
|
14
|
+
url: 'https://github.com/users/user/projects/1',
|
|
15
|
+
databaseId: 1,
|
|
16
|
+
name: 'Test Project',
|
|
17
|
+
status: {
|
|
18
|
+
name: 'Status',
|
|
19
|
+
fieldId: 'field-1',
|
|
20
|
+
statuses: [],
|
|
21
|
+
},
|
|
22
|
+
nextActionDate: null,
|
|
23
|
+
nextActionHour: null,
|
|
24
|
+
story: null,
|
|
25
|
+
remainingEstimationMinutes: null,
|
|
26
|
+
dependedIssueUrlSeparatedByComma: null,
|
|
27
|
+
completionDate50PercentConfidence: null,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const makeIssue = (overrides: Partial<Issue> = {}): Issue => ({
|
|
31
|
+
nameWithOwner: 'demo/repo',
|
|
32
|
+
number: 1,
|
|
33
|
+
title: 'Issue 1',
|
|
34
|
+
state: 'OPEN',
|
|
35
|
+
status: IN_TMUX_STATUS_NAME,
|
|
36
|
+
story: null,
|
|
37
|
+
nextActionDate: null,
|
|
38
|
+
nextActionHour: null,
|
|
39
|
+
estimationMinutes: null,
|
|
40
|
+
dependedIssueUrls: [],
|
|
41
|
+
completionDate50PercentConfidence: null,
|
|
42
|
+
url: 'https://github.com/demo/repo/issues/1',
|
|
43
|
+
assignees: [],
|
|
44
|
+
labels: [],
|
|
45
|
+
org: 'demo',
|
|
46
|
+
repo: 'repo',
|
|
47
|
+
body: '',
|
|
48
|
+
itemId: 'item-1',
|
|
49
|
+
isPr: false,
|
|
50
|
+
isInProgress: false,
|
|
51
|
+
isClosed: false,
|
|
52
|
+
createdAt: new Date(),
|
|
53
|
+
author: '',
|
|
54
|
+
closingIssueReferenceUrls: [],
|
|
55
|
+
...overrides,
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
type Mocked<T> = jest.Mocked<T> & jest.MockedObject<T>;
|
|
59
|
+
|
|
60
|
+
const createMockRunner = (): Mocked<LocalCommandRunner> => ({
|
|
61
|
+
runCommand: jest.fn(),
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const createMockIssueRepository = (
|
|
65
|
+
issues: Issue[],
|
|
66
|
+
): Pick<IssueRepository, 'getAllOpened'> => ({
|
|
67
|
+
getAllOpened: jest.fn().mockResolvedValue(issues),
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('cleanStaleTmuxSessions', () => {
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
jest.spyOn(console, 'log').mockImplementation(() => undefined);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
afterEach(() => {
|
|
76
|
+
jest.restoreAllMocks();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('kills a session mapping to an open issue whose status is not the excluded status', async () => {
|
|
80
|
+
const runner = createMockRunner();
|
|
81
|
+
runner.runCommand.mockImplementation(async (program, args) => {
|
|
82
|
+
if (program === 'tmux' && args[0] === 'list-sessions') {
|
|
83
|
+
return {
|
|
84
|
+
stdout: `https_//github_com/demo/repo/issues/1 ${NOW_EPOCH_SECONDS}\n`,
|
|
85
|
+
stderr: '',
|
|
86
|
+
exitCode: 0,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return { stdout: '', stderr: '', exitCode: 0 };
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
await cleanStaleTmuxSessions({
|
|
93
|
+
project: makeProject(),
|
|
94
|
+
allowCacheMinutes: ALLOW_CACHE_MINUTES,
|
|
95
|
+
issueRepository: createMockIssueRepository([
|
|
96
|
+
makeIssue({ status: 'In Progress' }),
|
|
97
|
+
]),
|
|
98
|
+
localCommandRunner: runner,
|
|
99
|
+
now: NOW,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
const killCall = runner.runCommand.mock.calls.find(
|
|
103
|
+
(call) => call[0] === 'tmux' && call[1][0] === 'kill-session',
|
|
104
|
+
);
|
|
105
|
+
expect(killCall?.[1]).toEqual([
|
|
106
|
+
'kill-session',
|
|
107
|
+
'-t',
|
|
108
|
+
'https_//github_com/demo/repo/issues/1',
|
|
109
|
+
]);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('does not kill an excluded-status session that has no reactivation trigger', async () => {
|
|
113
|
+
const runner = createMockRunner();
|
|
114
|
+
runner.runCommand.mockImplementation(async (program, args) => {
|
|
115
|
+
if (program === 'tmux' && args[0] === 'list-sessions') {
|
|
116
|
+
return {
|
|
117
|
+
stdout: `https_//github_com/demo/repo/issues/1 ${NOW_EPOCH_SECONDS}\n`,
|
|
118
|
+
stderr: '',
|
|
119
|
+
exitCode: 0,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return { stdout: '', stderr: '', exitCode: 0 };
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
await cleanStaleTmuxSessions({
|
|
126
|
+
project: makeProject(),
|
|
127
|
+
allowCacheMinutes: ALLOW_CACHE_MINUTES,
|
|
128
|
+
issueRepository: createMockIssueRepository([
|
|
129
|
+
makeIssue({ status: IN_TMUX_STATUS_NAME }),
|
|
130
|
+
]),
|
|
131
|
+
localCommandRunner: runner,
|
|
132
|
+
now: NOW,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const killCalls = runner.runCommand.mock.calls.filter(
|
|
136
|
+
(call) => call[0] === 'tmux' && call[1][0] === 'kill-session',
|
|
137
|
+
);
|
|
138
|
+
expect(killCalls).toHaveLength(0);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('kills a no-task session idle at least 24 hours and spares a recently active one', async () => {
|
|
142
|
+
const runner = createMockRunner();
|
|
143
|
+
const idleActivity = NOW_EPOCH_SECONDS - 24 * 60 * 60;
|
|
144
|
+
const recentActivity = NOW_EPOCH_SECONDS - 24 * 60 * 60 + 1;
|
|
145
|
+
runner.runCommand.mockImplementation(async (program, args) => {
|
|
146
|
+
if (program === 'tmux' && args[0] === 'list-sessions') {
|
|
147
|
+
return {
|
|
148
|
+
stdout: `idle_no_task ${idleActivity}\nrecent_no_task ${recentActivity}\n`,
|
|
149
|
+
stderr: '',
|
|
150
|
+
exitCode: 0,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return { stdout: '', stderr: '', exitCode: 0 };
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
await cleanStaleTmuxSessions({
|
|
157
|
+
project: makeProject(),
|
|
158
|
+
allowCacheMinutes: ALLOW_CACHE_MINUTES,
|
|
159
|
+
issueRepository: createMockIssueRepository([]),
|
|
160
|
+
localCommandRunner: runner,
|
|
161
|
+
now: NOW,
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
const killCalls = runner.runCommand.mock.calls.filter(
|
|
165
|
+
(call) => call[0] === 'tmux' && call[1][0] === 'kill-session',
|
|
166
|
+
);
|
|
167
|
+
expect(killCalls).toHaveLength(1);
|
|
168
|
+
expect(killCalls[0][1]).toEqual(['kill-session', '-t', 'idle_no_task']);
|
|
169
|
+
});
|
|
170
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Project } from '../../../domain/entities/Project';
|
|
2
|
+
import { LocalCommandRunner } from '../../../domain/usecases/adapter-interfaces/LocalCommandRunner';
|
|
3
|
+
import { IssueRepository } from '../../../domain/usecases/adapter-interfaces/IssueRepository';
|
|
4
|
+
import {
|
|
5
|
+
StaleTmuxSessionKillUseCase,
|
|
6
|
+
DEFAULT_EXCLUDED_STATUS,
|
|
7
|
+
DEFAULT_IDLE_THRESHOLD_SECONDS,
|
|
8
|
+
} from '../../../domain/usecases/StaleTmuxSessionKillUseCase';
|
|
9
|
+
import { NodeTmuxSessionRepository } from '../../repositories/NodeTmuxSessionRepository';
|
|
10
|
+
|
|
11
|
+
export type CleanStaleTmuxSessionsParams = {
|
|
12
|
+
project: Project;
|
|
13
|
+
allowCacheMinutes: number;
|
|
14
|
+
issueRepository: Pick<IssueRepository, 'getAllOpened'>;
|
|
15
|
+
localCommandRunner: LocalCommandRunner;
|
|
16
|
+
now: Date;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const cleanStaleTmuxSessions = async (
|
|
20
|
+
params: CleanStaleTmuxSessionsParams,
|
|
21
|
+
): Promise<void> => {
|
|
22
|
+
const {
|
|
23
|
+
project,
|
|
24
|
+
allowCacheMinutes,
|
|
25
|
+
issueRepository,
|
|
26
|
+
localCommandRunner,
|
|
27
|
+
now,
|
|
28
|
+
} = params;
|
|
29
|
+
const useCase = new StaleTmuxSessionKillUseCase(
|
|
30
|
+
issueRepository,
|
|
31
|
+
new NodeTmuxSessionRepository(localCommandRunner),
|
|
32
|
+
);
|
|
33
|
+
await useCase.run({
|
|
34
|
+
project,
|
|
35
|
+
allowCacheMinutes,
|
|
36
|
+
excludedStatus: DEFAULT_EXCLUDED_STATUS,
|
|
37
|
+
idleThresholdSeconds: DEFAULT_IDLE_THRESHOLD_SECONDS,
|
|
38
|
+
now,
|
|
39
|
+
});
|
|
40
|
+
};
|
|
@@ -44,6 +44,87 @@ describe('NodeTmuxSessionRepository', () => {
|
|
|
44
44
|
});
|
|
45
45
|
});
|
|
46
46
|
|
|
47
|
+
describe('listLiveSessionsWithActivity', () => {
|
|
48
|
+
it('parses session names and activity epoch seconds and drops blank lines', async () => {
|
|
49
|
+
const runner = createMockRunner();
|
|
50
|
+
runner.runCommand.mockResolvedValue({
|
|
51
|
+
stdout:
|
|
52
|
+
'https_//github_com/owner/repo/issues/9 1700000000\nno_task_session 1699000000\n\n',
|
|
53
|
+
stderr: '',
|
|
54
|
+
exitCode: 0,
|
|
55
|
+
});
|
|
56
|
+
const repository = new NodeTmuxSessionRepository(runner);
|
|
57
|
+
|
|
58
|
+
const result = await repository.listLiveSessionsWithActivity();
|
|
59
|
+
|
|
60
|
+
expect(result).toEqual([
|
|
61
|
+
{
|
|
62
|
+
sessionName: 'https_//github_com/owner/repo/issues/9',
|
|
63
|
+
activityEpochSeconds: 1700000000,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
sessionName: 'no_task_session',
|
|
67
|
+
activityEpochSeconds: 1699000000,
|
|
68
|
+
},
|
|
69
|
+
]);
|
|
70
|
+
expect(runner.runCommand.mock.calls[0][0]).toBe('tmux');
|
|
71
|
+
expect(runner.runCommand.mock.calls[0][1]).toEqual([
|
|
72
|
+
'list-sessions',
|
|
73
|
+
'-F',
|
|
74
|
+
'#{session_name} #{session_activity}',
|
|
75
|
+
]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('returns an empty list when tmux exits non-zero', async () => {
|
|
79
|
+
const runner = createMockRunner();
|
|
80
|
+
runner.runCommand.mockResolvedValue({
|
|
81
|
+
stdout: '',
|
|
82
|
+
stderr: 'no server running',
|
|
83
|
+
exitCode: 1,
|
|
84
|
+
});
|
|
85
|
+
const repository = new NodeTmuxSessionRepository(runner);
|
|
86
|
+
|
|
87
|
+
const result = await repository.listLiveSessionsWithActivity();
|
|
88
|
+
|
|
89
|
+
expect(result).toEqual([]);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('killSession', () => {
|
|
94
|
+
it('kills the tmux session by name', async () => {
|
|
95
|
+
const runner = createMockRunner();
|
|
96
|
+
runner.runCommand.mockResolvedValue({
|
|
97
|
+
stdout: '',
|
|
98
|
+
stderr: '',
|
|
99
|
+
exitCode: 0,
|
|
100
|
+
});
|
|
101
|
+
const repository = new NodeTmuxSessionRepository(runner);
|
|
102
|
+
|
|
103
|
+
await repository.killSession('no_task_session');
|
|
104
|
+
|
|
105
|
+
expect(runner.runCommand.mock.calls[0][0]).toBe('tmux');
|
|
106
|
+
expect(runner.runCommand.mock.calls[0][1]).toEqual([
|
|
107
|
+
'kill-session',
|
|
108
|
+
'-t',
|
|
109
|
+
'no_task_session',
|
|
110
|
+
]);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('throws when tmux exits non-zero', async () => {
|
|
114
|
+
const runner = createMockRunner();
|
|
115
|
+
runner.runCommand.mockResolvedValue({
|
|
116
|
+
stdout: '',
|
|
117
|
+
stderr: "can't find session",
|
|
118
|
+
exitCode: 1,
|
|
119
|
+
});
|
|
120
|
+
const repository = new NodeTmuxSessionRepository(runner);
|
|
121
|
+
|
|
122
|
+
await expect(repository.killSession('missing_session')).rejects.toThrow(
|
|
123
|
+
'Failed to kill tmux session "missing_session": exit code 1',
|
|
124
|
+
);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
47
128
|
describe('listInteractiveProcessCommandLines', () => {
|
|
48
129
|
it('parses process command lines from ps output', async () => {
|
|
49
130
|
const runner = createMockRunner();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { LocalCommandRunner } from '../../domain/usecases/adapter-interfaces/LocalCommandRunner';
|
|
2
2
|
import { TmuxSessionRepository } from '../../domain/usecases/adapter-interfaces/TmuxSessionRepository';
|
|
3
|
+
import { LiveTmuxSession } from '../../domain/entities/LiveTmuxSession';
|
|
3
4
|
|
|
4
5
|
export class NodeTmuxSessionRepository implements TmuxSessionRepository {
|
|
5
6
|
constructor(private readonly localCommandRunner: LocalCommandRunner) {}
|
|
@@ -18,6 +19,26 @@ export class NodeTmuxSessionRepository implements TmuxSessionRepository {
|
|
|
18
19
|
.filter((line) => line.length > 0);
|
|
19
20
|
};
|
|
20
21
|
|
|
22
|
+
listLiveSessionsWithActivity = async (): Promise<LiveTmuxSession[]> => {
|
|
23
|
+
const { stdout, exitCode } = await this.localCommandRunner.runCommand(
|
|
24
|
+
'tmux',
|
|
25
|
+
['list-sessions', '-F', '#{session_name} #{session_activity}'],
|
|
26
|
+
);
|
|
27
|
+
if (exitCode !== 0) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
return stdout
|
|
31
|
+
.split('\n')
|
|
32
|
+
.map((line) => line.trim())
|
|
33
|
+
.filter((line) => line.length > 0)
|
|
34
|
+
.map((line) => {
|
|
35
|
+
const separatorIndex = line.lastIndexOf(' ');
|
|
36
|
+
const sessionName = line.slice(0, separatorIndex);
|
|
37
|
+
const activityEpochSeconds = Number(line.slice(separatorIndex + 1));
|
|
38
|
+
return { sessionName, activityEpochSeconds };
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
21
42
|
listInteractiveProcessCommandLines = async (): Promise<string[]> => {
|
|
22
43
|
const { stdout, exitCode } = await this.localCommandRunner.runCommand(
|
|
23
44
|
'ps',
|
|
@@ -51,4 +72,18 @@ export class NodeTmuxSessionRepository implements TmuxSessionRepository {
|
|
|
51
72
|
issueUrl,
|
|
52
73
|
]);
|
|
53
74
|
};
|
|
75
|
+
|
|
76
|
+
killSession = async (sessionName: string): Promise<void> => {
|
|
77
|
+
const { stderr, exitCode } = await this.localCommandRunner.runCommand(
|
|
78
|
+
'tmux',
|
|
79
|
+
['kill-session', '-t', sessionName],
|
|
80
|
+
);
|
|
81
|
+
if (exitCode !== 0) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Failed to kill tmux session "${sessionName}": exit code ${exitCode}${
|
|
84
|
+
stderr ? `: ${stderr}` : ''
|
|
85
|
+
}`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
54
89
|
}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import {
|
|
2
|
+
StaleTmuxSessionKillUseCase,
|
|
3
|
+
DEFAULT_EXCLUDED_STATUS,
|
|
4
|
+
DEFAULT_IDLE_THRESHOLD_SECONDS,
|
|
5
|
+
} from './StaleTmuxSessionKillUseCase';
|
|
6
|
+
import { IssueRepository } from './adapter-interfaces/IssueRepository';
|
|
7
|
+
import { TmuxSessionRepository } from './adapter-interfaces/TmuxSessionRepository';
|
|
8
|
+
import { toTmuxSessionName } from './intmux/InTmuxByHumanSessionReconcileUseCase';
|
|
9
|
+
import { Issue } from '../entities/Issue';
|
|
10
|
+
import { LiveTmuxSession } from '../entities/LiveTmuxSession';
|
|
11
|
+
import { Project } from '../entities/Project';
|
|
12
|
+
import { IN_TMUX_STATUS_NAME } from '../entities/WorkflowStatus';
|
|
13
|
+
|
|
14
|
+
type Mocked<T> = jest.Mocked<T> & jest.MockedObject<T>;
|
|
15
|
+
|
|
16
|
+
const createMockProject = (): Project => ({
|
|
17
|
+
id: 'project-1',
|
|
18
|
+
url: 'https://github.com/users/user/projects/1',
|
|
19
|
+
databaseId: 1,
|
|
20
|
+
name: 'Test Project',
|
|
21
|
+
status: {
|
|
22
|
+
name: 'Status',
|
|
23
|
+
fieldId: 'field-1',
|
|
24
|
+
statuses: [],
|
|
25
|
+
},
|
|
26
|
+
nextActionDate: null,
|
|
27
|
+
nextActionHour: null,
|
|
28
|
+
story: null,
|
|
29
|
+
remainingEstimationMinutes: null,
|
|
30
|
+
dependedIssueUrlSeparatedByComma: null,
|
|
31
|
+
completionDate50PercentConfidence: null,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
let issueCounter = 0;
|
|
35
|
+
const createMockIssue = (overrides: Partial<Issue> = {}): Issue => {
|
|
36
|
+
issueCounter += 1;
|
|
37
|
+
return {
|
|
38
|
+
nameWithOwner: 'user/repo',
|
|
39
|
+
number: issueCounter,
|
|
40
|
+
title: `Test Issue ${issueCounter}`,
|
|
41
|
+
state: 'OPEN',
|
|
42
|
+
status: IN_TMUX_STATUS_NAME,
|
|
43
|
+
story: null,
|
|
44
|
+
nextActionDate: null,
|
|
45
|
+
nextActionHour: null,
|
|
46
|
+
estimationMinutes: null,
|
|
47
|
+
dependedIssueUrls: [],
|
|
48
|
+
completionDate50PercentConfidence: null,
|
|
49
|
+
url: `https://github.com/user/repo/issues/${issueCounter}`,
|
|
50
|
+
assignees: [],
|
|
51
|
+
labels: [],
|
|
52
|
+
org: 'user',
|
|
53
|
+
repo: 'repo',
|
|
54
|
+
body: '',
|
|
55
|
+
itemId: `item-${issueCounter}`,
|
|
56
|
+
isPr: false,
|
|
57
|
+
isInProgress: false,
|
|
58
|
+
isClosed: false,
|
|
59
|
+
createdAt: new Date(),
|
|
60
|
+
author: 'testuser',
|
|
61
|
+
closingIssueReferenceUrls: [],
|
|
62
|
+
...overrides,
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
describe('StaleTmuxSessionKillUseCase', () => {
|
|
67
|
+
let useCase: StaleTmuxSessionKillUseCase;
|
|
68
|
+
let mockIssueRepository: Mocked<Pick<IssueRepository, 'getAllOpened'>>;
|
|
69
|
+
let mockTmuxSessionRepository: Mocked<
|
|
70
|
+
Pick<TmuxSessionRepository, 'listLiveSessionsWithActivity' | 'killSession'>
|
|
71
|
+
>;
|
|
72
|
+
let mockProject: Project;
|
|
73
|
+
const now = new Date('2026-06-26T00:00:00Z');
|
|
74
|
+
const nowEpochSeconds = Math.floor(now.getTime() / 1000);
|
|
75
|
+
const allowCacheMinutes = 10;
|
|
76
|
+
|
|
77
|
+
const runParams = (): {
|
|
78
|
+
project: Project;
|
|
79
|
+
allowCacheMinutes: number;
|
|
80
|
+
excludedStatus: string;
|
|
81
|
+
idleThresholdSeconds: number;
|
|
82
|
+
now: Date;
|
|
83
|
+
} => ({
|
|
84
|
+
project: mockProject,
|
|
85
|
+
allowCacheMinutes,
|
|
86
|
+
excludedStatus: DEFAULT_EXCLUDED_STATUS,
|
|
87
|
+
idleThresholdSeconds: DEFAULT_IDLE_THRESHOLD_SECONDS,
|
|
88
|
+
now,
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
const setLiveSessions = (sessions: LiveTmuxSession[]): void => {
|
|
92
|
+
mockTmuxSessionRepository.listLiveSessionsWithActivity.mockResolvedValue(
|
|
93
|
+
sessions,
|
|
94
|
+
);
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
beforeEach(() => {
|
|
98
|
+
jest.resetAllMocks();
|
|
99
|
+
jest.spyOn(console, 'log').mockImplementation(() => undefined);
|
|
100
|
+
mockProject = createMockProject();
|
|
101
|
+
mockIssueRepository = {
|
|
102
|
+
getAllOpened: jest.fn().mockResolvedValue([]),
|
|
103
|
+
};
|
|
104
|
+
mockTmuxSessionRepository = {
|
|
105
|
+
listLiveSessionsWithActivity: jest.fn().mockResolvedValue([]),
|
|
106
|
+
killSession: jest.fn().mockResolvedValue(undefined),
|
|
107
|
+
};
|
|
108
|
+
useCase = new StaleTmuxSessionKillUseCase(
|
|
109
|
+
mockIssueRepository,
|
|
110
|
+
mockTmuxSessionRepository,
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('exposes the excluded status and idle threshold as named constants', () => {
|
|
115
|
+
expect(DEFAULT_EXCLUDED_STATUS).toBe('In Tmux by human');
|
|
116
|
+
expect(DEFAULT_EXCLUDED_STATUS).toBe(IN_TMUX_STATUS_NAME);
|
|
117
|
+
expect(DEFAULT_IDLE_THRESHOLD_SECONDS).toBe(86400);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('lists live sessions with activity and loads open issues for the project', async () => {
|
|
121
|
+
setLiveSessions([]);
|
|
122
|
+
await useCase.run(runParams());
|
|
123
|
+
expect(
|
|
124
|
+
mockTmuxSessionRepository.listLiveSessionsWithActivity,
|
|
125
|
+
).toHaveBeenCalledTimes(1);
|
|
126
|
+
expect(mockIssueRepository.getAllOpened).toHaveBeenCalledWith(
|
|
127
|
+
mockProject,
|
|
128
|
+
allowCacheMinutes,
|
|
129
|
+
);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('kills a session mapping to an open issue whose status is not the excluded status', async () => {
|
|
133
|
+
const issue = createMockIssue({ status: 'In Progress' });
|
|
134
|
+
const sessionName = toTmuxSessionName(issue.url);
|
|
135
|
+
setLiveSessions([{ sessionName, activityEpochSeconds: nowEpochSeconds }]);
|
|
136
|
+
mockIssueRepository.getAllOpened.mockResolvedValue([issue]);
|
|
137
|
+
await useCase.run(runParams());
|
|
138
|
+
expect(mockTmuxSessionRepository.killSession).toHaveBeenCalledWith(
|
|
139
|
+
sessionName,
|
|
140
|
+
);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('kills a session mapping to an open issue whose status is null', async () => {
|
|
144
|
+
const issue = createMockIssue({ status: null });
|
|
145
|
+
const sessionName = toTmuxSessionName(issue.url);
|
|
146
|
+
setLiveSessions([{ sessionName, activityEpochSeconds: nowEpochSeconds }]);
|
|
147
|
+
mockIssueRepository.getAllOpened.mockResolvedValue([issue]);
|
|
148
|
+
await useCase.run(runParams());
|
|
149
|
+
expect(mockTmuxSessionRepository.killSession).toHaveBeenCalledWith(
|
|
150
|
+
sessionName,
|
|
151
|
+
);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('kills an excluded-status session that has a next action date set', async () => {
|
|
155
|
+
const issue = createMockIssue({
|
|
156
|
+
status: DEFAULT_EXCLUDED_STATUS,
|
|
157
|
+
nextActionDate: new Date('2026-06-27T00:00:00Z'),
|
|
158
|
+
});
|
|
159
|
+
const sessionName = toTmuxSessionName(issue.url);
|
|
160
|
+
setLiveSessions([{ sessionName, activityEpochSeconds: nowEpochSeconds }]);
|
|
161
|
+
mockIssueRepository.getAllOpened.mockResolvedValue([issue]);
|
|
162
|
+
await useCase.run(runParams());
|
|
163
|
+
expect(mockTmuxSessionRepository.killSession).toHaveBeenCalledWith(
|
|
164
|
+
sessionName,
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('kills an excluded-status session that has a next action hour set', async () => {
|
|
169
|
+
const issue = createMockIssue({
|
|
170
|
+
status: DEFAULT_EXCLUDED_STATUS,
|
|
171
|
+
nextActionHour: 9,
|
|
172
|
+
});
|
|
173
|
+
const sessionName = toTmuxSessionName(issue.url);
|
|
174
|
+
setLiveSessions([{ sessionName, activityEpochSeconds: nowEpochSeconds }]);
|
|
175
|
+
mockIssueRepository.getAllOpened.mockResolvedValue([issue]);
|
|
176
|
+
await useCase.run(runParams());
|
|
177
|
+
expect(mockTmuxSessionRepository.killSession).toHaveBeenCalledWith(
|
|
178
|
+
sessionName,
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('never kills an excluded-status session that has no reactivation trigger', async () => {
|
|
183
|
+
const issue = createMockIssue({
|
|
184
|
+
status: DEFAULT_EXCLUDED_STATUS,
|
|
185
|
+
nextActionDate: null,
|
|
186
|
+
nextActionHour: null,
|
|
187
|
+
});
|
|
188
|
+
const sessionName = toTmuxSessionName(issue.url);
|
|
189
|
+
setLiveSessions([{ sessionName, activityEpochSeconds: nowEpochSeconds }]);
|
|
190
|
+
mockIssueRepository.getAllOpened.mockResolvedValue([issue]);
|
|
191
|
+
await useCase.run(runParams());
|
|
192
|
+
expect(mockTmuxSessionRepository.killSession).not.toHaveBeenCalled();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('kills a no-task session that has been idle at least the idle threshold', async () => {
|
|
196
|
+
const sessionName = 'no_task_session';
|
|
197
|
+
const idleActivity = nowEpochSeconds - DEFAULT_IDLE_THRESHOLD_SECONDS;
|
|
198
|
+
setLiveSessions([{ sessionName, activityEpochSeconds: idleActivity }]);
|
|
199
|
+
mockIssueRepository.getAllOpened.mockResolvedValue([]);
|
|
200
|
+
await useCase.run(runParams());
|
|
201
|
+
expect(mockTmuxSessionRepository.killSession).toHaveBeenCalledWith(
|
|
202
|
+
sessionName,
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('never kills a no-task session that was active within the idle threshold', async () => {
|
|
207
|
+
const sessionName = 'no_task_session';
|
|
208
|
+
const recentActivity = nowEpochSeconds - DEFAULT_IDLE_THRESHOLD_SECONDS + 1;
|
|
209
|
+
setLiveSessions([{ sessionName, activityEpochSeconds: recentActivity }]);
|
|
210
|
+
mockIssueRepository.getAllOpened.mockResolvedValue([]);
|
|
211
|
+
await useCase.run(runParams());
|
|
212
|
+
expect(mockTmuxSessionRepository.killSession).not.toHaveBeenCalled();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('does nothing when there are no live sessions', async () => {
|
|
216
|
+
setLiveSessions([]);
|
|
217
|
+
mockIssueRepository.getAllOpened.mockResolvedValue([]);
|
|
218
|
+
await useCase.run(runParams());
|
|
219
|
+
expect(mockTmuxSessionRepository.killSession).not.toHaveBeenCalled();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('maps a session back to its issue using the dot-and-colon-to-underscore convention', async () => {
|
|
223
|
+
const issue = createMockIssue({
|
|
224
|
+
url: 'https://github.com/owner/repo/issues/9',
|
|
225
|
+
status: 'In Progress',
|
|
226
|
+
});
|
|
227
|
+
const sessionName = 'https_//github_com/owner/repo/issues/9';
|
|
228
|
+
expect(toTmuxSessionName(issue.url)).toBe(sessionName);
|
|
229
|
+
setLiveSessions([{ sessionName, activityEpochSeconds: nowEpochSeconds }]);
|
|
230
|
+
mockIssueRepository.getAllOpened.mockResolvedValue([issue]);
|
|
231
|
+
await useCase.run(runParams());
|
|
232
|
+
expect(mockTmuxSessionRepository.killSession).toHaveBeenCalledWith(
|
|
233
|
+
sessionName,
|
|
234
|
+
);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('enumerates and logs the kill-candidate list before killing', async () => {
|
|
238
|
+
const logSpy = jest.spyOn(console, 'log');
|
|
239
|
+
const killOrder: string[] = [];
|
|
240
|
+
mockTmuxSessionRepository.killSession.mockImplementation(
|
|
241
|
+
async (sessionName: string) => {
|
|
242
|
+
killOrder.push(sessionName);
|
|
243
|
+
},
|
|
244
|
+
);
|
|
245
|
+
const issue = createMockIssue({ status: 'In Progress' });
|
|
246
|
+
const sessionName = toTmuxSessionName(issue.url);
|
|
247
|
+
setLiveSessions([{ sessionName, activityEpochSeconds: nowEpochSeconds }]);
|
|
248
|
+
mockIssueRepository.getAllOpened.mockResolvedValue([issue]);
|
|
249
|
+
|
|
250
|
+
await useCase.run(runParams());
|
|
251
|
+
|
|
252
|
+
const candidateLogIndex = logSpy.mock.calls.findIndex((call) =>
|
|
253
|
+
String(call[0]).startsWith('Kill candidate:'),
|
|
254
|
+
);
|
|
255
|
+
const killedLogIndex = logSpy.mock.calls.findIndex((call) =>
|
|
256
|
+
String(call[0]).startsWith('Killed tmux session:'),
|
|
257
|
+
);
|
|
258
|
+
expect(candidateLogIndex).toBeGreaterThanOrEqual(0);
|
|
259
|
+
expect(killedLogIndex).toBeGreaterThan(candidateLogIndex);
|
|
260
|
+
expect(killOrder).toEqual([sessionName]);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('does not suppress errors raised while killing a session', async () => {
|
|
264
|
+
const issue = createMockIssue({ status: 'In Progress' });
|
|
265
|
+
const sessionName = toTmuxSessionName(issue.url);
|
|
266
|
+
setLiveSessions([{ sessionName, activityEpochSeconds: nowEpochSeconds }]);
|
|
267
|
+
mockIssueRepository.getAllOpened.mockResolvedValue([issue]);
|
|
268
|
+
mockTmuxSessionRepository.killSession.mockRejectedValue(
|
|
269
|
+
new Error('tmux kill failed'),
|
|
270
|
+
);
|
|
271
|
+
await expect(useCase.run(runParams())).rejects.toThrow('tmux kill failed');
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('does not kill any session when loading open issues fails', async () => {
|
|
275
|
+
const sessionName = 'no_task_session';
|
|
276
|
+
const idleActivity = nowEpochSeconds - DEFAULT_IDLE_THRESHOLD_SECONDS;
|
|
277
|
+
setLiveSessions([{ sessionName, activityEpochSeconds: idleActivity }]);
|
|
278
|
+
mockIssueRepository.getAllOpened.mockRejectedValue(
|
|
279
|
+
new Error('failed to load issues'),
|
|
280
|
+
);
|
|
281
|
+
await expect(useCase.run(runParams())).rejects.toThrow(
|
|
282
|
+
'failed to load issues',
|
|
283
|
+
);
|
|
284
|
+
expect(mockTmuxSessionRepository.killSession).not.toHaveBeenCalled();
|
|
285
|
+
});
|
|
286
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Issue } from '../entities/Issue';
|
|
2
|
+
import { LiveTmuxSession } from '../entities/LiveTmuxSession';
|
|
3
|
+
import { Project } from '../entities/Project';
|
|
4
|
+
import { IN_TMUX_STATUS_NAME } from '../entities/WorkflowStatus';
|
|
5
|
+
import { IssueRepository } from './adapter-interfaces/IssueRepository';
|
|
6
|
+
import { TmuxSessionRepository } from './adapter-interfaces/TmuxSessionRepository';
|
|
7
|
+
import { toTmuxSessionName } from './intmux/InTmuxByHumanSessionReconcileUseCase';
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_EXCLUDED_STATUS = IN_TMUX_STATUS_NAME;
|
|
10
|
+
export const DEFAULT_IDLE_THRESHOLD_SECONDS = 24 * 60 * 60;
|
|
11
|
+
|
|
12
|
+
type KillCandidate = {
|
|
13
|
+
sessionName: string;
|
|
14
|
+
reason: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export class StaleTmuxSessionKillUseCase {
|
|
18
|
+
constructor(
|
|
19
|
+
private readonly issueRepository: Pick<IssueRepository, 'getAllOpened'>,
|
|
20
|
+
private readonly tmuxSessionRepository: Pick<
|
|
21
|
+
TmuxSessionRepository,
|
|
22
|
+
'listLiveSessionsWithActivity' | 'killSession'
|
|
23
|
+
>,
|
|
24
|
+
) {}
|
|
25
|
+
|
|
26
|
+
run = async (params: {
|
|
27
|
+
project: Project;
|
|
28
|
+
allowCacheMinutes: number;
|
|
29
|
+
excludedStatus: string;
|
|
30
|
+
idleThresholdSeconds: number;
|
|
31
|
+
now: Date;
|
|
32
|
+
}): Promise<void> => {
|
|
33
|
+
const liveSessions =
|
|
34
|
+
await this.tmuxSessionRepository.listLiveSessionsWithActivity();
|
|
35
|
+
const openIssues = await this.issueRepository.getAllOpened(
|
|
36
|
+
params.project,
|
|
37
|
+
params.allowCacheMinutes,
|
|
38
|
+
);
|
|
39
|
+
const issueBySessionName = new Map<string, Issue>();
|
|
40
|
+
for (const issue of openIssues) {
|
|
41
|
+
issueBySessionName.set(toTmuxSessionName(issue.url), issue);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const nowEpochSeconds = Math.floor(params.now.getTime() / 1000);
|
|
45
|
+
const killCandidates: KillCandidate[] = [];
|
|
46
|
+
for (const session of liveSessions) {
|
|
47
|
+
const reason = this.evaluateKillReason(
|
|
48
|
+
session,
|
|
49
|
+
issueBySessionName.get(session.sessionName) ?? null,
|
|
50
|
+
nowEpochSeconds,
|
|
51
|
+
params.excludedStatus,
|
|
52
|
+
params.idleThresholdSeconds,
|
|
53
|
+
);
|
|
54
|
+
if (reason !== null) {
|
|
55
|
+
killCandidates.push({ sessionName: session.sessionName, reason });
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log(
|
|
60
|
+
`Stale tmux session cleanup: ${killCandidates.length} kill candidate(s) of ${liveSessions.length} live session(s).`,
|
|
61
|
+
);
|
|
62
|
+
for (const candidate of killCandidates) {
|
|
63
|
+
console.log(
|
|
64
|
+
`Kill candidate: ${candidate.sessionName} (${candidate.reason})`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
for (const candidate of killCandidates) {
|
|
69
|
+
await this.tmuxSessionRepository.killSession(candidate.sessionName);
|
|
70
|
+
console.log(
|
|
71
|
+
`Killed tmux session: ${candidate.sessionName} (${candidate.reason})`,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
private evaluateKillReason = (
|
|
77
|
+
session: LiveTmuxSession,
|
|
78
|
+
issue: Issue | null,
|
|
79
|
+
nowEpochSeconds: number,
|
|
80
|
+
excludedStatus: string,
|
|
81
|
+
idleThresholdSeconds: number,
|
|
82
|
+
): string | null => {
|
|
83
|
+
if (issue !== null) {
|
|
84
|
+
if (issue.status !== excludedStatus) {
|
|
85
|
+
return `mapped to open issue ${issue.url} with status "${issue.status ?? 'null'}" which is not the excluded status "${excludedStatus}"`;
|
|
86
|
+
}
|
|
87
|
+
if (issue.nextActionDate !== null) {
|
|
88
|
+
return `mapped to open issue ${issue.url} which has a next action date set`;
|
|
89
|
+
}
|
|
90
|
+
if (issue.nextActionHour !== null) {
|
|
91
|
+
return `mapped to open issue ${issue.url} which has a next action hour set`;
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const idleSeconds = nowEpochSeconds - session.activityEpochSeconds;
|
|
97
|
+
if (idleSeconds >= idleThresholdSeconds) {
|
|
98
|
+
return `maps to no open issue and has been idle for ${idleSeconds} seconds (threshold ${idleThresholdSeconds} seconds)`;
|
|
99
|
+
}
|
|
100
|
+
return null;
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import { LiveTmuxSession } from '../../entities/LiveTmuxSession';
|
|
2
|
+
|
|
1
3
|
export interface TmuxSessionRepository {
|
|
2
4
|
listLiveSessionNames: () => Promise<string[]>;
|
|
5
|
+
listLiveSessionsWithActivity: () => Promise<LiveTmuxSession[]>;
|
|
3
6
|
listInteractiveProcessCommandLines: () => Promise<string[]>;
|
|
4
7
|
launchDetachedSession: (
|
|
5
8
|
sessionName: string,
|
|
6
9
|
launcherCommand: string,
|
|
7
10
|
issueUrl: string,
|
|
8
11
|
) => Promise<void>;
|
|
12
|
+
killSession: (sessionName: string) => Promise<void>;
|
|
9
13
|
}
|
|
@@ -60,10 +60,16 @@ const createFakeTmuxSessionRepository = (state: {
|
|
|
60
60
|
return {
|
|
61
61
|
launches,
|
|
62
62
|
listLiveSessionNames: async () => state.liveSessionNames,
|
|
63
|
+
listLiveSessionsWithActivity: async () =>
|
|
64
|
+
state.liveSessionNames.map((sessionName) => ({
|
|
65
|
+
sessionName,
|
|
66
|
+
activityEpochSeconds: 0,
|
|
67
|
+
})),
|
|
63
68
|
listInteractiveProcessCommandLines: async () => state.processCommandLines,
|
|
64
69
|
launchDetachedSession: async (sessionName, launcherCommand, issueUrl) => {
|
|
65
70
|
launches.push({ sessionName, launcherCommand, issueUrl });
|
|
66
71
|
},
|
|
72
|
+
killSession: async () => undefined,
|
|
67
73
|
};
|
|
68
74
|
};
|
|
69
75
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAgC3D,qBAAa,kCAAkC;IAC7C,MAAM,GACJ,gBAAgB,MAAM,EACtB,UAAU,OAAO,KAChB,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,eAAe,EAAE,IAAI,EAAE,CAAC;KACzB,GAAG,IAAI,CAAC,CAwcP;CACH"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Project } from '../../../domain/entities/Project';
|
|
2
|
+
import { LocalCommandRunner } from '../../../domain/usecases/adapter-interfaces/LocalCommandRunner';
|
|
3
|
+
import { IssueRepository } from '../../../domain/usecases/adapter-interfaces/IssueRepository';
|
|
4
|
+
export type CleanStaleTmuxSessionsParams = {
|
|
5
|
+
project: Project;
|
|
6
|
+
allowCacheMinutes: number;
|
|
7
|
+
issueRepository: Pick<IssueRepository, 'getAllOpened'>;
|
|
8
|
+
localCommandRunner: LocalCommandRunner;
|
|
9
|
+
now: Date;
|
|
10
|
+
};
|
|
11
|
+
export declare const cleanStaleTmuxSessions: (params: CleanStaleTmuxSessionsParams) => Promise<void>;
|
|
12
|
+
//# sourceMappingURL=staleTmuxSessionCleaner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staleTmuxSessionCleaner.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/staleTmuxSessionCleaner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAC;AACpG,OAAO,EAAE,eAAe,EAAE,MAAM,6DAA6D,CAAC;AAQ9F,MAAM,MAAM,4BAA4B,GAAG;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC;IACvD,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,GAAG,EAAE,IAAI,CAAC;CACX,CAAC;AAEF,eAAO,MAAM,sBAAsB,GACjC,QAAQ,4BAA4B,KACnC,OAAO,CAAC,IAAI,CAmBd,CAAC"}
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { LocalCommandRunner } from '../../domain/usecases/adapter-interfaces/LocalCommandRunner';
|
|
2
2
|
import { TmuxSessionRepository } from '../../domain/usecases/adapter-interfaces/TmuxSessionRepository';
|
|
3
|
+
import { LiveTmuxSession } from '../../domain/entities/LiveTmuxSession';
|
|
3
4
|
export declare class NodeTmuxSessionRepository implements TmuxSessionRepository {
|
|
4
5
|
private readonly localCommandRunner;
|
|
5
6
|
constructor(localCommandRunner: LocalCommandRunner);
|
|
6
7
|
listLiveSessionNames: () => Promise<string[]>;
|
|
8
|
+
listLiveSessionsWithActivity: () => Promise<LiveTmuxSession[]>;
|
|
7
9
|
listInteractiveProcessCommandLines: () => Promise<string[]>;
|
|
8
10
|
launchDetachedSession: (sessionName: string, launcherCommand: string, issueUrl: string) => Promise<void>;
|
|
11
|
+
killSession: (sessionName: string) => Promise<void>;
|
|
9
12
|
}
|
|
10
13
|
//# sourceMappingURL=NodeTmuxSessionRepository.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeTmuxSessionRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/NodeTmuxSessionRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6DAA6D,CAAC;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,gEAAgE,CAAC;
|
|
1
|
+
{"version":3,"file":"NodeTmuxSessionRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/NodeTmuxSessionRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,6DAA6D,CAAC;AACjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,gEAAgE,CAAC;AACvG,OAAO,EAAE,eAAe,EAAE,MAAM,uCAAuC,CAAC;AAExE,qBAAa,yBAA0B,YAAW,qBAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,kBAAkB;gBAAlB,kBAAkB,EAAE,kBAAkB;IAEnE,oBAAoB,QAAa,OAAO,CAAC,MAAM,EAAE,CAAC,CAYhD;IAEF,4BAA4B,QAAa,OAAO,CAAC,eAAe,EAAE,CAAC,CAkBjE;IAEF,kCAAkC,QAAa,OAAO,CAAC,MAAM,EAAE,CAAC,CAY9D;IAEF,qBAAqB,GACnB,aAAa,MAAM,EACnB,iBAAiB,MAAM,EACvB,UAAU,MAAM,KACf,OAAO,CAAC,IAAI,CAAC,CAcd;IAEF,WAAW,GAAU,aAAa,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CAYtD;CACH"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LiveTmuxSession.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/LiveTmuxSession.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Project } from '../entities/Project';
|
|
2
|
+
import { IssueRepository } from './adapter-interfaces/IssueRepository';
|
|
3
|
+
import { TmuxSessionRepository } from './adapter-interfaces/TmuxSessionRepository';
|
|
4
|
+
export declare const DEFAULT_EXCLUDED_STATUS = "In Tmux by human";
|
|
5
|
+
export declare const DEFAULT_IDLE_THRESHOLD_SECONDS: number;
|
|
6
|
+
export declare class StaleTmuxSessionKillUseCase {
|
|
7
|
+
private readonly issueRepository;
|
|
8
|
+
private readonly tmuxSessionRepository;
|
|
9
|
+
constructor(issueRepository: Pick<IssueRepository, 'getAllOpened'>, tmuxSessionRepository: Pick<TmuxSessionRepository, 'listLiveSessionsWithActivity' | 'killSession'>);
|
|
10
|
+
run: (params: {
|
|
11
|
+
project: Project;
|
|
12
|
+
allowCacheMinutes: number;
|
|
13
|
+
excludedStatus: string;
|
|
14
|
+
idleThresholdSeconds: number;
|
|
15
|
+
now: Date;
|
|
16
|
+
}) => Promise<void>;
|
|
17
|
+
private evaluateKillReason;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=StaleTmuxSessionKillUseCase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StaleTmuxSessionKillUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/StaleTmuxSessionKillUseCase.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE9C,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4CAA4C,CAAC;AAGnF,eAAO,MAAM,uBAAuB,qBAAsB,CAAC;AAC3D,eAAO,MAAM,8BAA8B,QAAe,CAAC;AAO3D,qBAAa,2BAA2B;IAEpC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,qBAAqB;gBADrB,eAAe,EAAE,IAAI,CAAC,eAAe,EAAE,cAAc,CAAC,EACtD,qBAAqB,EAAE,IAAI,CAC1C,qBAAqB,EACrB,8BAA8B,GAAG,aAAa,CAC/C;IAGH,GAAG,GAAU,QAAQ;QACnB,OAAO,EAAE,OAAO,CAAC;QACjB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,cAAc,EAAE,MAAM,CAAC;QACvB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,GAAG,EAAE,IAAI,CAAC;KACX,KAAG,OAAO,CAAC,IAAI,CAAC,CA0Cf;IAEF,OAAO,CAAC,kBAAkB,CAyBxB;CACH"}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import { LiveTmuxSession } from '../../entities/LiveTmuxSession';
|
|
1
2
|
export interface TmuxSessionRepository {
|
|
2
3
|
listLiveSessionNames: () => Promise<string[]>;
|
|
4
|
+
listLiveSessionsWithActivity: () => Promise<LiveTmuxSession[]>;
|
|
3
5
|
listInteractiveProcessCommandLines: () => Promise<string[]>;
|
|
4
6
|
launchDetachedSession: (sessionName: string, launcherCommand: string, issueUrl: string) => Promise<void>;
|
|
7
|
+
killSession: (sessionName: string) => Promise<void>;
|
|
5
8
|
}
|
|
6
9
|
//# sourceMappingURL=TmuxSessionRepository.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TmuxSessionRepository.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/TmuxSessionRepository.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,qBAAqB;IACpC,oBAAoB,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9C,kCAAkC,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5D,qBAAqB,EAAE,CACrB,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"TmuxSessionRepository.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/TmuxSessionRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAEjE,MAAM,WAAW,qBAAqB;IACpC,oBAAoB,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9C,4BAA4B,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAC/D,kCAAkC,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5D,qBAAqB,EAAE,CACrB,WAAW,EAAE,MAAM,EACnB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,KACb,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,WAAW,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD"}
|