github-issue-tower-defence-management 1.114.0 → 1.114.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +1 -1
- package/bin/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.js +36 -27
- package/bin/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.js.map +1 -1
- package/bin/adapter/repositories/FileSystemSessionRecordReader.js +69 -0
- package/bin/adapter/repositories/FileSystemSessionRecordReader.js.map +1 -0
- package/bin/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.js +9 -2
- package/bin/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.js.map +1 -1
- package/bin/domain/usecases/ResolveInteractiveLiveSessionsUseCase.js +15 -0
- package/bin/domain/usecases/ResolveInteractiveLiveSessionsUseCase.js.map +1 -1
- package/bin/domain/usecases/adapter-interfaces/SessionRecordReader.js +3 -0
- package/bin/domain/usecases/adapter-interfaces/SessionRecordReader.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.test.ts +107 -26
- package/src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.ts +49 -29
- package/src/adapter/repositories/FileSystemSessionRecordReader.test.ts +59 -0
- package/src/adapter/repositories/FileSystemSessionRecordReader.ts +31 -0
- package/src/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.test.ts +51 -0
- package/src/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.ts +10 -1
- package/src/domain/entities/InteractiveLiveSession.ts +1 -0
- package/src/domain/entities/LiveSessionProcessSnapshot.ts +1 -0
- package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +4 -0
- package/src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.test.ts +66 -59
- package/src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.ts +19 -0
- package/src/domain/usecases/adapter-interfaces/SessionRecordReader.ts +3 -0
- package/types/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.d.ts +4 -11
- package/types/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.d.ts.map +1 -1
- package/types/adapter/repositories/FileSystemSessionRecordReader.d.ts +5 -0
- package/types/adapter/repositories/FileSystemSessionRecordReader.d.ts.map +1 -0
- package/types/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.d.ts +3 -1
- package/types/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.d.ts.map +1 -1
- package/types/domain/entities/InteractiveLiveSession.d.ts +1 -0
- package/types/domain/entities/InteractiveLiveSession.d.ts.map +1 -1
- package/types/domain/entities/LiveSessionProcessSnapshot.d.ts +1 -0
- package/types/domain/entities/LiveSessionProcessSnapshot.d.ts.map +1 -1
- package/types/domain/usecases/ResolveInteractiveLiveSessionsUseCase.d.ts +1 -0
- package/types/domain/usecases/ResolveInteractiveLiveSessionsUseCase.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/SessionRecordReader.d.ts +4 -0
- package/types/domain/usecases/adapter-interfaces/SessionRecordReader.d.ts.map +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
## [1.114.1](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.114.0...v1.114.1) (2026-06-28)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **silent-notification:** resolve live transcripts via rotated id and shared projects dir ([#1065](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1065)) ([95e491a](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/95e491ab813e577c795c5a11d59fb1fab1b035e4)), closes [#1064](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1064)
|
|
7
|
+
|
|
1
8
|
# [1.114.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.113.0...v1.114.0) (2026-06-28)
|
|
2
9
|
|
|
3
10
|
|
package/README.md
CHANGED
|
@@ -556,7 +556,7 @@ For each `In Tmux by human` issue with no live session, the reconciler launches
|
|
|
556
556
|
|
|
557
557
|
Each schedule cycle, when `silentNotificationEnabled` is true, also inspects every live interactive Claude Code session and sends a short self-check reminder into a session via `tmux send-keys` when either of two independent conditions holds. The reminder is composed of only the sections whose condition is met, so a session receives one combined message containing one or both sections. When `silentNotificationEnabled` is not set, the entire step is a no-op even if the daemon picks up its configuration automatically.
|
|
558
558
|
|
|
559
|
-
Sessions are selected purely from the live process tree, independently of session naming or any GitHub issue linkage: every live tmux session is taken, the descendants of each session's pane processes are walked, and the first descendant that is an interactive Claude Code process (its command line contains `claude` and does not contain the owner-handover marker `Take ownership of`, and its environment exposes both `CLAUDE_CODE_SESSION_ID` and `CLAUDE_CONFIG_DIR`) is taken as that session's process. Both issue-url-named sessions and plain-named sessions (for example `workbench`) are selected equally. Because the walk is anchored on tmux panes, background owner-handover spawns, the preparation daemon, and monitor processes — none of which run inside an interactive tmux pane — are excluded; the owner-handover marker check is an additional guard. Each selected session's transcript is then resolved
|
|
559
|
+
Sessions are selected purely from the live process tree, independently of session naming or any GitHub issue linkage: every live tmux session is taken, the descendants of each session's pane processes are walked, and the first descendant that is an interactive Claude Code process (its command line contains `claude` and does not contain the owner-handover marker `Take ownership of`, and its environment exposes both `CLAUDE_CODE_SESSION_ID` and `CLAUDE_CONFIG_DIR`) is taken as that session's process. Both issue-url-named sessions and plain-named sessions (for example `workbench`) are selected equally. Because the walk is anchored on tmux panes, background owner-handover spawns, the preparation daemon, and monitor processes — none of which run inside an interactive tmux pane — are excluded; the owner-handover marker check is an additional guard. Each selected session's transcript is then resolved by scanning the `projects/<cwd-slug>/` subdirectories of two roots — the per-session `<CLAUDE_CONFIG_DIR>/projects/` and the shared `~/.claude/projects/` — for a `<sessionId>.jsonl` file named by any of the session's candidate session ids. The candidate ids are, in priority order, the current session id recorded in `<CLAUDE_CONFIG_DIR>/sessions/<pid>.json` (the session id rotates on resume or compaction, and this record holds the current value) followed by the launch id from the `CLAUDE_CODE_SESSION_ID` environment variable. Searching the shared root and the rotated current id is required because, for a resumed or compacted session, the actively-written transcript is named by the current id and lives under the shared root rather than under the per-session config directory; the most recently modified match is used when several exist. A session that has no issue URL resolves just as reliably as one that does.
|
|
560
560
|
|
|
561
561
|
The main-session stalled section is sent when the session's main output has been idle for at least `mainSilentThresholdSeconds`, unless the session is currently waiting on the owner (in that case the silence is expected and the section is suppressed). Idle time is computed from the timestamp of the latest `assistant` entry in the session's resolved transcript rather than from the transcript file modification time, so a transcript that is only updated by tool results or owner replies still counts as silent. Whether a session is waiting on the owner is determined through an injectable port; the built-in default reports no session as waiting, so the suppression is only applied when a host provides an implementation. When `ownerCallMarker` is configured, a transcript-based implementation is used: it reads each session's resolved transcript and treats the session as waiting on the owner when the latest entry containing `ownerCallMarker` is newer (by full timestamp) than the latest genuine owner reply. A self-check reminder that the monitor injects into a session via `tmux send-keys` itself lands in that session's transcript as a `user` text entry; every injected reminder carries a fixed sentinel tag (`[TDPM_SILENT_SESSION_SELF_CHECK_REMINDER]`), and a `user` entry whose text contains that sentinel is not counted as a genuine owner reply. This prevents the monitor's own reminder from being mistaken for the owner answering, which would otherwise make a genuinely waiting session stop being suppressed.
|
|
562
562
|
|
|
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.FileSystemInteractiveLiveSessionTranscriptResolver = void 0;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
|
+
const os = __importStar(require("os"));
|
|
38
39
|
const path = __importStar(require("path"));
|
|
39
40
|
const modifiedEpochMs = (filePath) => {
|
|
40
41
|
try {
|
|
@@ -45,19 +46,10 @@ const modifiedEpochMs = (filePath) => {
|
|
|
45
46
|
return null;
|
|
46
47
|
}
|
|
47
48
|
};
|
|
48
|
-
|
|
49
|
-
* Resolves the real transcript path of an interactive Claude Code session from
|
|
50
|
-
* the session id and config directory taken from the session's process
|
|
51
|
-
* environment. The transcript lives at
|
|
52
|
-
* `<configDir>/projects/<cwd-slug>/<sessionId>.jsonl`; this resolver scans the
|
|
53
|
-
* `projects` subdirectories for a file named `<sessionId>.jsonl` and returns the
|
|
54
|
-
* most recently modified match. Because resolution is keyed on the process
|
|
55
|
-
* session id rather than on a session name or issue URL, a plain-named session
|
|
56
|
-
* (for example one named `workbench`) resolves just as well as an issue-url
|
|
57
|
-
* named one.
|
|
58
|
-
*/
|
|
49
|
+
const defaultSharedProjectsDirectory = () => path.join(os.homedir(), '.claude', 'projects');
|
|
59
50
|
class FileSystemInteractiveLiveSessionTranscriptResolver {
|
|
60
|
-
constructor() {
|
|
51
|
+
constructor(sharedProjectsDirectory = defaultSharedProjectsDirectory()) {
|
|
52
|
+
this.sharedProjectsDirectory = sharedProjectsDirectory;
|
|
61
53
|
this.resolveTranscriptPaths = (sessions) => {
|
|
62
54
|
const resolved = new Map();
|
|
63
55
|
for (const session of sessions) {
|
|
@@ -69,7 +61,34 @@ class FileSystemInteractiveLiveSessionTranscriptResolver {
|
|
|
69
61
|
return resolved;
|
|
70
62
|
};
|
|
71
63
|
this.resolveTranscriptPath = (session) => {
|
|
72
|
-
const
|
|
64
|
+
const projectsDirectories = this.listProjectsDirectories(session.configDir);
|
|
65
|
+
let latestPath = null;
|
|
66
|
+
let latestEpochMs = -Infinity;
|
|
67
|
+
for (const candidateSessionId of session.candidateSessionIds) {
|
|
68
|
+
const fileName = `${candidateSessionId}.jsonl`;
|
|
69
|
+
for (const projectsDirectory of projectsDirectories) {
|
|
70
|
+
for (const candidate of this.listCandidatePaths(projectsDirectory, fileName)) {
|
|
71
|
+
const epochMs = modifiedEpochMs(candidate);
|
|
72
|
+
if (epochMs === null) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (epochMs > latestEpochMs) {
|
|
76
|
+
latestEpochMs = epochMs;
|
|
77
|
+
latestPath = candidate;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return latestPath;
|
|
83
|
+
};
|
|
84
|
+
this.listProjectsDirectories = (configDir) => {
|
|
85
|
+
const perSessionProjectsDirectory = path.join(configDir, 'projects');
|
|
86
|
+
if (perSessionProjectsDirectory === this.sharedProjectsDirectory) {
|
|
87
|
+
return [perSessionProjectsDirectory];
|
|
88
|
+
}
|
|
89
|
+
return [perSessionProjectsDirectory, this.sharedProjectsDirectory];
|
|
90
|
+
};
|
|
91
|
+
this.listCandidatePaths = (projectsDirectory, fileName) => {
|
|
73
92
|
let projectEntries;
|
|
74
93
|
try {
|
|
75
94
|
projectEntries = fs.readdirSync(projectsDirectory, {
|
|
@@ -77,26 +96,16 @@ class FileSystemInteractiveLiveSessionTranscriptResolver {
|
|
|
77
96
|
});
|
|
78
97
|
}
|
|
79
98
|
catch {
|
|
80
|
-
return
|
|
99
|
+
return [];
|
|
81
100
|
}
|
|
82
|
-
const
|
|
83
|
-
let latestPath = null;
|
|
84
|
-
let latestEpochMs = -Infinity;
|
|
101
|
+
const candidatePaths = [];
|
|
85
102
|
for (const projectEntry of projectEntries) {
|
|
86
103
|
if (!projectEntry.isDirectory()) {
|
|
87
104
|
continue;
|
|
88
105
|
}
|
|
89
|
-
|
|
90
|
-
const epochMs = modifiedEpochMs(candidate);
|
|
91
|
-
if (epochMs === null) {
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
if (epochMs > latestEpochMs) {
|
|
95
|
-
latestEpochMs = epochMs;
|
|
96
|
-
latestPath = candidate;
|
|
97
|
-
}
|
|
106
|
+
candidatePaths.push(path.join(projectsDirectory, projectEntry.name, fileName));
|
|
98
107
|
}
|
|
99
|
-
return
|
|
108
|
+
return candidatePaths;
|
|
100
109
|
};
|
|
101
110
|
}
|
|
102
111
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FileSystemInteractiveLiveSessionTranscriptResolver.js","sourceRoot":"","sources":["../../../src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,2CAA6B;AAI7B,MAAM,eAAe,GAAG,CAAC,QAAgB,EAAiB,EAAE;IAC1D,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACpC,OAAO,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"FileSystemInteractiveLiveSessionTranscriptResolver.js","sourceRoot":"","sources":["../../../src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.FileSystemSessionRecordReader = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const isRecord = (value) => typeof value === 'object' && value !== null;
|
|
40
|
+
class FileSystemSessionRecordReader {
|
|
41
|
+
constructor() {
|
|
42
|
+
this.readCurrentSessionId = (configDir, pid) => {
|
|
43
|
+
const recordPath = path.join(configDir, 'sessions', `${pid}.json`);
|
|
44
|
+
let content;
|
|
45
|
+
try {
|
|
46
|
+
content = fs.readFileSync(recordPath, 'utf8');
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
let parsed;
|
|
52
|
+
try {
|
|
53
|
+
parsed = JSON.parse(content);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (!isRecord(parsed)) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const sessionId = parsed.sessionId;
|
|
62
|
+
return typeof sessionId === 'string' && sessionId.length > 0
|
|
63
|
+
? sessionId
|
|
64
|
+
: null;
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.FileSystemSessionRecordReader = FileSystemSessionRecordReader;
|
|
69
|
+
//# sourceMappingURL=FileSystemSessionRecordReader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FileSystemSessionRecordReader.js","sourceRoot":"","sources":["../../../src/adapter/repositories/FileSystemSessionRecordReader.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAyB;AACzB,2CAA6B;AAG7B,MAAM,QAAQ,GAAG,CAAC,KAAc,EAAoC,EAAE,CACpE,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AAE9C,MAAa,6BAA6B;IAA1C;QACE,yBAAoB,GAAG,CAAC,SAAiB,EAAE,GAAW,EAAiB,EAAE;YACvE,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,GAAG,GAAG,OAAO,CAAC,CAAC;YACnE,IAAI,OAAe,CAAC;YACpB,IAAI,CAAC;gBACH,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAChD,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,MAAe,CAAC;YACpB,IAAI,CAAC;gBACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC/B,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,CAAC;YACd,CAAC;YACD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACtB,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,CAAC;YACnC,OAAO,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC;gBAC1D,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,IAAI,CAAC;QACX,CAAC,CAAC;IACJ,CAAC;CAAA;AAvBD,sEAuBC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.LocalProcessLiveSessionProcessSnapshotProvider = void 0;
|
|
4
|
+
const FileSystemSessionRecordReader_1 = require("./FileSystemSessionRecordReader");
|
|
4
5
|
const SESSION_ID_ENV_KEY = 'CLAUDE_CODE_SESSION_ID';
|
|
5
6
|
const CONFIG_DIR_ENV_KEY = 'CLAUDE_CONFIG_DIR';
|
|
6
7
|
/**
|
|
@@ -11,9 +12,10 @@ const CONFIG_DIR_ENV_KEY = 'CLAUDE_CONFIG_DIR';
|
|
|
11
12
|
* pure resolver, so all host access is contained in this adapter.
|
|
12
13
|
*/
|
|
13
14
|
class LocalProcessLiveSessionProcessSnapshotProvider {
|
|
14
|
-
constructor(localCommandRunner, environReader) {
|
|
15
|
+
constructor(localCommandRunner, environReader, sessionRecordReader = new FileSystemSessionRecordReader_1.FileSystemSessionRecordReader()) {
|
|
15
16
|
this.localCommandRunner = localCommandRunner;
|
|
16
17
|
this.environReader = environReader;
|
|
18
|
+
this.sessionRecordReader = sessionRecordReader;
|
|
17
19
|
this.getSnapshot = async () => {
|
|
18
20
|
const sessionNames = await this.listSessionNames();
|
|
19
21
|
const sessions = [];
|
|
@@ -59,12 +61,17 @@ class LocalProcessLiveSessionProcessSnapshotProvider {
|
|
|
59
61
|
const ppid = Number(match[2]);
|
|
60
62
|
const commandLine = match[3].trim();
|
|
61
63
|
const environ = this.environReader.readEnviron(pid);
|
|
64
|
+
const configDir = environ?.[CONFIG_DIR_ENV_KEY] ?? null;
|
|
65
|
+
const currentSessionId = configDir === null
|
|
66
|
+
? null
|
|
67
|
+
: this.sessionRecordReader.readCurrentSessionId(configDir, pid);
|
|
62
68
|
processes.push({
|
|
63
69
|
pid,
|
|
64
70
|
ppid,
|
|
65
71
|
commandLine,
|
|
66
72
|
sessionId: environ?.[SESSION_ID_ENV_KEY] ?? null,
|
|
67
|
-
|
|
73
|
+
currentSessionId,
|
|
74
|
+
configDir,
|
|
68
75
|
});
|
|
69
76
|
}
|
|
70
77
|
return processes;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalProcessLiveSessionProcessSnapshotProvider.js","sourceRoot":"","sources":["../../../src/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.ts"],"names":[],"mappings":";;;AASA,MAAM,kBAAkB,GAAG,wBAAwB,CAAC;AACpD,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AAE/C;;;;;;GAMG;AACH,MAAa,8CAA8C;IACzD,YACmB,kBAAsC,EACtC,aAAmC;
|
|
1
|
+
{"version":3,"file":"LocalProcessLiveSessionProcessSnapshotProvider.js","sourceRoot":"","sources":["../../../src/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.ts"],"names":[],"mappings":"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"}
|
|
@@ -55,11 +55,26 @@ class ResolveInteractiveLiveSessionsUseCase {
|
|
|
55
55
|
sessions.push({
|
|
56
56
|
sessionName: session.sessionName,
|
|
57
57
|
sessionId: interactiveProcess.sessionId,
|
|
58
|
+
candidateSessionIds: this.collectCandidateSessionIds(interactiveProcess),
|
|
58
59
|
configDir: interactiveProcess.configDir,
|
|
59
60
|
});
|
|
60
61
|
}
|
|
61
62
|
return sessions;
|
|
62
63
|
};
|
|
64
|
+
this.collectCandidateSessionIds = (interactiveProcess) => {
|
|
65
|
+
const candidateSessionIds = [];
|
|
66
|
+
const seenSessionIds = new Set();
|
|
67
|
+
for (const sessionId of [
|
|
68
|
+
interactiveProcess.currentSessionId,
|
|
69
|
+
interactiveProcess.sessionId,
|
|
70
|
+
]) {
|
|
71
|
+
if (sessionId !== null && !seenSessionIds.has(sessionId)) {
|
|
72
|
+
seenSessionIds.add(sessionId);
|
|
73
|
+
candidateSessionIds.push(sessionId);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return candidateSessionIds;
|
|
77
|
+
};
|
|
63
78
|
this.findInteractiveProcess = (panePids, processByPid, childrenByPpid) => {
|
|
64
79
|
const visited = new Set();
|
|
65
80
|
const queue = [...panePids];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ResolveInteractiveLiveSessionsUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ResolveInteractiveLiveSessionsUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SessionRecordReader.js","sourceRoot":"","sources":["../../../../src/domain/usecases/adapter-interfaces/SessionRecordReader.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -5,11 +5,13 @@ import { FileSystemInteractiveLiveSessionTranscriptResolver } from './FileSystem
|
|
|
5
5
|
|
|
6
6
|
describe('FileSystemInteractiveLiveSessionTranscriptResolver', () => {
|
|
7
7
|
let configRoot: string;
|
|
8
|
+
let sharedProjectsDirectory: string;
|
|
8
9
|
|
|
9
10
|
beforeEach(() => {
|
|
10
11
|
configRoot = fs.mkdtempSync(
|
|
11
12
|
path.join(os.tmpdir(), 'interactive-transcript-'),
|
|
12
13
|
);
|
|
14
|
+
sharedProjectsDirectory = path.join(configRoot, 'shared', 'projects');
|
|
13
15
|
});
|
|
14
16
|
|
|
15
17
|
afterEach(() => {
|
|
@@ -17,14 +19,13 @@ describe('FileSystemInteractiveLiveSessionTranscriptResolver', () => {
|
|
|
17
19
|
});
|
|
18
20
|
|
|
19
21
|
const writeTranscript = (params: {
|
|
20
|
-
|
|
22
|
+
projectsDirectory: string;
|
|
21
23
|
cwdSlug: string;
|
|
22
24
|
sessionId: string;
|
|
23
25
|
mtimeEpochSeconds?: number;
|
|
24
26
|
}): string => {
|
|
25
27
|
const projectDirectory = path.join(
|
|
26
|
-
params.
|
|
27
|
-
'projects',
|
|
28
|
+
params.projectsDirectory,
|
|
28
29
|
params.cwdSlug,
|
|
29
30
|
);
|
|
30
31
|
fs.mkdirSync(projectDirectory, { recursive: true });
|
|
@@ -40,40 +41,100 @@ describe('FileSystemInteractiveLiveSessionTranscriptResolver', () => {
|
|
|
40
41
|
return filePath;
|
|
41
42
|
};
|
|
42
43
|
|
|
43
|
-
it('resolves a transcript by config dir and session id
|
|
44
|
+
it('resolves a resume-case transcript by config dir and session id', () => {
|
|
44
45
|
const configDir = path.join(configRoot, 'workbench');
|
|
45
46
|
const filePath = writeTranscript({
|
|
46
|
-
configDir,
|
|
47
|
+
projectsDirectory: path.join(configDir, 'projects'),
|
|
47
48
|
cwdSlug: '-home-user',
|
|
48
49
|
sessionId: 'wb-uuid',
|
|
49
50
|
});
|
|
50
|
-
const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
|
|
51
|
+
const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
|
|
52
|
+
sharedProjectsDirectory,
|
|
53
|
+
);
|
|
51
54
|
|
|
52
55
|
const result = resolver.resolveTranscriptPaths([
|
|
53
|
-
{
|
|
56
|
+
{
|
|
57
|
+
sessionName: 'workbench',
|
|
58
|
+
sessionId: 'wb-uuid',
|
|
59
|
+
candidateSessionIds: ['wb-uuid'],
|
|
60
|
+
configDir,
|
|
61
|
+
},
|
|
54
62
|
]);
|
|
55
63
|
|
|
56
64
|
expect(result.get('workbench')).toBe(filePath);
|
|
57
65
|
});
|
|
58
66
|
|
|
59
|
-
it('
|
|
67
|
+
it('resolves a rotated-id transcript via a later candidate session id', () => {
|
|
68
|
+
const configDir = path.join(configRoot, 'workbench');
|
|
69
|
+
const rotatedPath = writeTranscript({
|
|
70
|
+
projectsDirectory: path.join(configDir, 'projects'),
|
|
71
|
+
cwdSlug: '-home-user',
|
|
72
|
+
sessionId: 'rotated-uuid',
|
|
73
|
+
});
|
|
74
|
+
const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
|
|
75
|
+
sharedProjectsDirectory,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const result = resolver.resolveTranscriptPaths([
|
|
79
|
+
{
|
|
80
|
+
sessionName: 'workbench',
|
|
81
|
+
sessionId: 'launch-uuid',
|
|
82
|
+
candidateSessionIds: ['rotated-uuid', 'launch-uuid'],
|
|
83
|
+
configDir,
|
|
84
|
+
},
|
|
85
|
+
]);
|
|
86
|
+
|
|
87
|
+
expect(result.get('workbench')).toBe(rotatedPath);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('resolves a transcript that lives under the shared projects directory', () => {
|
|
91
|
+
const configDir = path.join(configRoot, 'workbench');
|
|
92
|
+
const sharedPath = writeTranscript({
|
|
93
|
+
projectsDirectory: sharedProjectsDirectory,
|
|
94
|
+
cwdSlug: '-home-user',
|
|
95
|
+
sessionId: 'rotated-uuid',
|
|
96
|
+
});
|
|
97
|
+
const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
|
|
98
|
+
sharedProjectsDirectory,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
const result = resolver.resolveTranscriptPaths([
|
|
102
|
+
{
|
|
103
|
+
sessionName: 'workbench',
|
|
104
|
+
sessionId: 'launch-uuid',
|
|
105
|
+
candidateSessionIds: ['rotated-uuid', 'launch-uuid'],
|
|
106
|
+
configDir,
|
|
107
|
+
},
|
|
108
|
+
]);
|
|
109
|
+
|
|
110
|
+
expect(result.get('workbench')).toBe(sharedPath);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('prefers the most recently modified match across both projects roots', () => {
|
|
60
114
|
const configDir = path.join(configRoot, 'workbench');
|
|
61
115
|
writeTranscript({
|
|
62
|
-
configDir,
|
|
63
|
-
cwdSlug: '-home-user
|
|
64
|
-
sessionId: '
|
|
116
|
+
projectsDirectory: path.join(configDir, 'projects'),
|
|
117
|
+
cwdSlug: '-home-user',
|
|
118
|
+
sessionId: 'rotated-uuid',
|
|
65
119
|
mtimeEpochSeconds: 1700000000,
|
|
66
120
|
});
|
|
67
121
|
const newerPath = writeTranscript({
|
|
68
|
-
|
|
69
|
-
cwdSlug: '-home-user
|
|
70
|
-
sessionId: '
|
|
122
|
+
projectsDirectory: sharedProjectsDirectory,
|
|
123
|
+
cwdSlug: '-home-user',
|
|
124
|
+
sessionId: 'rotated-uuid',
|
|
71
125
|
mtimeEpochSeconds: 1700000500,
|
|
72
126
|
});
|
|
73
|
-
const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
|
|
127
|
+
const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
|
|
128
|
+
sharedProjectsDirectory,
|
|
129
|
+
);
|
|
74
130
|
|
|
75
131
|
const result = resolver.resolveTranscriptPaths([
|
|
76
|
-
{
|
|
132
|
+
{
|
|
133
|
+
sessionName: 'workbench',
|
|
134
|
+
sessionId: 'launch-uuid',
|
|
135
|
+
candidateSessionIds: ['rotated-uuid', 'launch-uuid'],
|
|
136
|
+
configDir,
|
|
137
|
+
},
|
|
77
138
|
]);
|
|
78
139
|
|
|
79
140
|
expect(result.get('workbench')).toBe(newerPath);
|
|
@@ -83,26 +144,30 @@ describe('FileSystemInteractiveLiveSessionTranscriptResolver', () => {
|
|
|
83
144
|
const workbenchConfig = path.join(configRoot, 'workbench');
|
|
84
145
|
const controlRoomConfig = path.join(configRoot, 'control-room');
|
|
85
146
|
const workbenchPath = writeTranscript({
|
|
86
|
-
|
|
147
|
+
projectsDirectory: path.join(workbenchConfig, 'projects'),
|
|
87
148
|
cwdSlug: '-home-user',
|
|
88
149
|
sessionId: 'wb-uuid',
|
|
89
150
|
});
|
|
90
151
|
const controlRoomPath = writeTranscript({
|
|
91
|
-
|
|
152
|
+
projectsDirectory: path.join(controlRoomConfig, 'projects'),
|
|
92
153
|
cwdSlug: '-home-user',
|
|
93
154
|
sessionId: 'cr-uuid',
|
|
94
155
|
});
|
|
95
|
-
const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
|
|
156
|
+
const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
|
|
157
|
+
sharedProjectsDirectory,
|
|
158
|
+
);
|
|
96
159
|
|
|
97
160
|
const result = resolver.resolveTranscriptPaths([
|
|
98
161
|
{
|
|
99
162
|
sessionName: 'workbench',
|
|
100
163
|
sessionId: 'wb-uuid',
|
|
164
|
+
candidateSessionIds: ['wb-uuid'],
|
|
101
165
|
configDir: workbenchConfig,
|
|
102
166
|
},
|
|
103
167
|
{
|
|
104
168
|
sessionName: 'control-room',
|
|
105
169
|
sessionId: 'cr-uuid',
|
|
170
|
+
candidateSessionIds: ['cr-uuid'],
|
|
106
171
|
configDir: controlRoomConfig,
|
|
107
172
|
},
|
|
108
173
|
]);
|
|
@@ -111,34 +176,50 @@ describe('FileSystemInteractiveLiveSessionTranscriptResolver', () => {
|
|
|
111
176
|
expect(result.get('control-room')).toBe(controlRoomPath);
|
|
112
177
|
});
|
|
113
178
|
|
|
114
|
-
it('omits a session whose transcript file is absent', () => {
|
|
179
|
+
it('omits a session whose transcript file is absent in both roots', () => {
|
|
115
180
|
const configDir = path.join(configRoot, 'workbench');
|
|
116
181
|
fs.mkdirSync(path.join(configDir, 'projects', '-home-user'), {
|
|
117
182
|
recursive: true,
|
|
118
183
|
});
|
|
119
|
-
const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
|
|
184
|
+
const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
|
|
185
|
+
sharedProjectsDirectory,
|
|
186
|
+
);
|
|
120
187
|
|
|
121
188
|
const result = resolver.resolveTranscriptPaths([
|
|
122
|
-
{
|
|
189
|
+
{
|
|
190
|
+
sessionName: 'workbench',
|
|
191
|
+
sessionId: 'missing-uuid',
|
|
192
|
+
candidateSessionIds: ['missing-uuid'],
|
|
193
|
+
configDir,
|
|
194
|
+
},
|
|
123
195
|
]);
|
|
124
196
|
|
|
125
197
|
expect(result.has('workbench')).toBe(false);
|
|
126
198
|
});
|
|
127
199
|
|
|
128
|
-
it('omits a session
|
|
200
|
+
it('omits a session when neither projects directory exists', () => {
|
|
129
201
|
const configDir = path.join(configRoot, 'workbench');
|
|
130
202
|
fs.mkdirSync(configDir, { recursive: true });
|
|
131
|
-
const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
|
|
203
|
+
const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
|
|
204
|
+
sharedProjectsDirectory,
|
|
205
|
+
);
|
|
132
206
|
|
|
133
207
|
const result = resolver.resolveTranscriptPaths([
|
|
134
|
-
{
|
|
208
|
+
{
|
|
209
|
+
sessionName: 'workbench',
|
|
210
|
+
sessionId: 'wb-uuid',
|
|
211
|
+
candidateSessionIds: ['wb-uuid'],
|
|
212
|
+
configDir,
|
|
213
|
+
},
|
|
135
214
|
]);
|
|
136
215
|
|
|
137
216
|
expect(result.has('workbench')).toBe(false);
|
|
138
217
|
});
|
|
139
218
|
|
|
140
219
|
it('returns an empty map for an empty session list', () => {
|
|
141
|
-
const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
|
|
220
|
+
const resolver = new FileSystemInteractiveLiveSessionTranscriptResolver(
|
|
221
|
+
sharedProjectsDirectory,
|
|
222
|
+
);
|
|
142
223
|
|
|
143
224
|
const result = resolver.resolveTranscriptPaths([]);
|
|
144
225
|
|