@agtd/agent 0.1.2 → 0.1.3
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/dist/agent.js +6 -49
- package/dist/{chunk-24ORBJSI.js → chunk-RJLNNX7L.js} +489 -32
- package/dist/cli.js +186 -120
- package/package.json +2 -1
- package/dist/__tests__/codexAdapterFindFile.test.d.ts +0 -2
- package/dist/__tests__/codexAdapterFindFile.test.d.ts.map +0 -1
- package/dist/__tests__/codexAdapterFindFile.test.js +0 -298
- package/dist/__tests__/codexAdapterFindFile.test.js.map +0 -1
- package/dist/__tests__/enrichers.test.d.ts +0 -2
- package/dist/__tests__/enrichers.test.d.ts.map +0 -1
- package/dist/__tests__/enrichers.test.js +0 -47
- package/dist/__tests__/enrichers.test.js.map +0 -1
- package/dist/__tests__/tmux.integration.test.d.ts +0 -2
- package/dist/__tests__/tmux.integration.test.d.ts.map +0 -1
- package/dist/__tests__/tmux.integration.test.js +0 -112
- package/dist/__tests__/tmux.integration.test.js.map +0 -1
- package/dist/__tests__/tmux.test.d.ts +0 -2
- package/dist/__tests__/tmux.test.d.ts.map +0 -1
- package/dist/__tests__/tmux.test.js +0 -26
- package/dist/__tests__/tmux.test.js.map +0 -1
- package/dist/__tests__/transcriptAdapters.test.d.ts +0 -2
- package/dist/__tests__/transcriptAdapters.test.d.ts.map +0 -1
- package/dist/__tests__/transcriptAdapters.test.js +0 -133
- package/dist/__tests__/transcriptAdapters.test.js.map +0 -1
- package/dist/adapters/claude-code.d.ts +0 -3
- package/dist/adapters/claude-code.d.ts.map +0 -1
- package/dist/adapters/claude-code.js +0 -48
- package/dist/adapters/claude-code.js.map +0 -1
- package/dist/adapters/codex.d.ts +0 -3
- package/dist/adapters/codex.d.ts.map +0 -1
- package/dist/adapters/codex.js +0 -33
- package/dist/adapters/codex.js.map +0 -1
- package/dist/adapters/generic.d.ts +0 -3
- package/dist/adapters/generic.d.ts.map +0 -1
- package/dist/adapters/generic.js +0 -38
- package/dist/adapters/generic.js.map +0 -1
- package/dist/adapters/index.d.ts +0 -7
- package/dist/adapters/index.d.ts.map +0 -1
- package/dist/adapters/index.js +0 -12
- package/dist/adapters/index.js.map +0 -1
- package/dist/agent.d.ts +0 -3
- package/dist/agent.d.ts.map +0 -1
- package/dist/agent.js.map +0 -1
- package/dist/cli.d.ts +0 -11
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/config.d.ts +0 -26
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -48
- package/dist/config.js.map +0 -1
- package/dist/enrichers/git.d.ts +0 -6
- package/dist/enrichers/git.d.ts.map +0 -1
- package/dist/enrichers/git.js +0 -20
- package/dist/enrichers/git.js.map +0 -1
- package/dist/enrichers/pr.d.ts +0 -2
- package/dist/enrichers/pr.d.ts.map +0 -1
- package/dist/enrichers/pr.js +0 -10
- package/dist/enrichers/pr.js.map +0 -1
- package/dist/enrichers/transcript.d.ts +0 -8
- package/dist/enrichers/transcript.d.ts.map +0 -1
- package/dist/enrichers/transcript.js +0 -33
- package/dist/enrichers/transcript.js.map +0 -1
- package/dist/heartbeat.d.ts +0 -4
- package/dist/heartbeat.d.ts.map +0 -1
- package/dist/heartbeat.js +0 -15
- package/dist/heartbeat.js.map +0 -1
- package/dist/init.d.ts +0 -10
- package/dist/init.d.ts.map +0 -1
- package/dist/init.js +0 -70
- package/dist/init.js.map +0 -1
- package/dist/register.d.ts +0 -3
- package/dist/register.d.ts.map +0 -1
- package/dist/register.js +0 -22
- package/dist/register.js.map +0 -1
- package/dist/sessionScanner.d.ts +0 -20
- package/dist/sessionScanner.d.ts.map +0 -1
- package/dist/sessionScanner.js +0 -479
- package/dist/sessionScanner.js.map +0 -1
- package/dist/spawn.d.ts +0 -8
- package/dist/spawn.d.ts.map +0 -1
- package/dist/spawn.js +0 -73
- package/dist/spawn.js.map +0 -1
- package/dist/syncProjects.d.ts +0 -3
- package/dist/syncProjects.d.ts.map +0 -1
- package/dist/syncProjects.js +0 -91
- package/dist/syncProjects.js.map +0 -1
- package/dist/terminalBridge.d.ts +0 -4
- package/dist/terminalBridge.d.ts.map +0 -1
- package/dist/terminalBridge.js +0 -42
- package/dist/terminalBridge.js.map +0 -1
- package/dist/tmux.d.ts +0 -21
- package/dist/tmux.d.ts.map +0 -1
- package/dist/tmux.js +0 -89
- package/dist/tmux.js.map +0 -1
- package/dist/transcriptAdapters/claude-code.d.ts +0 -3
- package/dist/transcriptAdapters/claude-code.d.ts.map +0 -1
- package/dist/transcriptAdapters/claude-code.js +0 -147
- package/dist/transcriptAdapters/claude-code.js.map +0 -1
- package/dist/transcriptAdapters/codex.d.ts +0 -3
- package/dist/transcriptAdapters/codex.d.ts.map +0 -1
- package/dist/transcriptAdapters/codex.js +0 -251
- package/dist/transcriptAdapters/codex.js.map +0 -1
- package/dist/transcriptAdapters/index.d.ts +0 -17
- package/dist/transcriptAdapters/index.d.ts.map +0 -1
- package/dist/transcriptAdapters/index.js +0 -9
- package/dist/transcriptAdapters/index.js.map +0 -1
- package/dist/wsClient.d.ts +0 -6
- package/dist/wsClient.d.ts.map +0 -1
- package/dist/wsClient.js +0 -156
- package/dist/wsClient.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,45 +1,117 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
1
|
+
import {
|
|
2
|
+
mergeCliOverrides,
|
|
3
|
+
parseConfigFile,
|
|
4
|
+
resolveConfigPath,
|
|
5
|
+
startAgent
|
|
6
|
+
} from "./chunk-RJLNNX7L.js";
|
|
7
|
+
|
|
8
|
+
// src/cli.ts
|
|
9
|
+
import { execSync } from "child_process";
|
|
10
|
+
import { createInterface as createInterface2 } from "readline/promises";
|
|
11
|
+
import { platform as platform2 } from "os";
|
|
12
|
+
|
|
13
|
+
// src/init.ts
|
|
14
|
+
import { createInterface } from "readline/promises";
|
|
15
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
16
|
+
import { resolve } from "path";
|
|
17
|
+
import { hostname, platform, homedir } from "os";
|
|
18
|
+
function buildConfigFromAnswers(answers) {
|
|
19
|
+
return {
|
|
20
|
+
deviceId: hostname(),
|
|
21
|
+
deviceName: answers.deviceName || `${hostname()}-${platform()}`,
|
|
22
|
+
backendUrl: answers.backendUrl,
|
|
23
|
+
apiKey: answers.apiKey,
|
|
24
|
+
projects: [],
|
|
25
|
+
projectDirs: answers.projectDirs ? answers.projectDirs.split(",").map((d) => d.trim()).filter(Boolean) : []
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
async function validateBackend(url) {
|
|
29
|
+
try {
|
|
30
|
+
const res = await fetch(`${url}/health`);
|
|
31
|
+
return res.ok;
|
|
32
|
+
} catch {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function runInitWizard() {
|
|
37
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
38
|
+
console.log("\nNo config found. Let's set up your agent.\n");
|
|
39
|
+
try {
|
|
40
|
+
const backendUrl = await rl.question("Backend URL: ");
|
|
41
|
+
if (!backendUrl) {
|
|
42
|
+
throw new Error("Backend URL is required");
|
|
43
|
+
}
|
|
44
|
+
process.stdout.write("Checking backend... ");
|
|
45
|
+
const reachable = await validateBackend(backendUrl);
|
|
46
|
+
if (!reachable) {
|
|
47
|
+
console.log("unreachable (continuing anyway)");
|
|
48
|
+
} else {
|
|
49
|
+
console.log("ok");
|
|
50
|
+
}
|
|
51
|
+
const apiKey = await rl.question("API Key: ");
|
|
52
|
+
if (!apiKey) {
|
|
53
|
+
throw new Error("API Key is required");
|
|
54
|
+
}
|
|
55
|
+
const defaultName = `${hostname()}-${platform()}`;
|
|
56
|
+
const deviceName = await rl.question(`Device name (${defaultName}): `);
|
|
57
|
+
const projectDirs = await rl.question(
|
|
58
|
+
"Project directories (comma-separated, optional): "
|
|
59
|
+
);
|
|
60
|
+
const config = buildConfigFromAnswers({
|
|
61
|
+
backendUrl,
|
|
62
|
+
apiKey,
|
|
63
|
+
deviceName,
|
|
64
|
+
projectDirs
|
|
65
|
+
});
|
|
66
|
+
const configDir = resolve(homedir(), ".agtd");
|
|
67
|
+
const configPath = resolve(configDir, "agent.config.json");
|
|
68
|
+
mkdirSync(configDir, { recursive: true });
|
|
69
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
70
|
+
console.log(`
|
|
71
|
+
Config saved to ${configPath}
|
|
72
|
+
`);
|
|
73
|
+
return config;
|
|
74
|
+
} finally {
|
|
75
|
+
rl.close();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// src/cli.ts
|
|
80
|
+
function parseArgs(argv) {
|
|
81
|
+
const args = { init: false, help: false };
|
|
82
|
+
for (let i = 2; i < argv.length; i++) {
|
|
83
|
+
const arg = argv[i];
|
|
84
|
+
const next = argv[i + 1];
|
|
85
|
+
switch (arg) {
|
|
86
|
+
case "--init":
|
|
87
|
+
args.init = true;
|
|
88
|
+
break;
|
|
89
|
+
case "--backend":
|
|
90
|
+
args.backend = next;
|
|
91
|
+
i++;
|
|
92
|
+
break;
|
|
93
|
+
case "--api-key":
|
|
94
|
+
args.apiKey = next;
|
|
95
|
+
i++;
|
|
96
|
+
break;
|
|
97
|
+
case "--device-name":
|
|
98
|
+
args.deviceName = next;
|
|
99
|
+
i++;
|
|
100
|
+
break;
|
|
101
|
+
case "--config":
|
|
102
|
+
args.configPath = next;
|
|
103
|
+
i++;
|
|
104
|
+
break;
|
|
105
|
+
case "--help":
|
|
106
|
+
case "-h":
|
|
107
|
+
args.help = true;
|
|
108
|
+
break;
|
|
38
109
|
}
|
|
39
|
-
|
|
110
|
+
}
|
|
111
|
+
return args;
|
|
40
112
|
}
|
|
41
113
|
function printHelp() {
|
|
42
|
-
|
|
114
|
+
console.log(`
|
|
43
115
|
Usage: agtd-agent [options]
|
|
44
116
|
|
|
45
117
|
Options:
|
|
@@ -59,92 +131,86 @@ Config is loaded from (in order):
|
|
|
59
131
|
`);
|
|
60
132
|
}
|
|
61
133
|
function hasTmux() {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
134
|
+
try {
|
|
135
|
+
execSync("which tmux", { stdio: "ignore" });
|
|
136
|
+
return true;
|
|
137
|
+
} catch {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
69
140
|
}
|
|
70
141
|
async function ensureTmux() {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
finally {
|
|
94
|
-
rl.close();
|
|
95
|
-
}
|
|
96
|
-
console.log(`Running: ${installCmd}`);
|
|
97
|
-
try {
|
|
98
|
-
execSync(installCmd, { stdio: "inherit" });
|
|
99
|
-
console.log("tmux installed successfully.\n");
|
|
100
|
-
}
|
|
101
|
-
catch {
|
|
102
|
-
console.error("Failed to install tmux. Please install it manually.");
|
|
103
|
-
process.exit(1);
|
|
142
|
+
if (hasTmux()) return;
|
|
143
|
+
const os = platform2();
|
|
144
|
+
let installCmd;
|
|
145
|
+
if (os === "darwin") {
|
|
146
|
+
installCmd = "brew install tmux";
|
|
147
|
+
} else if (os === "linux") {
|
|
148
|
+
installCmd = "sudo apt install -y tmux";
|
|
149
|
+
} else {
|
|
150
|
+
console.error(
|
|
151
|
+
"tmux is required but not installed. Please install it manually."
|
|
152
|
+
);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
156
|
+
try {
|
|
157
|
+
const answer = await rl.question(
|
|
158
|
+
`tmux is required but not installed. Install with "${installCmd}"? (Y/n) `
|
|
159
|
+
);
|
|
160
|
+
if (answer.toLowerCase() === "n") {
|
|
161
|
+
console.error("tmux is required to run the agent.");
|
|
162
|
+
process.exit(1);
|
|
104
163
|
}
|
|
164
|
+
} finally {
|
|
165
|
+
rl.close();
|
|
166
|
+
}
|
|
167
|
+
console.log(`Running: ${installCmd}`);
|
|
168
|
+
try {
|
|
169
|
+
execSync(installCmd, { stdio: "inherit" });
|
|
170
|
+
console.log("tmux installed successfully.\n");
|
|
171
|
+
} catch {
|
|
172
|
+
console.error("Failed to install tmux. Please install it manually.");
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
105
175
|
}
|
|
106
176
|
async function main() {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
// No config, no flags — run wizard
|
|
142
|
-
const config = await runInitWizard();
|
|
143
|
-
await startAgent(config);
|
|
144
|
-
}
|
|
177
|
+
const args = parseArgs(process.argv);
|
|
178
|
+
if (args.help) {
|
|
179
|
+
printHelp();
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
await ensureTmux();
|
|
183
|
+
if (args.init) {
|
|
184
|
+
const config = await runInitWizard();
|
|
185
|
+
await startAgent(config);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const configPath = resolveConfigPath(args.configPath);
|
|
189
|
+
if (configPath) {
|
|
190
|
+
const overrides = {
|
|
191
|
+
backend: args.backend,
|
|
192
|
+
apiKey: args.apiKey,
|
|
193
|
+
deviceName: args.deviceName
|
|
194
|
+
};
|
|
195
|
+
const config = mergeCliOverrides(parseConfigFile(configPath), overrides);
|
|
196
|
+
await startAgent(config);
|
|
197
|
+
} else if (args.backend && args.apiKey) {
|
|
198
|
+
const config = buildConfigFromAnswers({
|
|
199
|
+
backendUrl: args.backend,
|
|
200
|
+
apiKey: args.apiKey,
|
|
201
|
+
deviceName: args.deviceName ?? "",
|
|
202
|
+
projectDirs: ""
|
|
203
|
+
});
|
|
204
|
+
await startAgent(config);
|
|
205
|
+
} else {
|
|
206
|
+
const config = await runInitWizard();
|
|
207
|
+
await startAgent(config);
|
|
208
|
+
}
|
|
145
209
|
}
|
|
146
210
|
main().catch((e) => {
|
|
147
|
-
|
|
148
|
-
|
|
211
|
+
console.error("Agent failed:", e);
|
|
212
|
+
process.exit(1);
|
|
149
213
|
});
|
|
150
|
-
|
|
214
|
+
export {
|
|
215
|
+
parseArgs
|
|
216
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agtd/agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"agtd-agent": "./bin/agtd-agent.js"
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"dev": "tsx watch src/agent.ts",
|
|
16
16
|
"build": "tsc",
|
|
17
17
|
"build:publish": "tsup",
|
|
18
|
+
"prepublishOnly": "tsup",
|
|
18
19
|
"start": "node dist/agent.js",
|
|
19
20
|
"test": "vitest run",
|
|
20
21
|
"test:watch": "vitest"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"codexAdapterFindFile.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/codexAdapterFindFile.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,298 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Integration tests for codexAdapter.findFile() — the three-tier lookup:
|
|
3
|
-
*
|
|
4
|
-
* Tier 1: stored codexThreadId → exact match, unique per session
|
|
5
|
-
* Tier 2: tmux session creation time → used when codexThreadId absent
|
|
6
|
-
* Tier 3: latest thread by cwd → last resort, may collide if cwd shared
|
|
7
|
-
*
|
|
8
|
-
* node:child_process is mocked so these tests run without sqlite3 CLI or tmux.
|
|
9
|
-
*/
|
|
10
|
-
import { describe, it, expect, beforeAll, afterAll, beforeEach, vi } from "vitest";
|
|
11
|
-
import { writeFileSync, mkdirSync, rmSync } from "node:fs";
|
|
12
|
-
import { join } from "node:path";
|
|
13
|
-
import { tmpdir } from "node:os";
|
|
14
|
-
vi.mock("node:child_process", () => ({
|
|
15
|
-
execSync: vi.fn(),
|
|
16
|
-
}));
|
|
17
|
-
// Import AFTER vi.mock so the adapter picks up the mocked execSync
|
|
18
|
-
import { codexAdapter } from "../transcriptAdapters/codex.js";
|
|
19
|
-
import { execSync } from "node:child_process";
|
|
20
|
-
const mockExecSync = vi.mocked(execSync);
|
|
21
|
-
// ---------------------------------------------------------------------------
|
|
22
|
-
// Fixture setup
|
|
23
|
-
// ---------------------------------------------------------------------------
|
|
24
|
-
const THREAD_ID_1 = "01234567-0000-7000-0000-000000000001";
|
|
25
|
-
const THREAD_ID_2 = "01234567-0000-7000-0000-000000000002";
|
|
26
|
-
const SHARED_CWD = "/projects/shared-project";
|
|
27
|
-
const TMUX_SESSION_1 = "aidash-sess_aaa11111";
|
|
28
|
-
const TMUX_SESSION_2 = "aidash-sess_bbb22222";
|
|
29
|
-
const TMUX_CREATED_1 = "1700000000"; // earlier session
|
|
30
|
-
const TMUX_CREATED_2 = "1700000300"; // later session (5 min later)
|
|
31
|
-
let tmpDir;
|
|
32
|
-
let jsonlPath1; // rollout file for thread 1
|
|
33
|
-
let jsonlPath2; // rollout file for thread 2
|
|
34
|
-
beforeAll(() => {
|
|
35
|
-
tmpDir = join(tmpdir(), "agtd-codex-findfile-" + Date.now());
|
|
36
|
-
mkdirSync(join(tmpDir, ".codex"), { recursive: true });
|
|
37
|
-
// A zero-byte file is enough — existsSync(dbPath) just needs to pass
|
|
38
|
-
writeFileSync(join(tmpDir, ".codex", "state_5.sqlite"), "");
|
|
39
|
-
// Create real JSONL files so existsSync(rolloutPath) passes
|
|
40
|
-
jsonlPath1 = join(tmpDir, "rollout-thread1.jsonl");
|
|
41
|
-
jsonlPath2 = join(tmpDir, "rollout-thread2.jsonl");
|
|
42
|
-
writeFileSync(jsonlPath1, JSON.stringify({
|
|
43
|
-
type: "response_item",
|
|
44
|
-
timestamp: "2026-01-01T00:00:00Z",
|
|
45
|
-
payload: {
|
|
46
|
-
type: "message",
|
|
47
|
-
role: "user",
|
|
48
|
-
content: [{ type: "text", text: "session 1 message" }],
|
|
49
|
-
},
|
|
50
|
-
}) + "\n");
|
|
51
|
-
writeFileSync(jsonlPath2, JSON.stringify({
|
|
52
|
-
type: "response_item",
|
|
53
|
-
timestamp: "2026-01-02T00:00:00Z",
|
|
54
|
-
payload: {
|
|
55
|
-
type: "message",
|
|
56
|
-
role: "user",
|
|
57
|
-
content: [{ type: "text", text: "session 2 message" }],
|
|
58
|
-
},
|
|
59
|
-
}) + "\n");
|
|
60
|
-
});
|
|
61
|
-
afterAll(() => {
|
|
62
|
-
rmSync(tmpDir, { recursive: true, force: true });
|
|
63
|
-
});
|
|
64
|
-
beforeEach(() => {
|
|
65
|
-
mockExecSync.mockReset();
|
|
66
|
-
});
|
|
67
|
-
// ---------------------------------------------------------------------------
|
|
68
|
-
// Tier 1 tests — codexThreadId direct lookup
|
|
69
|
-
// ---------------------------------------------------------------------------
|
|
70
|
-
describe("Tier 1: codexThreadId lookup", () => {
|
|
71
|
-
it("returns the rollout file matching the stored codexThreadId", () => {
|
|
72
|
-
mockExecSync.mockReturnValueOnce((jsonlPath1 + "\n"));
|
|
73
|
-
const session = {
|
|
74
|
-
id: "sess_1",
|
|
75
|
-
cwd: SHARED_CWD,
|
|
76
|
-
agentType: "codex",
|
|
77
|
-
codexThreadId: THREAD_ID_1,
|
|
78
|
-
tmuxSession: TMUX_SESSION_1,
|
|
79
|
-
};
|
|
80
|
-
const result = codexAdapter.findFile(session, tmpDir);
|
|
81
|
-
expect(result).toBe(jsonlPath1);
|
|
82
|
-
// Must query by thread ID, not by cwd
|
|
83
|
-
expect(mockExecSync).toHaveBeenCalledWith(expect.stringContaining(THREAD_ID_1), expect.any(Object));
|
|
84
|
-
});
|
|
85
|
-
it("two sessions with the SAME cwd get DIFFERENT transcripts via codexThreadId", () => {
|
|
86
|
-
// Session 1 lookup
|
|
87
|
-
mockExecSync.mockReturnValueOnce((jsonlPath1 + "\n"));
|
|
88
|
-
const session1 = {
|
|
89
|
-
id: "sess_1",
|
|
90
|
-
cwd: SHARED_CWD,
|
|
91
|
-
codexThreadId: THREAD_ID_1,
|
|
92
|
-
};
|
|
93
|
-
const file1 = codexAdapter.findFile(session1, tmpDir);
|
|
94
|
-
mockExecSync.mockReset();
|
|
95
|
-
// Session 2 lookup
|
|
96
|
-
mockExecSync.mockReturnValueOnce((jsonlPath2 + "\n"));
|
|
97
|
-
const session2 = {
|
|
98
|
-
id: "sess_2",
|
|
99
|
-
cwd: SHARED_CWD,
|
|
100
|
-
codexThreadId: THREAD_ID_2,
|
|
101
|
-
};
|
|
102
|
-
const file2 = codexAdapter.findFile(session2, tmpDir);
|
|
103
|
-
expect(file1).toBe(jsonlPath1);
|
|
104
|
-
expect(file2).toBe(jsonlPath2);
|
|
105
|
-
// KEY regression assertion: same cwd must not produce same transcript
|
|
106
|
-
expect(file1).not.toBe(file2);
|
|
107
|
-
});
|
|
108
|
-
it("falls through to Tier 2 when Tier 1 returns a non-existent path", () => {
|
|
109
|
-
// Tier 1: returns a path that does not exist on disk
|
|
110
|
-
mockExecSync
|
|
111
|
-
.mockReturnValueOnce(("/nonexistent/rollout.jsonl\n")) // Tier 1 sqlite3
|
|
112
|
-
.mockReturnValueOnce((TMUX_CREATED_1 + "\n")) // Tier 2: tmux display-message
|
|
113
|
-
.mockReturnValueOnce((jsonlPath1 + "\n")); // Tier 2: sqlite3 by timestamp
|
|
114
|
-
const session = {
|
|
115
|
-
id: "sess_1",
|
|
116
|
-
cwd: SHARED_CWD,
|
|
117
|
-
codexThreadId: THREAD_ID_1,
|
|
118
|
-
tmuxSession: TMUX_SESSION_1,
|
|
119
|
-
};
|
|
120
|
-
const result = codexAdapter.findFile(session, tmpDir);
|
|
121
|
-
expect(result).toBe(jsonlPath1);
|
|
122
|
-
});
|
|
123
|
-
it("falls through to Tier 3 when codexThreadId returns empty string", () => {
|
|
124
|
-
// Tier 1: sqlite3 returns empty (thread not found)
|
|
125
|
-
// Tier 2: no tmuxSession → skipped
|
|
126
|
-
// Tier 3: returns latest by cwd
|
|
127
|
-
mockExecSync
|
|
128
|
-
.mockReturnValueOnce(("")) // Tier 1: empty
|
|
129
|
-
.mockReturnValueOnce((jsonlPath2 + "\n")); // Tier 3: latest by cwd
|
|
130
|
-
const session = {
|
|
131
|
-
id: "sess_x",
|
|
132
|
-
cwd: SHARED_CWD,
|
|
133
|
-
codexThreadId: THREAD_ID_1,
|
|
134
|
-
tmuxSession: null, // no tmux → skip Tier 2
|
|
135
|
-
};
|
|
136
|
-
const result = codexAdapter.findFile(session, tmpDir);
|
|
137
|
-
expect(result).toBe(jsonlPath2);
|
|
138
|
-
expect(mockExecSync).toHaveBeenCalledWith(expect.stringContaining("ORDER BY updated_at DESC"), expect.any(Object));
|
|
139
|
-
});
|
|
140
|
-
});
|
|
141
|
-
// ---------------------------------------------------------------------------
|
|
142
|
-
// Tier 2 tests — tmux session creation time
|
|
143
|
-
// ---------------------------------------------------------------------------
|
|
144
|
-
describe("Tier 2: tmux session creation time lookup", () => {
|
|
145
|
-
it("uses tmux creation time when codexThreadId is null", () => {
|
|
146
|
-
mockExecSync
|
|
147
|
-
.mockReturnValueOnce((TMUX_CREATED_1 + "\n")) // tmux display-message
|
|
148
|
-
.mockReturnValueOnce((jsonlPath1 + "\n")); // sqlite3 WHERE created_at >=
|
|
149
|
-
const session = {
|
|
150
|
-
id: "sess_3",
|
|
151
|
-
cwd: SHARED_CWD,
|
|
152
|
-
codexThreadId: null,
|
|
153
|
-
tmuxSession: TMUX_SESSION_1,
|
|
154
|
-
};
|
|
155
|
-
const result = codexAdapter.findFile(session, tmpDir);
|
|
156
|
-
expect(result).toBe(jsonlPath1);
|
|
157
|
-
expect(mockExecSync).toHaveBeenCalledWith(expect.stringContaining(`tmux display-message -t '${TMUX_SESSION_1}'`), expect.any(Object));
|
|
158
|
-
expect(mockExecSync).toHaveBeenCalledWith(expect.stringContaining(`created_at >= ${TMUX_CREATED_1}`), expect.any(Object));
|
|
159
|
-
});
|
|
160
|
-
it("two sessions sharing cwd get correct files via different tmux creation times", () => {
|
|
161
|
-
// Session 1: earlier tmux time → finds thread 1
|
|
162
|
-
mockExecSync
|
|
163
|
-
.mockReturnValueOnce((TMUX_CREATED_1 + "\n"))
|
|
164
|
-
.mockReturnValueOnce((jsonlPath1 + "\n"));
|
|
165
|
-
const session1 = {
|
|
166
|
-
id: "sess_3",
|
|
167
|
-
cwd: SHARED_CWD,
|
|
168
|
-
codexThreadId: null,
|
|
169
|
-
tmuxSession: TMUX_SESSION_1,
|
|
170
|
-
};
|
|
171
|
-
const file1 = codexAdapter.findFile(session1, tmpDir);
|
|
172
|
-
expect(file1).toBe(jsonlPath1);
|
|
173
|
-
mockExecSync.mockReset();
|
|
174
|
-
// Session 2: later tmux time → finds thread 2
|
|
175
|
-
mockExecSync
|
|
176
|
-
.mockReturnValueOnce((TMUX_CREATED_2 + "\n"))
|
|
177
|
-
.mockReturnValueOnce((jsonlPath2 + "\n"));
|
|
178
|
-
const session2 = {
|
|
179
|
-
id: "sess_4",
|
|
180
|
-
cwd: SHARED_CWD,
|
|
181
|
-
codexThreadId: null,
|
|
182
|
-
tmuxSession: TMUX_SESSION_2,
|
|
183
|
-
};
|
|
184
|
-
const file2 = codexAdapter.findFile(session2, tmpDir);
|
|
185
|
-
expect(file2).toBe(jsonlPath2);
|
|
186
|
-
expect(file1).not.toBe(file2);
|
|
187
|
-
});
|
|
188
|
-
it("falls through to Tier 3 when tmux command fails", () => {
|
|
189
|
-
mockExecSync
|
|
190
|
-
.mockImplementationOnce(() => {
|
|
191
|
-
throw new Error("no server running on /tmp/tmux/default");
|
|
192
|
-
})
|
|
193
|
-
.mockReturnValueOnce((jsonlPath2 + "\n")); // Tier 3 fallback
|
|
194
|
-
const session = {
|
|
195
|
-
id: "sess_5",
|
|
196
|
-
cwd: SHARED_CWD,
|
|
197
|
-
codexThreadId: null,
|
|
198
|
-
tmuxSession: TMUX_SESSION_1,
|
|
199
|
-
};
|
|
200
|
-
const result = codexAdapter.findFile(session, tmpDir);
|
|
201
|
-
expect(result).toBe(jsonlPath2);
|
|
202
|
-
expect(mockExecSync).toHaveBeenCalledWith(expect.stringContaining("ORDER BY updated_at DESC"), expect.any(Object));
|
|
203
|
-
});
|
|
204
|
-
it("falls through to Tier 3 when tmux returns non-numeric output", () => {
|
|
205
|
-
mockExecSync
|
|
206
|
-
.mockReturnValueOnce(("not-a-number\n")) // tmux returns garbage
|
|
207
|
-
.mockReturnValueOnce((jsonlPath2 + "\n")); // Tier 3
|
|
208
|
-
const session = {
|
|
209
|
-
id: "sess_6",
|
|
210
|
-
cwd: SHARED_CWD,
|
|
211
|
-
codexThreadId: null,
|
|
212
|
-
tmuxSession: TMUX_SESSION_1,
|
|
213
|
-
};
|
|
214
|
-
const result = codexAdapter.findFile(session, tmpDir);
|
|
215
|
-
expect(result).toBe(jsonlPath2);
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
// ---------------------------------------------------------------------------
|
|
219
|
-
// Tier 3 tests — latest thread by cwd (last resort)
|
|
220
|
-
// ---------------------------------------------------------------------------
|
|
221
|
-
describe("Tier 3: latest thread by cwd (last resort)", () => {
|
|
222
|
-
it("returns latest updated thread when no codexThreadId and no tmuxSession", () => {
|
|
223
|
-
mockExecSync.mockReturnValueOnce((jsonlPath2 + "\n"));
|
|
224
|
-
const session = {
|
|
225
|
-
id: "sess_7",
|
|
226
|
-
cwd: SHARED_CWD,
|
|
227
|
-
codexThreadId: null,
|
|
228
|
-
tmuxSession: null,
|
|
229
|
-
};
|
|
230
|
-
const result = codexAdapter.findFile(session, tmpDir);
|
|
231
|
-
expect(result).toBe(jsonlPath2);
|
|
232
|
-
expect(mockExecSync).toHaveBeenCalledWith(expect.stringContaining("ORDER BY updated_at DESC LIMIT 1"), expect.any(Object));
|
|
233
|
-
});
|
|
234
|
-
it("WARNING: returns same file for two sessions with same cwd (Tier 3 limitation)", () => {
|
|
235
|
-
// This test documents the known limitation of Tier 3 — it cannot disambiguate
|
|
236
|
-
// sessions sharing the same cwd without codexThreadId or tmuxSession.
|
|
237
|
-
// Tier 1 and Tier 2 exist to avoid hitting this case.
|
|
238
|
-
const session1 = { id: "sess_8", cwd: SHARED_CWD, codexThreadId: null, tmuxSession: null };
|
|
239
|
-
const session2 = { id: "sess_9", cwd: SHARED_CWD, codexThreadId: null, tmuxSession: null };
|
|
240
|
-
mockExecSync.mockReturnValueOnce((jsonlPath2 + "\n"));
|
|
241
|
-
const file1 = codexAdapter.findFile(session1, tmpDir);
|
|
242
|
-
mockExecSync.mockReset();
|
|
243
|
-
mockExecSync.mockReturnValueOnce((jsonlPath2 + "\n"));
|
|
244
|
-
const file2 = codexAdapter.findFile(session2, tmpDir);
|
|
245
|
-
// Both get the same (latest) file — this is the documented limitation
|
|
246
|
-
expect(file1).toBe(jsonlPath2);
|
|
247
|
-
expect(file2).toBe(jsonlPath2);
|
|
248
|
-
expect(file1).toBe(file2); // expected collision — document this behavior
|
|
249
|
-
});
|
|
250
|
-
it("returns null when cwd is empty", () => {
|
|
251
|
-
// No SQLite DB fallback and no sessions dir
|
|
252
|
-
const session = {
|
|
253
|
-
id: "sess_10",
|
|
254
|
-
cwd: "",
|
|
255
|
-
codexThreadId: null,
|
|
256
|
-
tmuxSession: null,
|
|
257
|
-
};
|
|
258
|
-
const result = codexAdapter.findFile(session, join(tmpDir, "no-sessions-dir"));
|
|
259
|
-
expect(result).toBeNull();
|
|
260
|
-
});
|
|
261
|
-
});
|
|
262
|
-
// ---------------------------------------------------------------------------
|
|
263
|
-
// SQL injection / quoting edge cases
|
|
264
|
-
// ---------------------------------------------------------------------------
|
|
265
|
-
describe("shell quoting — paths with special characters", () => {
|
|
266
|
-
it("handles cwd containing single quotes in SQLite query", () => {
|
|
267
|
-
// This is the exact bug that broke Tier 2 before the fix:
|
|
268
|
-
// using single-quote outer wrapping with a cwd like /Users/foo/it's-project
|
|
269
|
-
// broke because the shell interpreter saw the inner ' as ending the argument.
|
|
270
|
-
mockExecSync.mockReturnValueOnce((jsonlPath1 + "\n"));
|
|
271
|
-
const session = {
|
|
272
|
-
id: "sess_sq",
|
|
273
|
-
cwd: "/projects/it's-project",
|
|
274
|
-
codexThreadId: THREAD_ID_1,
|
|
275
|
-
tmuxSession: null,
|
|
276
|
-
};
|
|
277
|
-
// Should not throw; the quoting logic must handle single quotes in paths
|
|
278
|
-
expect(() => codexAdapter.findFile(session, tmpDir)).not.toThrow();
|
|
279
|
-
});
|
|
280
|
-
it("Tier 3 CWD query uses double-quote outer wrapping (regression: near '/' syntax error)", () => {
|
|
281
|
-
// The original bug: sqlite3 'db' 'WHERE cwd='/Users/...' was broken because
|
|
282
|
-
// the shell split on / in the path. Fixed by using "double-quote wrapping".
|
|
283
|
-
// Verify the query string passed to execSync uses the correct quoting.
|
|
284
|
-
mockExecSync.mockReturnValueOnce((jsonlPath1 + "\n"));
|
|
285
|
-
const session = {
|
|
286
|
-
id: "sess_dq",
|
|
287
|
-
cwd: "/Users/robyparapat/workspace/repo/payment-connectors-service",
|
|
288
|
-
codexThreadId: null,
|
|
289
|
-
tmuxSession: null,
|
|
290
|
-
};
|
|
291
|
-
codexAdapter.findFile(session, tmpDir);
|
|
292
|
-
// The execSync call must use double quotes as outer wrapper for the SQL argument
|
|
293
|
-
// so the cwd single quotes are treated as literal characters, not shell delimiters
|
|
294
|
-
const call = mockExecSync.mock.calls[0][0];
|
|
295
|
-
expect(call).toMatch(/sqlite3 '.*' "SELECT rollout_path/);
|
|
296
|
-
});
|
|
297
|
-
});
|
|
298
|
-
//# sourceMappingURL=codexAdapterFindFile.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"codexAdapterFindFile.test.js","sourceRoot":"","sources":["../../src/__tests__/codexAdapterFindFile.test.ts"],"names":[],"mappings":"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"}
|