github-issue-tower-defence-management 1.96.0 → 1.97.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/README.md +9 -0
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +18 -2
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.js +19 -0
- package/bin/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.js.map +1 -0
- package/bin/adapter/repositories/NodeTmuxSessionRepository.js +44 -0
- package/bin/adapter/repositories/NodeTmuxSessionRepository.js.map +1 -0
- package/bin/domain/usecases/adapter-interfaces/TmuxSessionRepository.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/TmuxSessionRepository.js.map +1 -0
- package/bin/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.js +41 -0
- package/bin/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.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/inTmuxByHumanSessionReconciler.test.ts +123 -0
- package/src/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.ts +29 -0
- package/src/adapter/repositories/NodeTmuxSessionRepository.test.ts +100 -0
- package/src/adapter/repositories/NodeTmuxSessionRepository.ts +53 -0
- package/src/domain/usecases/adapter-interfaces/TmuxSessionRepository.ts +9 -0
- package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.test.ts +186 -0
- package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.ts +75 -0
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.d.ts +10 -0
- package/types/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.d.ts.map +1 -0
- package/types/adapter/repositories/NodeTmuxSessionRepository.d.ts +10 -0
- package/types/adapter/repositories/NodeTmuxSessionRepository.d.ts.map +1 -0
- package/types/domain/usecases/adapter-interfaces/TmuxSessionRepository.d.ts +6 -0
- package/types/domain/usecases/adapter-interfaces/TmuxSessionRepository.d.ts.map +1 -0
- package/types/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.d.ts +19 -0
- package/types/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.d.ts.map +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
# [1.97.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.96.0...v1.97.0) (2026-06-23)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* **intmux:** restart missing In Tmux by human sessions each schedule cycle ([#917](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/917)) ([0afe2b1](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/0afe2b1bc3a4c2ec76d51c0cd9337ed10bf871ec)), closes [#916](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/916)
|
|
7
|
+
|
|
1
8
|
# [1.96.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.95.0...v1.96.0) (2026-06-23)
|
|
2
9
|
|
|
3
10
|
|
package/README.md
CHANGED
|
@@ -261,6 +261,7 @@ inTmuxDataOutputDir?: string # Optional: Base output directory for the in-tmux-b
|
|
|
261
261
|
inTmuxConsoleBaseUrl?: string # Optional: Console base URL used to build the tdpmConsoleUrl in the v3/v4 in-tmux-by-human files (for example https://console.example.com). When unset, the v3 and v4 files are skipped
|
|
262
262
|
inTmuxConsoleToken?: string # Optional: Token embedded in the ?k= query string of the v4 in-tmux-by-human files. When unset, the v4 per-project file and index.v4.json are skipped
|
|
263
263
|
inTmuxProjectOrder?: string[] # Optional: Ordered list of project codes used to build the in-tmux-by-human index files. When unset or empty, the index files are skipped
|
|
264
|
+
inTmuxLauncherCommand?: string # Optional: Launcher command that starts an interactive session for an `In Tmux by human` issue. Each schedule cycle, any open assigned `In Tmux by human` issue without a live session is restarted by running this command with the issue URL as its argument inside a new detached tmux session named after the issue URL. When unset, session restarting is skipped
|
|
264
265
|
changeTargetPathAliases?: # Optional: Map of short alias keys to full repository-root-relative directory paths. Allows `change-target:<alias>` labels to reference deeply nested paths that exceed GitHub's 50-character label limit. When a `change-target:` label's value matches a key in this map, it is expanded to the corresponding full path before confinement checking. Values with leading or trailing slashes are normalized automatically. Example below
|
|
265
266
|
adapter-interfaces: src/domain/usecases/adapter-interfaces
|
|
266
267
|
```
|
|
@@ -494,6 +495,14 @@ The index files list every project in `inTmuxProjectOrder` whose `{name}.json` a
|
|
|
494
495
|
- `groups`: Story groups. The v3 `groups` carry a `urls` array using the v2 `{ url, title }` entry shape. The v4 `groups` use tmux terminology: each group carries a `sessions` array and each session is `{ name, description }` where `name` is the GitHub issue URL and `description` is the issue title.
|
|
495
496
|
- Token handling: The v4 per-project file and `index.v4.json` are skipped when `inTmuxConsoleToken` is unset. The token value is never written to source code, tests, or documentation; it is supplied through configuration only.
|
|
496
497
|
|
|
498
|
+
### Session Restart Reconciler
|
|
499
|
+
|
|
500
|
+
When `inTmuxLauncherCommand` is configured, each schedule cycle also reconciles the interactive sessions for `In Tmux by human` issues so that those sessions keep running. The same issues selected for the data files above (status `In Tmux by human`, open, assigned to the project manager) are checked for liveness, and any whose session is missing is restarted.
|
|
501
|
+
|
|
502
|
+
A session is considered live for an issue only when both of the following hold: a tmux session named after the issue URL exists, and a running process advertises the issue URL on its command line. The tmux session name is the issue URL with every non-alphanumeric character replaced by an underscore.
|
|
503
|
+
|
|
504
|
+
For each `In Tmux by human` issue with no live session, the reconciler launches a new detached tmux session named after the issue URL that runs `{inTmuxLauncherCommand} {issueUrl}`. Process and tmux inspection and session launching are performed through injectable ports, so the reconciliation logic is unit-testable without touching the host. When `inTmuxLauncherCommand` is unset the reconciler is skipped, and any error during reconciliation is logged and swallowed so the schedule cycle is never affected.
|
|
505
|
+
|
|
497
506
|
## Token Rotation Order File
|
|
498
507
|
|
|
499
508
|
After each schedule cycle where Claude OAuth token rotation is active, TDPM writes the computed rotation order to:
|
|
@@ -45,6 +45,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
45
45
|
const situationFileWriter_1 = require("./situationFileWriter");
|
|
46
46
|
const consoleListsWriter_1 = require("./consoleListsWriter");
|
|
47
47
|
const inTmuxByHumanDataWriter_1 = require("./inTmuxByHumanDataWriter");
|
|
48
|
+
const inTmuxByHumanSessionReconciler_1 = require("./inTmuxByHumanSessionReconciler");
|
|
48
49
|
const rotationOrderFileWriter_1 = require("./rotationOrderFileWriter");
|
|
49
50
|
const projectConfig_1 = require("../cli/projectConfig");
|
|
50
51
|
const SystemDateRepository_1 = require("../../repositories/SystemDateRepository");
|
|
@@ -88,13 +89,13 @@ class HandleScheduledEventUseCaseHandler {
|
|
|
88
89
|
this.handle = async (configFilePath, _verbose) => {
|
|
89
90
|
const configFileContent = fs_1.default.readFileSync(configFilePath, 'utf8');
|
|
90
91
|
const input = yaml_1.default.parse(configFileContent);
|
|
91
|
-
if (!(() => { const _io0 = input => "string" === typeof input.projectName && "string" === typeof input.org && "string" === typeof input.projectUrl && "string" === typeof input.manager && ("object" === typeof input.workingReport && null !== input.workingReport && _io1(input.workingReport)) && "string" === typeof input.urlOfStoryView && "boolean" === typeof input.disabled && "number" === typeof input.allowIssueCacheMinutes && (null === input.labelsAsLlmAgentName || undefined === input.labelsAsLlmAgentName || Array.isArray(input.labelsAsLlmAgentName) && input.labelsAsLlmAgentName.every(elem => "string" === typeof elem)) && (null === input.changeTargetPathAliases || undefined === input.changeTargetPathAliases || "object" === typeof input.changeTargetPathAliases && null !== input.changeTargetPathAliases && false === Array.isArray(input.changeTargetPathAliases) && _io2(input.changeTargetPathAliases)) && (null === input.startPreparation || undefined === input.startPreparation || "object" === typeof input.startPreparation && null !== input.startPreparation && _io3(input.startPreparation)) && (undefined === input.thresholdForAutoReject || "number" === typeof input.thresholdForAutoReject) && (null === input.dailySecurityScan || undefined === input.dailySecurityScan || "object" === typeof input.dailySecurityScan && null !== input.dailySecurityScan && _io4(input.dailySecurityScan)) && (undefined === input.claudeCodeOauthTokenListJsonPath || "string" === typeof input.claudeCodeOauthTokenListJsonPath) && (undefined === input.consoleDataOutputDir || "string" === typeof input.consoleDataOutputDir) && (undefined === input.inTmuxDataOutputDir || "string" === typeof input.inTmuxDataOutputDir) && (undefined === input.inTmuxConsoleBaseUrl || "string" === typeof input.inTmuxConsoleBaseUrl) && (undefined === input.inTmuxConsoleToken || "string" === typeof input.inTmuxConsoleToken) && (undefined === input.inTmuxProjectOrder || Array.isArray(input.inTmuxProjectOrder) && input.inTmuxProjectOrder.every(elem => "string" === typeof elem)) && ("object" === typeof input.credentials && null !== input.credentials && _io5(input.credentials)); const _io1 = input => "string" === typeof input.repo && (Array.isArray(input.members) && input.members.every(elem => "string" === typeof elem)) && "string" === typeof input.spreadsheetUrl; const _io2 = input => Object.keys(input).every(key => {
|
|
92
|
+
if (!(() => { const _io0 = input => "string" === typeof input.projectName && "string" === typeof input.org && "string" === typeof input.projectUrl && "string" === typeof input.manager && ("object" === typeof input.workingReport && null !== input.workingReport && _io1(input.workingReport)) && "string" === typeof input.urlOfStoryView && "boolean" === typeof input.disabled && "number" === typeof input.allowIssueCacheMinutes && (null === input.labelsAsLlmAgentName || undefined === input.labelsAsLlmAgentName || Array.isArray(input.labelsAsLlmAgentName) && input.labelsAsLlmAgentName.every(elem => "string" === typeof elem)) && (null === input.changeTargetPathAliases || undefined === input.changeTargetPathAliases || "object" === typeof input.changeTargetPathAliases && null !== input.changeTargetPathAliases && false === Array.isArray(input.changeTargetPathAliases) && _io2(input.changeTargetPathAliases)) && (null === input.startPreparation || undefined === input.startPreparation || "object" === typeof input.startPreparation && null !== input.startPreparation && _io3(input.startPreparation)) && (undefined === input.thresholdForAutoReject || "number" === typeof input.thresholdForAutoReject) && (null === input.dailySecurityScan || undefined === input.dailySecurityScan || "object" === typeof input.dailySecurityScan && null !== input.dailySecurityScan && _io4(input.dailySecurityScan)) && (undefined === input.claudeCodeOauthTokenListJsonPath || "string" === typeof input.claudeCodeOauthTokenListJsonPath) && (undefined === input.consoleDataOutputDir || "string" === typeof input.consoleDataOutputDir) && (undefined === input.inTmuxDataOutputDir || "string" === typeof input.inTmuxDataOutputDir) && (undefined === input.inTmuxConsoleBaseUrl || "string" === typeof input.inTmuxConsoleBaseUrl) && (undefined === input.inTmuxConsoleToken || "string" === typeof input.inTmuxConsoleToken) && (undefined === input.inTmuxProjectOrder || Array.isArray(input.inTmuxProjectOrder) && input.inTmuxProjectOrder.every(elem => "string" === typeof elem)) && (undefined === input.inTmuxLauncherCommand || "string" === typeof input.inTmuxLauncherCommand) && ("object" === typeof input.credentials && null !== input.credentials && _io5(input.credentials)); const _io1 = input => "string" === typeof input.repo && (Array.isArray(input.members) && input.members.every(elem => "string" === typeof elem)) && "string" === typeof input.spreadsheetUrl; const _io2 = input => Object.keys(input).every(key => {
|
|
92
93
|
const value = input[key];
|
|
93
94
|
if (undefined === value)
|
|
94
95
|
return true;
|
|
95
96
|
return "string" === typeof value;
|
|
96
97
|
}); const _io3 = input => "string" === typeof input.defaultAgentName && (null === input.defaultLlmModelName || undefined === input.defaultLlmModelName || "string" === typeof input.defaultLlmModelName) && (null === input.fallbackLlmModelName || undefined === input.fallbackLlmModelName || "string" === typeof input.fallbackLlmModelName) && (null === input.defaultLlmAgentName || undefined === input.defaultLlmAgentName || "string" === typeof input.defaultLlmAgentName) && "string" === typeof input.configFilePath && (null === input.maximumPreparingIssuesCount || "number" === typeof input.maximumPreparingIssuesCount) && (undefined === input.utilizationPercentageThreshold || "number" === typeof input.utilizationPercentageThreshold) && (null === input.allowedIssueAuthors || undefined === input.allowedIssueAuthors || Array.isArray(input.allowedIssueAuthors) && input.allowedIssueAuthors.every(elem => "string" === typeof elem)) && (undefined === input.preparationProcessCheckCommand || "string" === typeof input.preparationProcessCheckCommand) && (null === input.codexHomeCandidates || undefined === input.codexHomeCandidates || Array.isArray(input.codexHomeCandidates) && input.codexHomeCandidates.every(elem => "string" === typeof elem)) && (undefined === input.awLogDirectoryPath || "string" === typeof input.awLogDirectoryPath) && (undefined === input.awLogStaleThresholdMinutes || "number" === typeof input.awLogStaleThresholdMinutes) && (null === input.awaitingQualityCheckStatus || undefined === input.awaitingQualityCheckStatus || "string" === typeof input.awaitingQualityCheckStatus) && (null === input.labelsAsLlmAgentName || undefined === input.labelsAsLlmAgentName || Array.isArray(input.labelsAsLlmAgentName) && input.labelsAsLlmAgentName.every(elem => "string" === typeof elem)); const _io4 = input => "string" === typeof input.scanBaseDirectory && "number" === typeof input.targetHourUtc && (undefined === input.enableKevNvdReport || "boolean" === typeof input.enableKevNvdReport) && (undefined === input.kevReportRepo || "string" === typeof input.kevReportRepo); const _io5 = input => "object" === typeof input.manager && null !== input.manager && _io6(input.manager) && ("object" === typeof input.bot && null !== input.bot && _io10(input.bot)); const _io6 = input => "object" === typeof input.github && null !== input.github && _io7(input.github) && ("object" === typeof input.slack && null !== input.slack && _io8(input.slack)) && ("object" === typeof input.googleServiceAccount && null !== input.googleServiceAccount && _io9(input.googleServiceAccount)); const _io7 = input => "string" === typeof input.token; const _io8 = input => "string" === typeof input.userToken; const _io9 = input => "string" === typeof input.serviceAccountKey; const _io10 = input => "object" === typeof input.github && null !== input.github && _io11(input.github); const _io11 = input => "string" === typeof input.token; return input => "object" === typeof input && null !== input && _io0(input); })()(input)) {
|
|
97
|
-
throw new Error(`Invalid input: ${JSON.stringify(input)}\n\n${JSON.stringify((() => { const _io0 = input => "string" === typeof input.projectName && "string" === typeof input.org && "string" === typeof input.projectUrl && "string" === typeof input.manager && ("object" === typeof input.workingReport && null !== input.workingReport && _io1(input.workingReport)) && "string" === typeof input.urlOfStoryView && "boolean" === typeof input.disabled && "number" === typeof input.allowIssueCacheMinutes && (null === input.labelsAsLlmAgentName || undefined === input.labelsAsLlmAgentName || Array.isArray(input.labelsAsLlmAgentName) && input.labelsAsLlmAgentName.every(elem => "string" === typeof elem)) && (null === input.changeTargetPathAliases || undefined === input.changeTargetPathAliases || "object" === typeof input.changeTargetPathAliases && null !== input.changeTargetPathAliases && false === Array.isArray(input.changeTargetPathAliases) && _io2(input.changeTargetPathAliases)) && (null === input.startPreparation || undefined === input.startPreparation || "object" === typeof input.startPreparation && null !== input.startPreparation && _io3(input.startPreparation)) && (undefined === input.thresholdForAutoReject || "number" === typeof input.thresholdForAutoReject) && (null === input.dailySecurityScan || undefined === input.dailySecurityScan || "object" === typeof input.dailySecurityScan && null !== input.dailySecurityScan && _io4(input.dailySecurityScan)) && (undefined === input.claudeCodeOauthTokenListJsonPath || "string" === typeof input.claudeCodeOauthTokenListJsonPath) && (undefined === input.consoleDataOutputDir || "string" === typeof input.consoleDataOutputDir) && (undefined === input.inTmuxDataOutputDir || "string" === typeof input.inTmuxDataOutputDir) && (undefined === input.inTmuxConsoleBaseUrl || "string" === typeof input.inTmuxConsoleBaseUrl) && (undefined === input.inTmuxConsoleToken || "string" === typeof input.inTmuxConsoleToken) && (undefined === input.inTmuxProjectOrder || Array.isArray(input.inTmuxProjectOrder) && input.inTmuxProjectOrder.every(elem => "string" === typeof elem)) && ("object" === typeof input.credentials && null !== input.credentials && _io5(input.credentials)); const _io1 = input => "string" === typeof input.repo && (Array.isArray(input.members) && input.members.every(elem => "string" === typeof elem)) && "string" === typeof input.spreadsheetUrl; const _io2 = input => Object.keys(input).every(key => {
|
|
98
|
+
throw new Error(`Invalid input: ${JSON.stringify(input)}\n\n${JSON.stringify((() => { const _io0 = input => "string" === typeof input.projectName && "string" === typeof input.org && "string" === typeof input.projectUrl && "string" === typeof input.manager && ("object" === typeof input.workingReport && null !== input.workingReport && _io1(input.workingReport)) && "string" === typeof input.urlOfStoryView && "boolean" === typeof input.disabled && "number" === typeof input.allowIssueCacheMinutes && (null === input.labelsAsLlmAgentName || undefined === input.labelsAsLlmAgentName || Array.isArray(input.labelsAsLlmAgentName) && input.labelsAsLlmAgentName.every(elem => "string" === typeof elem)) && (null === input.changeTargetPathAliases || undefined === input.changeTargetPathAliases || "object" === typeof input.changeTargetPathAliases && null !== input.changeTargetPathAliases && false === Array.isArray(input.changeTargetPathAliases) && _io2(input.changeTargetPathAliases)) && (null === input.startPreparation || undefined === input.startPreparation || "object" === typeof input.startPreparation && null !== input.startPreparation && _io3(input.startPreparation)) && (undefined === input.thresholdForAutoReject || "number" === typeof input.thresholdForAutoReject) && (null === input.dailySecurityScan || undefined === input.dailySecurityScan || "object" === typeof input.dailySecurityScan && null !== input.dailySecurityScan && _io4(input.dailySecurityScan)) && (undefined === input.claudeCodeOauthTokenListJsonPath || "string" === typeof input.claudeCodeOauthTokenListJsonPath) && (undefined === input.consoleDataOutputDir || "string" === typeof input.consoleDataOutputDir) && (undefined === input.inTmuxDataOutputDir || "string" === typeof input.inTmuxDataOutputDir) && (undefined === input.inTmuxConsoleBaseUrl || "string" === typeof input.inTmuxConsoleBaseUrl) && (undefined === input.inTmuxConsoleToken || "string" === typeof input.inTmuxConsoleToken) && (undefined === input.inTmuxProjectOrder || Array.isArray(input.inTmuxProjectOrder) && input.inTmuxProjectOrder.every(elem => "string" === typeof elem)) && (undefined === input.inTmuxLauncherCommand || "string" === typeof input.inTmuxLauncherCommand) && ("object" === typeof input.credentials && null !== input.credentials && _io5(input.credentials)); const _io1 = input => "string" === typeof input.repo && (Array.isArray(input.members) && input.members.every(elem => "string" === typeof elem)) && "string" === typeof input.spreadsheetUrl; const _io2 = input => Object.keys(input).every(key => {
|
|
98
99
|
const value = input[key];
|
|
99
100
|
if (undefined === value)
|
|
100
101
|
return true;
|
|
@@ -207,6 +208,10 @@ class HandleScheduledEventUseCaseHandler {
|
|
|
207
208
|
path: _path + ".inTmuxProjectOrder",
|
|
208
209
|
expected: "(Array<string> | undefined)",
|
|
209
210
|
value: input.inTmuxProjectOrder
|
|
211
|
+
}), undefined === input.inTmuxLauncherCommand || "string" === typeof input.inTmuxLauncherCommand || _report(_exceptionable, {
|
|
212
|
+
path: _path + ".inTmuxLauncherCommand",
|
|
213
|
+
expected: "(string | undefined)",
|
|
214
|
+
value: input.inTmuxLauncherCommand
|
|
210
215
|
}), ("object" === typeof input.credentials && null !== input.credentials || _report(_exceptionable, {
|
|
211
216
|
path: _path + ".credentials",
|
|
212
217
|
expected: "__type.o2",
|
|
@@ -581,6 +586,17 @@ class HandleScheduledEventUseCaseHandler {
|
|
|
581
586
|
catch (error) {
|
|
582
587
|
console.error(`Failed to write in-tmux-by-human data: ${error instanceof Error ? error.message : String(error)}`);
|
|
583
588
|
}
|
|
589
|
+
try {
|
|
590
|
+
await (0, inTmuxByHumanSessionReconciler_1.reconcileInTmuxByHumanSessions)({
|
|
591
|
+
inTmuxLauncherCommand: mergedInput.inTmuxLauncherCommand ?? null,
|
|
592
|
+
assigneeLogin: input.manager,
|
|
593
|
+
issues: result.issues,
|
|
594
|
+
localCommandRunner: nodeLocalCommandRunner,
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
catch (error) {
|
|
598
|
+
console.error(`Failed to reconcile in-tmux-by-human sessions: ${error instanceof Error ? error.message : String(error)}`);
|
|
599
|
+
}
|
|
584
600
|
}
|
|
585
601
|
return result;
|
|
586
602
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HandleScheduledEventUseCaseHandler.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,gDAAwB;AACxB,kDAA0B;AAC1B,4CAAoB;AACpB,+DAA2D;AAC3D,6DAAyD;AACzD,uEAAmE;AACnE,uEAAmE;AACnE,wDAG8B;AAC9B,kFAA+E;AAC/E,sFAAmF;AACnF,gGAA6F;AAC7F,0FAAuF;AACvF,wFAAqF;AACrF,sFAAmF;AACnF,wGAAqG;AACrG,8GAA2G;AAC3G,sGAAmG;AACnG,gGAA6F;AAC7F,kGAA+F;AAC/F,gIAA6H;AAC7H,oHAAiH;AACjH,wGAAqG;AAIrG,0FAAuF;AACvF,wGAAqG;AACrG,gIAA6H;AAC7H,wGAAqG;AACrG,kIAA+H;AAC/H,8GAA2G;AAC3G,0GAAuG;AACvG,wGAAqG;AACrG,0HAAuH;AACvH,8GAA2G;AAC3G,8FAA2F;AAC3F,sFAAmF;AACnF,wGAAqG;AACrG,oGAAiG;AACjG,sGAAmG;AACnG,gHAA6G;AAC7G,0HAAuH;AACvH,kGAA+F;AAC/F,8GAA2G;AAC3G,gGAA6F;AAC7F,0EAAuE;AACvE,4EAKiD;AAEjD,MAAa,kCAAkC;IAA/C;QACE,WAAM,GAAG,KAAK,EACZ,cAAsB,EACtB,QAAiB,EAMT,EAAE;YACV,MAAM,iBAAiB,GAAG,YAAE,CAAC,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;YAClE,MAAM,KAAK,GAAY,cAAI,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"HandleScheduledEventUseCaseHandler.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reconcileInTmuxByHumanSessions = void 0;
|
|
4
|
+
const InTmuxByHumanSessionReconcileUseCase_1 = require("../../../domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase");
|
|
5
|
+
const NodeTmuxSessionRepository_1 = require("../../repositories/NodeTmuxSessionRepository");
|
|
6
|
+
const reconcileInTmuxByHumanSessions = async (params) => {
|
|
7
|
+
const { inTmuxLauncherCommand, assigneeLogin, issues, localCommandRunner } = params;
|
|
8
|
+
if (!inTmuxLauncherCommand || !assigneeLogin) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const useCase = new InTmuxByHumanSessionReconcileUseCase_1.InTmuxByHumanSessionReconcileUseCase(new NodeTmuxSessionRepository_1.NodeTmuxSessionRepository(localCommandRunner));
|
|
12
|
+
await useCase.run({
|
|
13
|
+
issues,
|
|
14
|
+
assigneeLogin,
|
|
15
|
+
launcherCommand: inTmuxLauncherCommand,
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
exports.reconcileInTmuxByHumanSessions = reconcileInTmuxByHumanSessions;
|
|
19
|
+
//# sourceMappingURL=inTmuxByHumanSessionReconciler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inTmuxByHumanSessionReconciler.js","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.ts"],"names":[],"mappings":";;;AAEA,+HAA4H;AAC5H,4FAAyF;AASlF,MAAM,8BAA8B,GAAG,KAAK,EACjD,MAA4C,EAC7B,EAAE;IACjB,MAAM,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,EAAE,kBAAkB,EAAE,GACxE,MAAM,CAAC;IACT,IAAI,CAAC,qBAAqB,IAAI,CAAC,aAAa,EAAE,CAAC;QAC7C,OAAO;IACT,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,2EAAoC,CACtD,IAAI,qDAAyB,CAAC,kBAAkB,CAAC,CAClD,CAAC;IACF,MAAM,OAAO,CAAC,GAAG,CAAC;QAChB,MAAM;QACN,aAAa;QACb,eAAe,EAAE,qBAAqB;KACvC,CAAC,CAAC;AACL,CAAC,CAAC;AAhBW,QAAA,8BAA8B,kCAgBzC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NodeTmuxSessionRepository = void 0;
|
|
4
|
+
class NodeTmuxSessionRepository {
|
|
5
|
+
constructor(localCommandRunner) {
|
|
6
|
+
this.localCommandRunner = localCommandRunner;
|
|
7
|
+
this.listLiveSessionNames = async () => {
|
|
8
|
+
const { stdout, exitCode } = await this.localCommandRunner.runCommand('tmux', ['list-sessions', '-F', '#{session_name}']);
|
|
9
|
+
if (exitCode !== 0) {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
return stdout
|
|
13
|
+
.split('\n')
|
|
14
|
+
.map((line) => line.trim())
|
|
15
|
+
.filter((line) => line.length > 0);
|
|
16
|
+
};
|
|
17
|
+
this.listInteractiveProcessCommandLines = async () => {
|
|
18
|
+
const { stdout, exitCode } = await this.localCommandRunner.runCommand('ps', ['-eo', 'args=']);
|
|
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
|
+
};
|
|
27
|
+
this.launchDetachedSession = async (sessionName, launcherCommand, issueUrl) => {
|
|
28
|
+
await this.localCommandRunner.runCommand('tmux', [
|
|
29
|
+
'new-session',
|
|
30
|
+
'-d',
|
|
31
|
+
'-s',
|
|
32
|
+
sessionName,
|
|
33
|
+
'sh',
|
|
34
|
+
'-lc',
|
|
35
|
+
`exec "$1" "$2"`,
|
|
36
|
+
'sh',
|
|
37
|
+
launcherCommand,
|
|
38
|
+
issueUrl,
|
|
39
|
+
]);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.NodeTmuxSessionRepository = NodeTmuxSessionRepository;
|
|
44
|
+
//# sourceMappingURL=NodeTmuxSessionRepository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NodeTmuxSessionRepository.js","sourceRoot":"","sources":["../../../src/adapter/repositories/NodeTmuxSessionRepository.ts"],"names":[],"mappings":";;;AAGA,MAAa,yBAAyB;IACpC,YAA6B,kBAAsC;QAAtC,uBAAkB,GAAlB,kBAAkB,CAAoB;QAEnE,yBAAoB,GAAG,KAAK,IAAuB,EAAE;YACnD,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,UAAU,CACnE,MAAM,EACN,CAAC,eAAe,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAC3C,CAAC;YACF,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;gBACnB,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,OAAO,MAAM;iBACV,KAAK,CAAC,IAAI,CAAC;iBACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;iBAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC;QAEF,uCAAkC,GAAG,KAAK,IAAuB,EAAE;YACjE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,UAAU,CACnE,IAAI,EACJ,CAAC,KAAK,EAAE,OAAO,CAAC,CACjB,CAAC;YACF,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;gBACnB,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,OAAO,MAAM;iBACV,KAAK,CAAC,IAAI,CAAC;iBACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;iBAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QACvC,CAAC,CAAC;QAEF,0BAAqB,GAAG,KAAK,EAC3B,WAAmB,EACnB,eAAuB,EACvB,QAAgB,EACD,EAAE;YACjB,MAAM,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,MAAM,EAAE;gBAC/C,aAAa;gBACb,IAAI;gBACJ,IAAI;gBACJ,WAAW;gBACX,IAAI;gBACJ,KAAK;gBACL,gBAAgB;gBAChB,IAAI;gBACJ,eAAe;gBACf,QAAQ;aACT,CAAC,CAAC;QACL,CAAC,CAAC;IA/CoE,CAAC;CAgDxE;AAjDD,8DAiDC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TmuxSessionRepository.js","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/TmuxSessionRepository.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InTmuxByHumanSessionReconcileUseCase = exports.toTmuxSessionName = void 0;
|
|
4
|
+
const WorkflowStatus_1 = require("../../entities/WorkflowStatus");
|
|
5
|
+
const toTmuxSessionName = (issueUrl) => issueUrl.replace(/[^a-zA-Z0-9]/g, '_');
|
|
6
|
+
exports.toTmuxSessionName = toTmuxSessionName;
|
|
7
|
+
class InTmuxByHumanSessionReconcileUseCase {
|
|
8
|
+
constructor(tmuxSessionRepository) {
|
|
9
|
+
this.tmuxSessionRepository = tmuxSessionRepository;
|
|
10
|
+
this.run = async (input) => {
|
|
11
|
+
const { issues, assigneeLogin, launcherCommand } = input;
|
|
12
|
+
const targetIssues = issues.filter((issue) => this.isInTmuxByHuman(issue, assigneeLogin));
|
|
13
|
+
if (targetIssues.length === 0) {
|
|
14
|
+
return { launchedIssueUrls: [] };
|
|
15
|
+
}
|
|
16
|
+
const liveSessionNames = new Set(await this.tmuxSessionRepository.listLiveSessionNames());
|
|
17
|
+
const processCommandLines = await this.tmuxSessionRepository.listInteractiveProcessCommandLines();
|
|
18
|
+
const launchedIssueUrls = [];
|
|
19
|
+
for (const issue of targetIssues) {
|
|
20
|
+
if (this.hasLiveSession(issue.url, liveSessionNames, processCommandLines)) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
await this.tmuxSessionRepository.launchDetachedSession((0, exports.toTmuxSessionName)(issue.url), launcherCommand, issue.url);
|
|
24
|
+
launchedIssueUrls.push(issue.url);
|
|
25
|
+
}
|
|
26
|
+
return { launchedIssueUrls };
|
|
27
|
+
};
|
|
28
|
+
this.isInTmuxByHuman = (issue, assigneeLogin) => issue.status === WorkflowStatus_1.IN_TMUX_STATUS_NAME &&
|
|
29
|
+
issue.isClosed === false &&
|
|
30
|
+
issue.assignees.includes(assigneeLogin);
|
|
31
|
+
this.hasLiveSession = (issueUrl, liveSessionNames, processCommandLines) => {
|
|
32
|
+
const sessionName = (0, exports.toTmuxSessionName)(issueUrl);
|
|
33
|
+
if (!liveSessionNames.has(sessionName)) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
return processCommandLines.some((commandLine) => commandLine.includes(issueUrl));
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.InTmuxByHumanSessionReconcileUseCase = InTmuxByHumanSessionReconcileUseCase;
|
|
41
|
+
//# sourceMappingURL=InTmuxByHumanSessionReconcileUseCase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InTmuxByHumanSessionReconcileUseCase.js","sourceRoot":"","sources":["../../../../src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.ts"],"names":[],"mappings":";;;AACA,kEAAoE;AAa7D,MAAM,iBAAiB,GAAG,CAAC,QAAgB,EAAU,EAAE,CAC5D,QAAQ,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;AAD5B,QAAA,iBAAiB,qBACW;AAEzC,MAAa,oCAAoC;IAC/C,YAA6B,qBAA4C;QAA5C,0BAAqB,GAArB,qBAAqB,CAAuB;QAEzE,QAAG,GAAG,KAAK,EACT,KAAyC,EACK,EAAE;YAChD,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,eAAe,EAAE,GAAG,KAAK,CAAC;YAEzD,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAC3C,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,aAAa,CAAC,CAC3C,CAAC;YACF,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC9B,OAAO,EAAE,iBAAiB,EAAE,EAAE,EAAE,CAAC;YACnC,CAAC;YAED,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAC9B,MAAM,IAAI,CAAC,qBAAqB,CAAC,oBAAoB,EAAE,CACxD,CAAC;YACF,MAAM,mBAAmB,GACvB,MAAM,IAAI,CAAC,qBAAqB,CAAC,kCAAkC,EAAE,CAAC;YAExE,MAAM,iBAAiB,GAAa,EAAE,CAAC;YACvC,KAAK,MAAM,KAAK,IAAI,YAAY,EAAE,CAAC;gBACjC,IACE,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,EAAE,gBAAgB,EAAE,mBAAmB,CAAC,EACrE,CAAC;oBACD,SAAS;gBACX,CAAC;gBACD,MAAM,IAAI,CAAC,qBAAqB,CAAC,qBAAqB,CACpD,IAAA,yBAAiB,EAAC,KAAK,CAAC,GAAG,CAAC,EAC5B,eAAe,EACf,KAAK,CAAC,GAAG,CACV,CAAC;gBACF,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YACpC,CAAC;YAED,OAAO,EAAE,iBAAiB,EAAE,CAAC;QAC/B,CAAC,CAAC;QAEM,oBAAe,GAAG,CAAC,KAAY,EAAE,aAAqB,EAAW,EAAE,CACzE,KAAK,CAAC,MAAM,KAAK,oCAAmB;YACpC,KAAK,CAAC,QAAQ,KAAK,KAAK;YACxB,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;QAElC,mBAAc,GAAG,CACvB,QAAgB,EAChB,gBAA6B,EAC7B,mBAA6B,EACpB,EAAE;YACX,MAAM,WAAW,GAAG,IAAA,yBAAiB,EAAC,QAAQ,CAAC,CAAC;YAChD,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;gBACvC,OAAO,KAAK,CAAC;YACf,CAAC;YACD,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,EAAE,CAC9C,WAAW,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAC/B,CAAC;QACJ,CAAC,CAAC;IAvD0E,CAAC;CAwD9E;AAzDD,oFAyDC"}
|
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@ import fs from 'fs';
|
|
|
4
4
|
import { writeSituationFile } from './situationFileWriter';
|
|
5
5
|
import { writeConsoleLists } from './consoleListsWriter';
|
|
6
6
|
import { writeInTmuxByHumanData } from './inTmuxByHumanDataWriter';
|
|
7
|
+
import { reconcileInTmuxByHumanSessions } from './inTmuxByHumanSessionReconciler';
|
|
7
8
|
import { writeRotationOrderFile } from './rotationOrderFileWriter';
|
|
8
9
|
import {
|
|
9
10
|
fetchProjectReadme,
|
|
@@ -73,6 +74,7 @@ export class HandleScheduledEventUseCaseHandler {
|
|
|
73
74
|
inTmuxConsoleBaseUrl?: string;
|
|
74
75
|
inTmuxConsoleToken?: string;
|
|
75
76
|
inTmuxProjectOrder?: string[];
|
|
77
|
+
inTmuxLauncherCommand?: string;
|
|
76
78
|
credentials: {
|
|
77
79
|
manager: {
|
|
78
80
|
github: {
|
|
@@ -409,6 +411,21 @@ export class HandleScheduledEventUseCaseHandler {
|
|
|
409
411
|
}`,
|
|
410
412
|
);
|
|
411
413
|
}
|
|
414
|
+
|
|
415
|
+
try {
|
|
416
|
+
await reconcileInTmuxByHumanSessions({
|
|
417
|
+
inTmuxLauncherCommand: mergedInput.inTmuxLauncherCommand ?? null,
|
|
418
|
+
assigneeLogin: input.manager,
|
|
419
|
+
issues: result.issues,
|
|
420
|
+
localCommandRunner: nodeLocalCommandRunner,
|
|
421
|
+
});
|
|
422
|
+
} catch (error) {
|
|
423
|
+
console.error(
|
|
424
|
+
`Failed to reconcile in-tmux-by-human sessions: ${
|
|
425
|
+
error instanceof Error ? error.message : String(error)
|
|
426
|
+
}`,
|
|
427
|
+
);
|
|
428
|
+
}
|
|
412
429
|
}
|
|
413
430
|
return result;
|
|
414
431
|
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { Issue } from '../../../domain/entities/Issue';
|
|
2
|
+
import { LocalCommandRunner } from '../../../domain/usecases/adapter-interfaces/LocalCommandRunner';
|
|
3
|
+
import { reconcileInTmuxByHumanSessions } from './inTmuxByHumanSessionReconciler';
|
|
4
|
+
|
|
5
|
+
const ASSIGNEE = 'owner-login';
|
|
6
|
+
|
|
7
|
+
const makeIssue = (overrides: Partial<Issue> = {}): Issue => ({
|
|
8
|
+
nameWithOwner: 'demo/repo',
|
|
9
|
+
number: 1,
|
|
10
|
+
title: 'Issue 1',
|
|
11
|
+
state: 'OPEN',
|
|
12
|
+
status: 'In Tmux by human',
|
|
13
|
+
story: null,
|
|
14
|
+
nextActionDate: null,
|
|
15
|
+
nextActionHour: null,
|
|
16
|
+
estimationMinutes: null,
|
|
17
|
+
dependedIssueUrls: [],
|
|
18
|
+
completionDate50PercentConfidence: null,
|
|
19
|
+
url: 'https://github.com/demo/repo/issues/1',
|
|
20
|
+
assignees: [ASSIGNEE],
|
|
21
|
+
labels: [],
|
|
22
|
+
org: 'demo',
|
|
23
|
+
repo: 'repo',
|
|
24
|
+
body: '',
|
|
25
|
+
itemId: 'item-1',
|
|
26
|
+
isPr: false,
|
|
27
|
+
isInProgress: false,
|
|
28
|
+
isClosed: false,
|
|
29
|
+
createdAt: new Date(),
|
|
30
|
+
author: '',
|
|
31
|
+
...overrides,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
type Mocked<T> = jest.Mocked<T> & jest.MockedObject<T>;
|
|
35
|
+
|
|
36
|
+
const createMockRunner = (): Mocked<LocalCommandRunner> => ({
|
|
37
|
+
runCommand: jest.fn(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('reconcileInTmuxByHumanSessions', () => {
|
|
41
|
+
it('does nothing when no launcher command is configured', async () => {
|
|
42
|
+
const runner = createMockRunner();
|
|
43
|
+
|
|
44
|
+
await reconcileInTmuxByHumanSessions({
|
|
45
|
+
inTmuxLauncherCommand: null,
|
|
46
|
+
assigneeLogin: ASSIGNEE,
|
|
47
|
+
issues: [makeIssue()],
|
|
48
|
+
localCommandRunner: runner,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
expect(runner.runCommand.mock.calls).toHaveLength(0);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('launches a detached tmux session when an In Tmux by human session is missing', async () => {
|
|
55
|
+
const runner = createMockRunner();
|
|
56
|
+
runner.runCommand.mockImplementation(async (program, args) => {
|
|
57
|
+
if (program === 'tmux' && args[0] === 'list-sessions') {
|
|
58
|
+
return { stdout: '', stderr: '', exitCode: 0 };
|
|
59
|
+
}
|
|
60
|
+
if (program === 'ps') {
|
|
61
|
+
return { stdout: '', stderr: '', exitCode: 0 };
|
|
62
|
+
}
|
|
63
|
+
return { stdout: '', stderr: '', exitCode: 0 };
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
await reconcileInTmuxByHumanSessions({
|
|
67
|
+
inTmuxLauncherCommand: 'cl',
|
|
68
|
+
assigneeLogin: ASSIGNEE,
|
|
69
|
+
issues: [makeIssue()],
|
|
70
|
+
localCommandRunner: runner,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const newSessionCall = runner.runCommand.mock.calls.find(
|
|
74
|
+
(call) => call[0] === 'tmux' && call[1][0] === 'new-session',
|
|
75
|
+
);
|
|
76
|
+
expect(newSessionCall).toBeDefined();
|
|
77
|
+
expect(newSessionCall?.[1]).toEqual([
|
|
78
|
+
'new-session',
|
|
79
|
+
'-d',
|
|
80
|
+
'-s',
|
|
81
|
+
'https___github_com_demo_repo_issues_1',
|
|
82
|
+
'sh',
|
|
83
|
+
'-lc',
|
|
84
|
+
'exec "$1" "$2"',
|
|
85
|
+
'sh',
|
|
86
|
+
'cl',
|
|
87
|
+
'https://github.com/demo/repo/issues/1',
|
|
88
|
+
]);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('does not launch when a live session already exists', async () => {
|
|
92
|
+
const runner = createMockRunner();
|
|
93
|
+
runner.runCommand.mockImplementation(async (program, args) => {
|
|
94
|
+
if (program === 'tmux' && args[0] === 'list-sessions') {
|
|
95
|
+
return {
|
|
96
|
+
stdout: 'https___github_com_demo_repo_issues_1\n',
|
|
97
|
+
stderr: '',
|
|
98
|
+
exitCode: 0,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (program === 'ps') {
|
|
102
|
+
return {
|
|
103
|
+
stdout: 'claude --name https://github.com/demo/repo/issues/1\n',
|
|
104
|
+
stderr: '',
|
|
105
|
+
exitCode: 0,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return { stdout: '', stderr: '', exitCode: 0 };
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await reconcileInTmuxByHumanSessions({
|
|
112
|
+
inTmuxLauncherCommand: 'cl',
|
|
113
|
+
assigneeLogin: ASSIGNEE,
|
|
114
|
+
issues: [makeIssue()],
|
|
115
|
+
localCommandRunner: runner,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const newSessionCalls = runner.runCommand.mock.calls.filter(
|
|
119
|
+
(call) => call[0] === 'tmux' && call[1][0] === 'new-session',
|
|
120
|
+
);
|
|
121
|
+
expect(newSessionCalls).toHaveLength(0);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Issue } from '../../../domain/entities/Issue';
|
|
2
|
+
import { LocalCommandRunner } from '../../../domain/usecases/adapter-interfaces/LocalCommandRunner';
|
|
3
|
+
import { InTmuxByHumanSessionReconcileUseCase } from '../../../domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase';
|
|
4
|
+
import { NodeTmuxSessionRepository } from '../../repositories/NodeTmuxSessionRepository';
|
|
5
|
+
|
|
6
|
+
export type ReconcileInTmuxByHumanSessionsParams = {
|
|
7
|
+
inTmuxLauncherCommand: string | null;
|
|
8
|
+
assigneeLogin: string;
|
|
9
|
+
issues: Issue[];
|
|
10
|
+
localCommandRunner: LocalCommandRunner;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const reconcileInTmuxByHumanSessions = async (
|
|
14
|
+
params: ReconcileInTmuxByHumanSessionsParams,
|
|
15
|
+
): Promise<void> => {
|
|
16
|
+
const { inTmuxLauncherCommand, assigneeLogin, issues, localCommandRunner } =
|
|
17
|
+
params;
|
|
18
|
+
if (!inTmuxLauncherCommand || !assigneeLogin) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const useCase = new InTmuxByHumanSessionReconcileUseCase(
|
|
22
|
+
new NodeTmuxSessionRepository(localCommandRunner),
|
|
23
|
+
);
|
|
24
|
+
await useCase.run({
|
|
25
|
+
issues,
|
|
26
|
+
assigneeLogin,
|
|
27
|
+
launcherCommand: inTmuxLauncherCommand,
|
|
28
|
+
});
|
|
29
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { LocalCommandRunner } from '../../domain/usecases/adapter-interfaces/LocalCommandRunner';
|
|
2
|
+
import { NodeTmuxSessionRepository } from './NodeTmuxSessionRepository';
|
|
3
|
+
|
|
4
|
+
type Mocked<T> = jest.Mocked<T> & jest.MockedObject<T>;
|
|
5
|
+
|
|
6
|
+
const createMockRunner = (): Mocked<LocalCommandRunner> => ({
|
|
7
|
+
runCommand: jest.fn(),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
describe('NodeTmuxSessionRepository', () => {
|
|
11
|
+
describe('listLiveSessionNames', () => {
|
|
12
|
+
it('parses tmux session names and drops blank lines', async () => {
|
|
13
|
+
const runner = createMockRunner();
|
|
14
|
+
runner.runCommand.mockResolvedValue({
|
|
15
|
+
stdout: 'session-a\nsession-b\n\n',
|
|
16
|
+
stderr: '',
|
|
17
|
+
exitCode: 0,
|
|
18
|
+
});
|
|
19
|
+
const repository = new NodeTmuxSessionRepository(runner);
|
|
20
|
+
|
|
21
|
+
const result = await repository.listLiveSessionNames();
|
|
22
|
+
|
|
23
|
+
expect(result).toEqual(['session-a', 'session-b']);
|
|
24
|
+
expect(runner.runCommand.mock.calls[0][0]).toBe('tmux');
|
|
25
|
+
expect(runner.runCommand.mock.calls[0][1]).toEqual([
|
|
26
|
+
'list-sessions',
|
|
27
|
+
'-F',
|
|
28
|
+
'#{session_name}',
|
|
29
|
+
]);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('returns an empty list when tmux exits non-zero', async () => {
|
|
33
|
+
const runner = createMockRunner();
|
|
34
|
+
runner.runCommand.mockResolvedValue({
|
|
35
|
+
stdout: '',
|
|
36
|
+
stderr: 'no server running',
|
|
37
|
+
exitCode: 1,
|
|
38
|
+
});
|
|
39
|
+
const repository = new NodeTmuxSessionRepository(runner);
|
|
40
|
+
|
|
41
|
+
const result = await repository.listLiveSessionNames();
|
|
42
|
+
|
|
43
|
+
expect(result).toEqual([]);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('listInteractiveProcessCommandLines', () => {
|
|
48
|
+
it('parses process command lines from ps output', async () => {
|
|
49
|
+
const runner = createMockRunner();
|
|
50
|
+
runner.runCommand.mockResolvedValue({
|
|
51
|
+
stdout:
|
|
52
|
+
'claude --name https://github.com/demo/repo/issues/1\n/usr/bin/tmux\n',
|
|
53
|
+
stderr: '',
|
|
54
|
+
exitCode: 0,
|
|
55
|
+
});
|
|
56
|
+
const repository = new NodeTmuxSessionRepository(runner);
|
|
57
|
+
|
|
58
|
+
const result = await repository.listInteractiveProcessCommandLines();
|
|
59
|
+
|
|
60
|
+
expect(result).toEqual([
|
|
61
|
+
'claude --name https://github.com/demo/repo/issues/1',
|
|
62
|
+
'/usr/bin/tmux',
|
|
63
|
+
]);
|
|
64
|
+
expect(runner.runCommand.mock.calls[0][0]).toBe('ps');
|
|
65
|
+
expect(runner.runCommand.mock.calls[0][1]).toEqual(['-eo', 'args=']);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('launchDetachedSession', () => {
|
|
70
|
+
it('creates a detached tmux session running the launcher command with the issue url', async () => {
|
|
71
|
+
const runner = createMockRunner();
|
|
72
|
+
runner.runCommand.mockResolvedValue({
|
|
73
|
+
stdout: '',
|
|
74
|
+
stderr: '',
|
|
75
|
+
exitCode: 0,
|
|
76
|
+
});
|
|
77
|
+
const repository = new NodeTmuxSessionRepository(runner);
|
|
78
|
+
|
|
79
|
+
await repository.launchDetachedSession(
|
|
80
|
+
'https___github_com_demo_repo_issues_1',
|
|
81
|
+
'cl',
|
|
82
|
+
'https://github.com/demo/repo/issues/1',
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
expect(runner.runCommand.mock.calls[0][0]).toBe('tmux');
|
|
86
|
+
expect(runner.runCommand.mock.calls[0][1]).toEqual([
|
|
87
|
+
'new-session',
|
|
88
|
+
'-d',
|
|
89
|
+
'-s',
|
|
90
|
+
'https___github_com_demo_repo_issues_1',
|
|
91
|
+
'sh',
|
|
92
|
+
'-lc',
|
|
93
|
+
'exec "$1" "$2"',
|
|
94
|
+
'sh',
|
|
95
|
+
'cl',
|
|
96
|
+
'https://github.com/demo/repo/issues/1',
|
|
97
|
+
]);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { LocalCommandRunner } from '../../domain/usecases/adapter-interfaces/LocalCommandRunner';
|
|
2
|
+
import { TmuxSessionRepository } from '../../domain/usecases/adapter-interfaces/TmuxSessionRepository';
|
|
3
|
+
|
|
4
|
+
export class NodeTmuxSessionRepository implements TmuxSessionRepository {
|
|
5
|
+
constructor(private readonly localCommandRunner: LocalCommandRunner) {}
|
|
6
|
+
|
|
7
|
+
listLiveSessionNames = async (): Promise<string[]> => {
|
|
8
|
+
const { stdout, exitCode } = await this.localCommandRunner.runCommand(
|
|
9
|
+
'tmux',
|
|
10
|
+
['list-sessions', '-F', '#{session_name}'],
|
|
11
|
+
);
|
|
12
|
+
if (exitCode !== 0) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
return stdout
|
|
16
|
+
.split('\n')
|
|
17
|
+
.map((line) => line.trim())
|
|
18
|
+
.filter((line) => line.length > 0);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
listInteractiveProcessCommandLines = async (): Promise<string[]> => {
|
|
22
|
+
const { stdout, exitCode } = await this.localCommandRunner.runCommand(
|
|
23
|
+
'ps',
|
|
24
|
+
['-eo', 'args='],
|
|
25
|
+
);
|
|
26
|
+
if (exitCode !== 0) {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
return stdout
|
|
30
|
+
.split('\n')
|
|
31
|
+
.map((line) => line.trim())
|
|
32
|
+
.filter((line) => line.length > 0);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
launchDetachedSession = async (
|
|
36
|
+
sessionName: string,
|
|
37
|
+
launcherCommand: string,
|
|
38
|
+
issueUrl: string,
|
|
39
|
+
): Promise<void> => {
|
|
40
|
+
await this.localCommandRunner.runCommand('tmux', [
|
|
41
|
+
'new-session',
|
|
42
|
+
'-d',
|
|
43
|
+
'-s',
|
|
44
|
+
sessionName,
|
|
45
|
+
'sh',
|
|
46
|
+
'-lc',
|
|
47
|
+
`exec "$1" "$2"`,
|
|
48
|
+
'sh',
|
|
49
|
+
launcherCommand,
|
|
50
|
+
issueUrl,
|
|
51
|
+
]);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface TmuxSessionRepository {
|
|
2
|
+
listLiveSessionNames: () => Promise<string[]>;
|
|
3
|
+
listInteractiveProcessCommandLines: () => Promise<string[]>;
|
|
4
|
+
launchDetachedSession: (
|
|
5
|
+
sessionName: string,
|
|
6
|
+
launcherCommand: string,
|
|
7
|
+
issueUrl: string,
|
|
8
|
+
) => Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { Issue } from '../../entities/Issue';
|
|
2
|
+
import { IN_TMUX_STATUS_NAME } from '../../entities/WorkflowStatus';
|
|
3
|
+
import { TmuxSessionRepository } from '../adapter-interfaces/TmuxSessionRepository';
|
|
4
|
+
import {
|
|
5
|
+
InTmuxByHumanSessionReconcileUseCase,
|
|
6
|
+
toTmuxSessionName,
|
|
7
|
+
} from './InTmuxByHumanSessionReconcileUseCase';
|
|
8
|
+
|
|
9
|
+
const ASSIGNEE = 'owner-login';
|
|
10
|
+
const LAUNCHER = 'cl';
|
|
11
|
+
|
|
12
|
+
let issueCounter = 0;
|
|
13
|
+
const makeIssue = (overrides: Partial<Issue> = {}): Issue => {
|
|
14
|
+
issueCounter += 1;
|
|
15
|
+
return {
|
|
16
|
+
nameWithOwner: 'demo/repo',
|
|
17
|
+
number: issueCounter,
|
|
18
|
+
title: `Issue ${issueCounter}`,
|
|
19
|
+
state: 'OPEN',
|
|
20
|
+
status: IN_TMUX_STATUS_NAME,
|
|
21
|
+
story: null,
|
|
22
|
+
nextActionDate: null,
|
|
23
|
+
nextActionHour: null,
|
|
24
|
+
estimationMinutes: null,
|
|
25
|
+
dependedIssueUrls: [],
|
|
26
|
+
completionDate50PercentConfidence: null,
|
|
27
|
+
url: `https://github.com/demo/repo/issues/${issueCounter}`,
|
|
28
|
+
assignees: [ASSIGNEE],
|
|
29
|
+
labels: [],
|
|
30
|
+
org: 'demo',
|
|
31
|
+
repo: 'repo',
|
|
32
|
+
body: '',
|
|
33
|
+
itemId: `item-${issueCounter}`,
|
|
34
|
+
isPr: false,
|
|
35
|
+
isInProgress: false,
|
|
36
|
+
isClosed: false,
|
|
37
|
+
createdAt: new Date(),
|
|
38
|
+
author: '',
|
|
39
|
+
...overrides,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const createFakeTmuxSessionRepository = (state: {
|
|
44
|
+
liveSessionNames: string[];
|
|
45
|
+
processCommandLines: string[];
|
|
46
|
+
}): TmuxSessionRepository & {
|
|
47
|
+
launches: {
|
|
48
|
+
sessionName: string;
|
|
49
|
+
launcherCommand: string;
|
|
50
|
+
issueUrl: string;
|
|
51
|
+
}[];
|
|
52
|
+
} => {
|
|
53
|
+
const launches: {
|
|
54
|
+
sessionName: string;
|
|
55
|
+
launcherCommand: string;
|
|
56
|
+
issueUrl: string;
|
|
57
|
+
}[] = [];
|
|
58
|
+
return {
|
|
59
|
+
launches,
|
|
60
|
+
listLiveSessionNames: async () => state.liveSessionNames,
|
|
61
|
+
listInteractiveProcessCommandLines: async () => state.processCommandLines,
|
|
62
|
+
launchDetachedSession: async (sessionName, launcherCommand, issueUrl) => {
|
|
63
|
+
launches.push({ sessionName, launcherCommand, issueUrl });
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
describe('InTmuxByHumanSessionReconcileUseCase', () => {
|
|
69
|
+
beforeEach(() => {
|
|
70
|
+
issueCounter = 0;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('launches a detached session for an In Tmux by human issue with no live session', async () => {
|
|
74
|
+
const issue = makeIssue();
|
|
75
|
+
const repository = createFakeTmuxSessionRepository({
|
|
76
|
+
liveSessionNames: [],
|
|
77
|
+
processCommandLines: [],
|
|
78
|
+
});
|
|
79
|
+
const useCase = new InTmuxByHumanSessionReconcileUseCase(repository);
|
|
80
|
+
|
|
81
|
+
const result = await useCase.run({
|
|
82
|
+
issues: [issue],
|
|
83
|
+
assigneeLogin: ASSIGNEE,
|
|
84
|
+
launcherCommand: LAUNCHER,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(repository.launches).toEqual([
|
|
88
|
+
{
|
|
89
|
+
sessionName: toTmuxSessionName(issue.url),
|
|
90
|
+
launcherCommand: LAUNCHER,
|
|
91
|
+
issueUrl: issue.url,
|
|
92
|
+
},
|
|
93
|
+
]);
|
|
94
|
+
expect(result.launchedIssueUrls).toEqual([issue.url]);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('does not launch when a live session already exists for the issue', async () => {
|
|
98
|
+
const issue = makeIssue();
|
|
99
|
+
const repository = createFakeTmuxSessionRepository({
|
|
100
|
+
liveSessionNames: [toTmuxSessionName(issue.url)],
|
|
101
|
+
processCommandLines: [
|
|
102
|
+
`claude --model opus --agent leader --name ${issue.url}`,
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
const useCase = new InTmuxByHumanSessionReconcileUseCase(repository);
|
|
106
|
+
|
|
107
|
+
const result = await useCase.run({
|
|
108
|
+
issues: [issue],
|
|
109
|
+
assigneeLogin: ASSIGNEE,
|
|
110
|
+
launcherCommand: LAUNCHER,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
expect(repository.launches).toEqual([]);
|
|
114
|
+
expect(result.launchedIssueUrls).toEqual([]);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('treats a session as missing when the tmux session exists but no process advertises the issue url', async () => {
|
|
118
|
+
const issue = makeIssue();
|
|
119
|
+
const repository = createFakeTmuxSessionRepository({
|
|
120
|
+
liveSessionNames: [toTmuxSessionName(issue.url)],
|
|
121
|
+
processCommandLines: ['claude --model opus --name some-other-task'],
|
|
122
|
+
});
|
|
123
|
+
const useCase = new InTmuxByHumanSessionReconcileUseCase(repository);
|
|
124
|
+
|
|
125
|
+
const result = await useCase.run({
|
|
126
|
+
issues: [issue],
|
|
127
|
+
assigneeLogin: ASSIGNEE,
|
|
128
|
+
launcherCommand: LAUNCHER,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
expect(repository.launches).toHaveLength(1);
|
|
132
|
+
expect(result.launchedIssueUrls).toEqual([issue.url]);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
it('launches only the missing sessions across multiple In Tmux by human issues', async () => {
|
|
136
|
+
const liveIssue = makeIssue();
|
|
137
|
+
const missingIssueOne = makeIssue();
|
|
138
|
+
const missingIssueTwo = makeIssue();
|
|
139
|
+
const repository = createFakeTmuxSessionRepository({
|
|
140
|
+
liveSessionNames: [toTmuxSessionName(liveIssue.url)],
|
|
141
|
+
processCommandLines: [`claude --model opus --name ${liveIssue.url}`],
|
|
142
|
+
});
|
|
143
|
+
const useCase = new InTmuxByHumanSessionReconcileUseCase(repository);
|
|
144
|
+
|
|
145
|
+
const result = await useCase.run({
|
|
146
|
+
issues: [liveIssue, missingIssueOne, missingIssueTwo],
|
|
147
|
+
assigneeLogin: ASSIGNEE,
|
|
148
|
+
launcherCommand: LAUNCHER,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
expect(result.launchedIssueUrls).toEqual([
|
|
152
|
+
missingIssueOne.url,
|
|
153
|
+
missingIssueTwo.url,
|
|
154
|
+
]);
|
|
155
|
+
expect(repository.launches.map((launch) => launch.issueUrl)).toEqual([
|
|
156
|
+
missingIssueOne.url,
|
|
157
|
+
missingIssueTwo.url,
|
|
158
|
+
]);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('ignores issues that are not In Tmux by human, closed, or assigned to another login', async () => {
|
|
162
|
+
const otherStatusIssue = makeIssue({ status: 'Done' });
|
|
163
|
+
const closedIssue = makeIssue({ isClosed: true });
|
|
164
|
+
const otherAssigneeIssue = makeIssue({ assignees: ['someone-else'] });
|
|
165
|
+
const repository = createFakeTmuxSessionRepository({
|
|
166
|
+
liveSessionNames: [],
|
|
167
|
+
processCommandLines: [],
|
|
168
|
+
});
|
|
169
|
+
const useCase = new InTmuxByHumanSessionReconcileUseCase(repository);
|
|
170
|
+
|
|
171
|
+
const result = await useCase.run({
|
|
172
|
+
issues: [otherStatusIssue, closedIssue, otherAssigneeIssue],
|
|
173
|
+
assigneeLogin: ASSIGNEE,
|
|
174
|
+
launcherCommand: LAUNCHER,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
expect(repository.launches).toEqual([]);
|
|
178
|
+
expect(result.launchedIssueUrls).toEqual([]);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('transforms an issue url into a tmux session name by replacing non-alphanumeric characters', () => {
|
|
182
|
+
expect(toTmuxSessionName('https://github.com/demo/repo/issues/42')).toBe(
|
|
183
|
+
'https___github_com_demo_repo_issues_42',
|
|
184
|
+
);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Issue } from '../../entities/Issue';
|
|
2
|
+
import { IN_TMUX_STATUS_NAME } from '../../entities/WorkflowStatus';
|
|
3
|
+
import { TmuxSessionRepository } from '../adapter-interfaces/TmuxSessionRepository';
|
|
4
|
+
|
|
5
|
+
export type InTmuxByHumanSessionReconcileInput = {
|
|
6
|
+
issues: Issue[];
|
|
7
|
+
assigneeLogin: string;
|
|
8
|
+
launcherCommand: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type InTmuxByHumanSessionReconcileResult = {
|
|
12
|
+
launchedIssueUrls: string[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const toTmuxSessionName = (issueUrl: string): string =>
|
|
16
|
+
issueUrl.replace(/[^a-zA-Z0-9]/g, '_');
|
|
17
|
+
|
|
18
|
+
export class InTmuxByHumanSessionReconcileUseCase {
|
|
19
|
+
constructor(private readonly tmuxSessionRepository: TmuxSessionRepository) {}
|
|
20
|
+
|
|
21
|
+
run = async (
|
|
22
|
+
input: InTmuxByHumanSessionReconcileInput,
|
|
23
|
+
): Promise<InTmuxByHumanSessionReconcileResult> => {
|
|
24
|
+
const { issues, assigneeLogin, launcherCommand } = input;
|
|
25
|
+
|
|
26
|
+
const targetIssues = issues.filter((issue) =>
|
|
27
|
+
this.isInTmuxByHuman(issue, assigneeLogin),
|
|
28
|
+
);
|
|
29
|
+
if (targetIssues.length === 0) {
|
|
30
|
+
return { launchedIssueUrls: [] };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const liveSessionNames = new Set(
|
|
34
|
+
await this.tmuxSessionRepository.listLiveSessionNames(),
|
|
35
|
+
);
|
|
36
|
+
const processCommandLines =
|
|
37
|
+
await this.tmuxSessionRepository.listInteractiveProcessCommandLines();
|
|
38
|
+
|
|
39
|
+
const launchedIssueUrls: string[] = [];
|
|
40
|
+
for (const issue of targetIssues) {
|
|
41
|
+
if (
|
|
42
|
+
this.hasLiveSession(issue.url, liveSessionNames, processCommandLines)
|
|
43
|
+
) {
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
await this.tmuxSessionRepository.launchDetachedSession(
|
|
47
|
+
toTmuxSessionName(issue.url),
|
|
48
|
+
launcherCommand,
|
|
49
|
+
issue.url,
|
|
50
|
+
);
|
|
51
|
+
launchedIssueUrls.push(issue.url);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return { launchedIssueUrls };
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
private isInTmuxByHuman = (issue: Issue, assigneeLogin: string): boolean =>
|
|
58
|
+
issue.status === IN_TMUX_STATUS_NAME &&
|
|
59
|
+
issue.isClosed === false &&
|
|
60
|
+
issue.assignees.includes(assigneeLogin);
|
|
61
|
+
|
|
62
|
+
private hasLiveSession = (
|
|
63
|
+
issueUrl: string,
|
|
64
|
+
liveSessionNames: Set<string>,
|
|
65
|
+
processCommandLines: string[],
|
|
66
|
+
): boolean => {
|
|
67
|
+
const sessionName = toTmuxSessionName(issueUrl);
|
|
68
|
+
if (!liveSessionNames.has(sessionName)) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return processCommandLines.some((commandLine) =>
|
|
72
|
+
commandLine.includes(issueUrl),
|
|
73
|
+
);
|
|
74
|
+
};
|
|
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":"AA0BA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AA8B3D,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,CA4WP;CACH"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Issue } from '../../../domain/entities/Issue';
|
|
2
|
+
import { LocalCommandRunner } from '../../../domain/usecases/adapter-interfaces/LocalCommandRunner';
|
|
3
|
+
export type ReconcileInTmuxByHumanSessionsParams = {
|
|
4
|
+
inTmuxLauncherCommand: string | null;
|
|
5
|
+
assigneeLogin: string;
|
|
6
|
+
issues: Issue[];
|
|
7
|
+
localCommandRunner: LocalCommandRunner;
|
|
8
|
+
};
|
|
9
|
+
export declare const reconcileInTmuxByHumanSessions: (params: ReconcileInTmuxByHumanSessionsParams) => Promise<void>;
|
|
10
|
+
//# sourceMappingURL=inTmuxByHumanSessionReconciler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inTmuxByHumanSessionReconciler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gEAAgE,CAAC;AAIpG,MAAM,MAAM,oCAAoC,GAAG;IACjD,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,kBAAkB,EAAE,kBAAkB,CAAC;CACxC,CAAC;AAEF,eAAO,MAAM,8BAA8B,GACzC,QAAQ,oCAAoC,KAC3C,OAAO,CAAC,IAAI,CAcd,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { LocalCommandRunner } from '../../domain/usecases/adapter-interfaces/LocalCommandRunner';
|
|
2
|
+
import { TmuxSessionRepository } from '../../domain/usecases/adapter-interfaces/TmuxSessionRepository';
|
|
3
|
+
export declare class NodeTmuxSessionRepository implements TmuxSessionRepository {
|
|
4
|
+
private readonly localCommandRunner;
|
|
5
|
+
constructor(localCommandRunner: LocalCommandRunner);
|
|
6
|
+
listLiveSessionNames: () => Promise<string[]>;
|
|
7
|
+
listInteractiveProcessCommandLines: () => Promise<string[]>;
|
|
8
|
+
launchDetachedSession: (sessionName: string, launcherCommand: string, issueUrl: string) => Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=NodeTmuxSessionRepository.d.ts.map
|
|
@@ -0,0 +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;AAEvG,qBAAa,yBAA0B,YAAW,qBAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,kBAAkB;gBAAlB,kBAAkB,EAAE,kBAAkB;IAEnE,oBAAoB,QAAa,OAAO,CAAC,MAAM,EAAE,CAAC,CAYhD;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,CAad;CACH"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface TmuxSessionRepository {
|
|
2
|
+
listLiveSessionNames: () => Promise<string[]>;
|
|
3
|
+
listInteractiveProcessCommandLines: () => Promise<string[]>;
|
|
4
|
+
launchDetachedSession: (sessionName: string, launcherCommand: string, issueUrl: string) => Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
//# sourceMappingURL=TmuxSessionRepository.d.ts.map
|
|
@@ -0,0 +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;CACpB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Issue } from '../../entities/Issue';
|
|
2
|
+
import { TmuxSessionRepository } from '../adapter-interfaces/TmuxSessionRepository';
|
|
3
|
+
export type InTmuxByHumanSessionReconcileInput = {
|
|
4
|
+
issues: Issue[];
|
|
5
|
+
assigneeLogin: string;
|
|
6
|
+
launcherCommand: string;
|
|
7
|
+
};
|
|
8
|
+
export type InTmuxByHumanSessionReconcileResult = {
|
|
9
|
+
launchedIssueUrls: string[];
|
|
10
|
+
};
|
|
11
|
+
export declare const toTmuxSessionName: (issueUrl: string) => string;
|
|
12
|
+
export declare class InTmuxByHumanSessionReconcileUseCase {
|
|
13
|
+
private readonly tmuxSessionRepository;
|
|
14
|
+
constructor(tmuxSessionRepository: TmuxSessionRepository);
|
|
15
|
+
run: (input: InTmuxByHumanSessionReconcileInput) => Promise<InTmuxByHumanSessionReconcileResult>;
|
|
16
|
+
private isInTmuxByHuman;
|
|
17
|
+
private hasLiveSession;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=InTmuxByHumanSessionReconcileUseCase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InTmuxByHumanSessionReconcileUseCase.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAE7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAC;AAEpF,MAAM,MAAM,kCAAkC,GAAG;IAC/C,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,mCAAmC,GAAG;IAChD,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAAI,UAAU,MAAM,KAAG,MACb,CAAC;AAEzC,qBAAa,oCAAoC;IACnC,OAAO,CAAC,QAAQ,CAAC,qBAAqB;gBAArB,qBAAqB,EAAE,qBAAqB;IAEzE,GAAG,GACD,OAAO,kCAAkC,KACxC,OAAO,CAAC,mCAAmC,CAAC,CAgC7C;IAEF,OAAO,CAAC,eAAe,CAGmB;IAE1C,OAAO,CAAC,cAAc,CAYpB;CACH"}
|