@agtd/agent 0.1.1 → 0.1.2
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/__tests__/codexAdapterFindFile.test.d.ts +2 -0
- package/dist/__tests__/codexAdapterFindFile.test.d.ts.map +1 -0
- package/dist/__tests__/codexAdapterFindFile.test.js +298 -0
- package/dist/__tests__/codexAdapterFindFile.test.js.map +1 -0
- package/dist/__tests__/enrichers.test.d.ts +2 -0
- package/dist/__tests__/enrichers.test.d.ts.map +1 -0
- package/dist/__tests__/enrichers.test.js +47 -0
- package/dist/__tests__/enrichers.test.js.map +1 -0
- package/dist/__tests__/tmux.integration.test.d.ts +2 -0
- package/dist/__tests__/tmux.integration.test.d.ts.map +1 -0
- package/dist/__tests__/tmux.integration.test.js +112 -0
- package/dist/__tests__/tmux.integration.test.js.map +1 -0
- package/dist/__tests__/tmux.test.d.ts +2 -0
- package/dist/__tests__/tmux.test.d.ts.map +1 -0
- package/dist/__tests__/tmux.test.js +26 -0
- package/dist/__tests__/tmux.test.js.map +1 -0
- package/dist/__tests__/transcriptAdapters.test.d.ts +2 -0
- package/dist/__tests__/transcriptAdapters.test.d.ts.map +1 -0
- package/dist/__tests__/transcriptAdapters.test.js +133 -0
- package/dist/__tests__/transcriptAdapters.test.js.map +1 -0
- package/dist/adapters/claude-code.d.ts +3 -0
- package/dist/adapters/claude-code.d.ts.map +1 -0
- package/dist/adapters/claude-code.js +48 -0
- package/dist/adapters/claude-code.js.map +1 -0
- package/dist/adapters/codex.d.ts +3 -0
- package/dist/adapters/codex.d.ts.map +1 -0
- package/dist/adapters/codex.js +33 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/generic.d.ts +3 -0
- package/dist/adapters/generic.d.ts.map +1 -0
- package/dist/adapters/generic.js +38 -0
- package/dist/adapters/generic.js.map +1 -0
- package/dist/adapters/index.d.ts +7 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +12 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/agent.d.ts +3 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +49 -6
- package/dist/agent.js.map +1 -0
- package/dist/cli.d.ts +11 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +120 -186
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +26 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +48 -0
- package/dist/config.js.map +1 -0
- package/dist/enrichers/git.d.ts +6 -0
- package/dist/enrichers/git.d.ts.map +1 -0
- package/dist/enrichers/git.js +20 -0
- package/dist/enrichers/git.js.map +1 -0
- package/dist/enrichers/pr.d.ts +2 -0
- package/dist/enrichers/pr.d.ts.map +1 -0
- package/dist/enrichers/pr.js +10 -0
- package/dist/enrichers/pr.js.map +1 -0
- package/dist/enrichers/transcript.d.ts +8 -0
- package/dist/enrichers/transcript.d.ts.map +1 -0
- package/dist/enrichers/transcript.js +33 -0
- package/dist/enrichers/transcript.js.map +1 -0
- package/dist/heartbeat.d.ts +4 -0
- package/dist/heartbeat.d.ts.map +1 -0
- package/dist/heartbeat.js +15 -0
- package/dist/heartbeat.js.map +1 -0
- package/dist/init.d.ts +10 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +70 -0
- package/dist/init.js.map +1 -0
- package/dist/register.d.ts +3 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +22 -0
- package/dist/register.js.map +1 -0
- package/dist/sessionScanner.d.ts +20 -0
- package/dist/sessionScanner.d.ts.map +1 -0
- package/dist/sessionScanner.js +479 -0
- package/dist/sessionScanner.js.map +1 -0
- package/dist/spawn.d.ts +8 -0
- package/dist/spawn.d.ts.map +1 -0
- package/dist/spawn.js +73 -0
- package/dist/spawn.js.map +1 -0
- package/dist/syncProjects.d.ts +3 -0
- package/dist/syncProjects.d.ts.map +1 -0
- package/dist/syncProjects.js +91 -0
- package/dist/syncProjects.js.map +1 -0
- package/dist/terminalBridge.d.ts +4 -0
- package/dist/terminalBridge.d.ts.map +1 -0
- package/dist/terminalBridge.js +42 -0
- package/dist/terminalBridge.js.map +1 -0
- package/dist/tmux.d.ts +21 -0
- package/dist/tmux.d.ts.map +1 -0
- package/dist/tmux.js +89 -0
- package/dist/tmux.js.map +1 -0
- package/dist/transcriptAdapters/claude-code.d.ts +3 -0
- package/dist/transcriptAdapters/claude-code.d.ts.map +1 -0
- package/dist/transcriptAdapters/claude-code.js +147 -0
- package/dist/transcriptAdapters/claude-code.js.map +1 -0
- package/dist/transcriptAdapters/codex.d.ts +3 -0
- package/dist/transcriptAdapters/codex.d.ts.map +1 -0
- package/dist/transcriptAdapters/codex.js +251 -0
- package/dist/transcriptAdapters/codex.js.map +1 -0
- package/dist/transcriptAdapters/index.d.ts +17 -0
- package/dist/transcriptAdapters/index.d.ts.map +1 -0
- package/dist/transcriptAdapters/index.js +9 -0
- package/dist/transcriptAdapters/index.js.map +1 -0
- package/dist/wsClient.d.ts +6 -0
- package/dist/wsClient.d.ts.map +1 -0
- package/dist/wsClient.js +156 -0
- package/dist/wsClient.js.map +1 -0
- package/package.json +6 -2
package/dist/init.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,wBAAsB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBvE"}
|
package/dist/register.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { hostname, platform } from "node:os";
|
|
2
|
+
export async function registerDevice(config) {
|
|
3
|
+
const payload = {
|
|
4
|
+
id: config.deviceId,
|
|
5
|
+
name: config.deviceName,
|
|
6
|
+
host: hostname(),
|
|
7
|
+
os: platform(),
|
|
8
|
+
};
|
|
9
|
+
const res = await fetch(`${config.backendUrl}/api/register-device`, {
|
|
10
|
+
method: "POST",
|
|
11
|
+
headers: {
|
|
12
|
+
"Content-Type": "application/json",
|
|
13
|
+
"x-api-key": config.apiKey,
|
|
14
|
+
},
|
|
15
|
+
body: JSON.stringify(payload),
|
|
16
|
+
});
|
|
17
|
+
if (!res.ok) {
|
|
18
|
+
throw new Error(`Register device failed: ${res.status} ${res.statusText}`);
|
|
19
|
+
}
|
|
20
|
+
console.log(`Device registered: ${config.deviceId}`);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=register.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../src/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAI7C,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,MAAmB;IACtD,MAAM,OAAO,GAA0B;QACrC,EAAE,EAAE,MAAM,CAAC,QAAQ;QACnB,IAAI,EAAE,MAAM,CAAC,UAAU;QACvB,IAAI,EAAE,QAAQ,EAAE;QAChB,EAAE,EAAE,QAAQ,EAAE;KACf,CAAC;IAEF,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,UAAU,sBAAsB,EAAE;QAClE,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,WAAW,EAAE,MAAM,CAAC,MAAM;SAC3B;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;KAC9B,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,sBAAsB,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AgentConfig } from "./config.js";
|
|
2
|
+
interface DiscoveredSession {
|
|
3
|
+
sessionId: string;
|
|
4
|
+
project: string;
|
|
5
|
+
cwd: string;
|
|
6
|
+
branch: string;
|
|
7
|
+
model: string;
|
|
8
|
+
status: "working" | "idle" | "awaiting_permission";
|
|
9
|
+
agentType: string;
|
|
10
|
+
pid: number;
|
|
11
|
+
tmuxSession: string;
|
|
12
|
+
}
|
|
13
|
+
/** Discover all agent sessions (Claude Code + Codex) */
|
|
14
|
+
export declare function discoverAllSessions(): DiscoveredSession[];
|
|
15
|
+
export declare const discoverClaudeSessionsAll: typeof discoverAllSessions;
|
|
16
|
+
/** Post heartbeats for all discovered sessions */
|
|
17
|
+
export declare function scanAndReportSessions(config: AgentConfig): Promise<void>;
|
|
18
|
+
export declare function startSessionScanner(config: AgentConfig): NodeJS.Timeout;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=sessionScanner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionScanner.d.ts","sourceRoot":"","sources":["../src/sessionScanner.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,UAAU,iBAAiB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,SAAS,GAAG,MAAM,GAAG,qBAAqB,CAAC;IACnD,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,MAAM,CAAC;CACrB;AAqeD,wDAAwD;AACxD,wBAAgB,mBAAmB,IAAI,iBAAiB,EAAE,CAOzD;AAGD,eAAO,MAAM,yBAAyB,4BAAsB,CAAC;AAE7D,kDAAkD;AAClD,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,IAAI,CAAC,CAoCf;AAID,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,OAAO,CAMvE"}
|
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { statSync, readdirSync, existsSync, openSync, readSync, closeSync, } from "node:fs";
|
|
3
|
+
import { join, basename } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { getGitInfo } from "./enrichers/git.js";
|
|
6
|
+
/** Map of PID -> tmux session name for all tmux pane processes */
|
|
7
|
+
function buildTmuxPidMap() {
|
|
8
|
+
const map = new Map();
|
|
9
|
+
try {
|
|
10
|
+
const output = execSync("tmux list-panes -a -F '#{pane_pid} #{session_name}' 2>/dev/null", { encoding: "utf-8", timeout: 3000, stdio: "pipe" }).trim();
|
|
11
|
+
for (const line of output.split("\n")) {
|
|
12
|
+
if (!line)
|
|
13
|
+
continue;
|
|
14
|
+
const spaceIdx = line.indexOf(" ");
|
|
15
|
+
if (spaceIdx === -1)
|
|
16
|
+
continue;
|
|
17
|
+
const pid = parseInt(line.substring(0, spaceIdx), 10);
|
|
18
|
+
const sessionName = line.substring(spaceIdx + 1);
|
|
19
|
+
if (!isNaN(pid) && sessionName) {
|
|
20
|
+
map.set(pid, sessionName);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// tmux not running or not available
|
|
26
|
+
}
|
|
27
|
+
return map;
|
|
28
|
+
}
|
|
29
|
+
/** Find which tmux session a process is running in (checks parent chain) */
|
|
30
|
+
function findTmuxSessionForPid(pid, tmuxPidMap) {
|
|
31
|
+
if (tmuxPidMap.size === 0)
|
|
32
|
+
return "";
|
|
33
|
+
// Check if this PID or any ancestor is a tmux pane process
|
|
34
|
+
let currentPid = pid;
|
|
35
|
+
const maxDepth = 10;
|
|
36
|
+
for (let i = 0; i < maxDepth; i++) {
|
|
37
|
+
const session = tmuxPidMap.get(currentPid);
|
|
38
|
+
if (session)
|
|
39
|
+
return session;
|
|
40
|
+
// Get parent PID
|
|
41
|
+
try {
|
|
42
|
+
const ppid = parseInt(execSync(`ps -p ${currentPid} -o ppid=`, {
|
|
43
|
+
encoding: "utf-8",
|
|
44
|
+
timeout: 2000,
|
|
45
|
+
stdio: "pipe",
|
|
46
|
+
}).trim(), 10);
|
|
47
|
+
if (isNaN(ppid) || ppid <= 1 || ppid === currentPid)
|
|
48
|
+
break;
|
|
49
|
+
currentPid = ppid;
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return "";
|
|
56
|
+
}
|
|
57
|
+
/** Convert a path to Claude's project directory name format */
|
|
58
|
+
function pathToProjectDir(p) {
|
|
59
|
+
return p.replace(/\//g, "-");
|
|
60
|
+
}
|
|
61
|
+
/** Get the CWD of a process by PID (macOS) */
|
|
62
|
+
function getProcessCwd(pid) {
|
|
63
|
+
try {
|
|
64
|
+
const output = execSync(`lsof -a -p ${pid} -d cwd -Fn 2>/dev/null | grep '^n'`, { encoding: "utf-8", timeout: 3000, stdio: "pipe" }).trim();
|
|
65
|
+
const match = output.match(/^n(.+)$/m);
|
|
66
|
+
return match ? match[1] : null;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Find all running agent processes (Claude Code + Codex) */
|
|
73
|
+
function findAgentProcesses() {
|
|
74
|
+
const processes = [];
|
|
75
|
+
try {
|
|
76
|
+
const psOutput = execSync("ps aux", {
|
|
77
|
+
encoding: "utf-8",
|
|
78
|
+
timeout: 5000,
|
|
79
|
+
stdio: "pipe",
|
|
80
|
+
});
|
|
81
|
+
for (const line of psOutput.split("\n")) {
|
|
82
|
+
const parts = line.trim().split(/\s+/);
|
|
83
|
+
const pid = parseInt(parts[1], 10);
|
|
84
|
+
if (isNaN(pid) || pid === process.pid)
|
|
85
|
+
continue;
|
|
86
|
+
// --- Claude Code ---
|
|
87
|
+
if ((line.includes("/claude") || line.match(/\sclaude(\s|$)/)) &&
|
|
88
|
+
!line.includes("grep") &&
|
|
89
|
+
!line.includes("codex")) {
|
|
90
|
+
let sessionId = null;
|
|
91
|
+
const resumeMatch = line.match(/--resume\s+([a-f0-9-]{36})/);
|
|
92
|
+
const sessionMatch = line.match(/--session-id\s+([a-f0-9-]{36})/);
|
|
93
|
+
if (resumeMatch)
|
|
94
|
+
sessionId = resumeMatch[1];
|
|
95
|
+
else if (sessionMatch)
|
|
96
|
+
sessionId = sessionMatch[1];
|
|
97
|
+
let model = "default";
|
|
98
|
+
const modelMatch = line.match(/--model\s+(\S+)/);
|
|
99
|
+
if (modelMatch)
|
|
100
|
+
model = modelMatch[1];
|
|
101
|
+
const cwd = getProcessCwd(pid);
|
|
102
|
+
processes.push({
|
|
103
|
+
pid,
|
|
104
|
+
agentType: "claude-code",
|
|
105
|
+
sessionId,
|
|
106
|
+
model,
|
|
107
|
+
cwd,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
// --- Codex (terminal CLI only, not app-server) ---
|
|
111
|
+
if (line.match(/\scodex\s*$/) || // bare "codex" at end of line (terminal)
|
|
112
|
+
line.match(/\scodex\s+(?!app-server)/) // codex with args but not app-server
|
|
113
|
+
) {
|
|
114
|
+
const cwd = getProcessCwd(pid);
|
|
115
|
+
// Codex doesn't expose session ID in process args easily
|
|
116
|
+
// Use PID-based session ID for uniqueness
|
|
117
|
+
const sessionId = `codex-${pid}`;
|
|
118
|
+
processes.push({
|
|
119
|
+
pid,
|
|
120
|
+
agentType: "codex",
|
|
121
|
+
sessionId,
|
|
122
|
+
model: "codex",
|
|
123
|
+
cwd,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// ps failed
|
|
130
|
+
}
|
|
131
|
+
return processes;
|
|
132
|
+
}
|
|
133
|
+
/** Find the most recently modified session file in a Claude project directory */
|
|
134
|
+
function findLatestSessionFile(projPath) {
|
|
135
|
+
try {
|
|
136
|
+
const files = readdirSync(projPath).filter((f) => f.endsWith(".jsonl"));
|
|
137
|
+
let latest = null;
|
|
138
|
+
for (const file of files) {
|
|
139
|
+
const filePath = join(projPath, file);
|
|
140
|
+
try {
|
|
141
|
+
const mtime = statSync(filePath).mtimeMs;
|
|
142
|
+
if (!latest || mtime > latest.mtimeMs) {
|
|
143
|
+
latest = { sessionId: basename(file, ".jsonl"), mtimeMs: mtime };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return latest;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/** Check if a Claude session is waiting for user permission by reading the last JSONL entry.
|
|
157
|
+
* Only reads the tail of the file (~4KB) to avoid loading multi-MB transcripts. */
|
|
158
|
+
function isAwaitingPermissionFromJsonl(projPath, sessionId) {
|
|
159
|
+
try {
|
|
160
|
+
const filePath = join(projPath, `${sessionId}.jsonl`);
|
|
161
|
+
const size = statSync(filePath).size;
|
|
162
|
+
if (size === 0)
|
|
163
|
+
return false;
|
|
164
|
+
// Read only the last 4KB — enough for the last JSONL entry
|
|
165
|
+
const TAIL_BYTES = 4096;
|
|
166
|
+
const start = Math.max(0, size - TAIL_BYTES);
|
|
167
|
+
const buf = Buffer.alloc(Math.min(TAIL_BYTES, size));
|
|
168
|
+
const fd = openSync(filePath, "r");
|
|
169
|
+
try {
|
|
170
|
+
readSync(fd, buf, 0, buf.length, start);
|
|
171
|
+
}
|
|
172
|
+
finally {
|
|
173
|
+
closeSync(fd);
|
|
174
|
+
}
|
|
175
|
+
const tail = buf.toString("utf-8");
|
|
176
|
+
const lines = tail.trimEnd().split("\n");
|
|
177
|
+
// If we sliced mid-line, the last complete line is still what we want
|
|
178
|
+
const lastLine = lines[lines.length - 1];
|
|
179
|
+
if (!lastLine)
|
|
180
|
+
return false;
|
|
181
|
+
const record = JSON.parse(lastLine);
|
|
182
|
+
// If the last entry is an assistant message that stopped because it wants
|
|
183
|
+
// to use a tool, Claude is waiting for the user to approve
|
|
184
|
+
return (record.type === "assistant" && record.message?.stop_reason === "tool_use");
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/** Patterns in tmux pane content that indicate the session is waiting for user input.
|
|
191
|
+
* Works across all agent types (Claude Code, Codex, etc.) */
|
|
192
|
+
const AWAITING_INPUT_PATTERNS = [
|
|
193
|
+
// Claude Code: tool use approval
|
|
194
|
+
/Do you want to proceed\?/,
|
|
195
|
+
/Allow once/,
|
|
196
|
+
/Allow always/,
|
|
197
|
+
// Codex: command approval
|
|
198
|
+
/Would you like to run the following command\?/,
|
|
199
|
+
/Yes, proceed/,
|
|
200
|
+
/Press enter to confirm or esc to cancel/,
|
|
201
|
+
/don't ask again for/,
|
|
202
|
+
// Generic: common approval prompts
|
|
203
|
+
/\(y\/n\)\s*$/,
|
|
204
|
+
/\[Y\/n\]\s*$/,
|
|
205
|
+
/\[yes\/no\]\s*$/,
|
|
206
|
+
];
|
|
207
|
+
/** Check if a tmux session is showing a prompt waiting for user input */
|
|
208
|
+
function isAwaitingInputFromTmux(tmuxSession) {
|
|
209
|
+
if (!tmuxSession)
|
|
210
|
+
return false;
|
|
211
|
+
try {
|
|
212
|
+
const output = execSync(`tmux capture-pane -t '${tmuxSession.replace(/'/g, "'\\''")}' -p -S -20`, {
|
|
213
|
+
encoding: "utf-8",
|
|
214
|
+
timeout: 3000,
|
|
215
|
+
stdio: "pipe",
|
|
216
|
+
});
|
|
217
|
+
return AWAITING_INPUT_PATTERNS.some((pattern) => pattern.test(output));
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
/** Discover Claude Code sessions by matching processes to session files */
|
|
224
|
+
function discoverClaudeSessions(processes, tmuxPidMap) {
|
|
225
|
+
const sessions = [];
|
|
226
|
+
const claudeProjectsDir = join(homedir(), ".claude", "projects");
|
|
227
|
+
if (!existsSync(claudeProjectsDir))
|
|
228
|
+
return sessions;
|
|
229
|
+
const now = Date.now();
|
|
230
|
+
const WORKING_THRESHOLD_MS = 5 * 1000;
|
|
231
|
+
const seen = new Map(); // sessionId -> index in sessions[]
|
|
232
|
+
const seenCwd = new Map(); // cwd -> index in sessions[]
|
|
233
|
+
for (const proc of processes) {
|
|
234
|
+
if (proc.agentType !== "claude-code")
|
|
235
|
+
continue;
|
|
236
|
+
const cwd = proc.cwd;
|
|
237
|
+
if (!cwd)
|
|
238
|
+
continue;
|
|
239
|
+
const projDirName = pathToProjectDir(cwd);
|
|
240
|
+
const projPath = join(claudeProjectsDir, projDirName);
|
|
241
|
+
if (!existsSync(projPath))
|
|
242
|
+
continue;
|
|
243
|
+
let sessionId = proc.sessionId;
|
|
244
|
+
let mtimeMs = null;
|
|
245
|
+
if (sessionId) {
|
|
246
|
+
const filePath = join(projPath, `${sessionId}.jsonl`);
|
|
247
|
+
try {
|
|
248
|
+
mtimeMs = statSync(filePath).mtimeMs;
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
sessionId = null;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (!sessionId) {
|
|
255
|
+
const latest = findLatestSessionFile(projPath);
|
|
256
|
+
if (!latest)
|
|
257
|
+
continue;
|
|
258
|
+
sessionId = latest.sessionId;
|
|
259
|
+
mtimeMs = latest.mtimeMs;
|
|
260
|
+
}
|
|
261
|
+
if (!mtimeMs)
|
|
262
|
+
continue;
|
|
263
|
+
// Detect if running inside tmux
|
|
264
|
+
const tmuxSession = findTmuxSessionForPid(proc.pid, tmuxPidMap);
|
|
265
|
+
// If this process is in a dashboard-spawned tmux session (aidash-sess_*),
|
|
266
|
+
// use the spawn session ID to avoid duplicate cards
|
|
267
|
+
const isSpawned = /^aidash-(sess_\w+)$/.test(tmuxSession);
|
|
268
|
+
const spawnMatch = tmuxSession.match(/^aidash-(sess_\w+)$/);
|
|
269
|
+
if (spawnMatch) {
|
|
270
|
+
sessionId = spawnMatch[1];
|
|
271
|
+
}
|
|
272
|
+
// If session already seen, upgrade with tmux info if available
|
|
273
|
+
if (seen.has(sessionId)) {
|
|
274
|
+
if (tmuxSession) {
|
|
275
|
+
const idx = seen.get(sessionId);
|
|
276
|
+
sessions[idx].tmuxSession = tmuxSession;
|
|
277
|
+
}
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
// Dedup by cwd: if another process for the same cwd was already recorded,
|
|
281
|
+
// prefer the one in a dashboard-spawned tmux session (aidash-sess_*)
|
|
282
|
+
if (seenCwd.has(cwd)) {
|
|
283
|
+
const existingIdx = seenCwd.get(cwd);
|
|
284
|
+
const existing = sessions[existingIdx];
|
|
285
|
+
if (isSpawned && !existing.tmuxSession.startsWith("aidash-")) {
|
|
286
|
+
// Replace non-spawned entry with spawned one
|
|
287
|
+
sessions[existingIdx] = undefined;
|
|
288
|
+
seen.delete(existing.sessionId);
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
// Keep existing entry (it's spawned or both are non-spawned)
|
|
292
|
+
continue;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
seen.set(sessionId, sessions.length);
|
|
296
|
+
seenCwd.set(cwd, sessions.length);
|
|
297
|
+
const ageMs = now - mtimeMs;
|
|
298
|
+
const project = basename(cwd);
|
|
299
|
+
let branch = "";
|
|
300
|
+
const gitInfo = getGitInfo(cwd);
|
|
301
|
+
if (gitInfo)
|
|
302
|
+
branch = gitInfo.branch;
|
|
303
|
+
// Check both JSONL transcript and tmux pane for waiting-for-input state
|
|
304
|
+
const awaiting = isAwaitingPermissionFromJsonl(projPath, sessionId) ||
|
|
305
|
+
isAwaitingInputFromTmux(tmuxSession);
|
|
306
|
+
sessions.push({
|
|
307
|
+
sessionId,
|
|
308
|
+
project,
|
|
309
|
+
cwd,
|
|
310
|
+
branch,
|
|
311
|
+
model: proc.model,
|
|
312
|
+
status: awaiting
|
|
313
|
+
? "awaiting_permission"
|
|
314
|
+
: ageMs < WORKING_THRESHOLD_MS
|
|
315
|
+
? "working"
|
|
316
|
+
: "idle",
|
|
317
|
+
agentType: "claude-code",
|
|
318
|
+
pid: proc.pid,
|
|
319
|
+
tmuxSession,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
// Filter out entries that were replaced during cwd dedup
|
|
323
|
+
return sessions.filter(Boolean);
|
|
324
|
+
}
|
|
325
|
+
/** Get Codex sessions from SQLite, keyed by cwd */
|
|
326
|
+
function getCodexSessionsFromDb() {
|
|
327
|
+
const map = new Map();
|
|
328
|
+
const dbPath = join(homedir(), ".codex", "state_5.sqlite");
|
|
329
|
+
if (!existsSync(dbPath))
|
|
330
|
+
return map;
|
|
331
|
+
try {
|
|
332
|
+
const output = execSync(`sqlite3 "${dbPath}" "SELECT id, cwd, title, model_provider, updated_at FROM threads ORDER BY updated_at DESC LIMIT 50;"`, { encoding: "utf-8", timeout: 3000, stdio: "pipe" }).trim();
|
|
333
|
+
if (!output)
|
|
334
|
+
return map;
|
|
335
|
+
for (const line of output.split("\n")) {
|
|
336
|
+
const [id, cwd, title, model, updatedAtStr] = line.split("|");
|
|
337
|
+
if (!cwd || map.has(cwd))
|
|
338
|
+
continue; // first one per cwd is most recent
|
|
339
|
+
map.set(cwd, {
|
|
340
|
+
id,
|
|
341
|
+
cwd,
|
|
342
|
+
title,
|
|
343
|
+
model: model || "codex",
|
|
344
|
+
updatedAt: parseInt(updatedAtStr, 10) || 0,
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
catch {
|
|
349
|
+
// sqlite not available
|
|
350
|
+
}
|
|
351
|
+
return map;
|
|
352
|
+
}
|
|
353
|
+
/** Discover Codex sessions from running processes */
|
|
354
|
+
function discoverCodexSessions(processes, tmuxPidMap) {
|
|
355
|
+
const sessions = [];
|
|
356
|
+
const seen = new Set();
|
|
357
|
+
const codexProcesses = processes.filter((p) => p.agentType === "codex" && p.cwd && p.cwd !== "/");
|
|
358
|
+
if (codexProcesses.length === 0)
|
|
359
|
+
return sessions;
|
|
360
|
+
// Get session info from SQLite (keyed by cwd)
|
|
361
|
+
const codexSessions = getCodexSessionsFromDb();
|
|
362
|
+
const nowSecs = Math.floor(Date.now() / 1000);
|
|
363
|
+
const WORKING_THRESHOLD_SECS = 5;
|
|
364
|
+
for (const proc of codexProcesses) {
|
|
365
|
+
const cwd = proc.cwd;
|
|
366
|
+
const dbSession = codexSessions.get(cwd);
|
|
367
|
+
const sessionId = dbSession ? dbSession.id : `codex-${proc.pid}`;
|
|
368
|
+
if (seen.has(sessionId))
|
|
369
|
+
continue;
|
|
370
|
+
seen.add(sessionId);
|
|
371
|
+
const project = basename(cwd);
|
|
372
|
+
let branch = "";
|
|
373
|
+
const gitInfo = getGitInfo(cwd);
|
|
374
|
+
if (gitInfo)
|
|
375
|
+
branch = gitInfo.branch;
|
|
376
|
+
// Use SQLite updated_at (unix seconds) for accurate activity tracking
|
|
377
|
+
const ageSecs = dbSession ? nowSecs - dbSession.updatedAt : Infinity;
|
|
378
|
+
const model = dbSession ? dbSession.model : "codex";
|
|
379
|
+
// Detect if running inside tmux
|
|
380
|
+
const tmuxSession = findTmuxSessionForPid(proc.pid, tmuxPidMap);
|
|
381
|
+
// If in a dashboard-spawned tmux session, use spawn session ID
|
|
382
|
+
const spawnMatch = tmuxSession.match(/^aidash-(sess_\w+)$/);
|
|
383
|
+
if (spawnMatch) {
|
|
384
|
+
const spawnId = spawnMatch[1];
|
|
385
|
+
if (!seen.has(spawnId)) {
|
|
386
|
+
seen.add(spawnId);
|
|
387
|
+
// Replace sessionId so it matches the spawn heartbeat
|
|
388
|
+
sessions.push({
|
|
389
|
+
sessionId: spawnId,
|
|
390
|
+
project,
|
|
391
|
+
cwd,
|
|
392
|
+
branch,
|
|
393
|
+
model,
|
|
394
|
+
status: isAwaitingInputFromTmux(tmuxSession)
|
|
395
|
+
? "awaiting_permission"
|
|
396
|
+
: ageSecs < WORKING_THRESHOLD_SECS
|
|
397
|
+
? "working"
|
|
398
|
+
: "idle",
|
|
399
|
+
agentType: "codex",
|
|
400
|
+
pid: proc.pid,
|
|
401
|
+
tmuxSession,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
continue;
|
|
405
|
+
}
|
|
406
|
+
// Check tmux pane for Codex approval prompts
|
|
407
|
+
const awaiting = isAwaitingInputFromTmux(tmuxSession);
|
|
408
|
+
sessions.push({
|
|
409
|
+
sessionId,
|
|
410
|
+
project,
|
|
411
|
+
cwd,
|
|
412
|
+
branch,
|
|
413
|
+
model,
|
|
414
|
+
status: awaiting
|
|
415
|
+
? "awaiting_permission"
|
|
416
|
+
: ageSecs < WORKING_THRESHOLD_SECS
|
|
417
|
+
? "working"
|
|
418
|
+
: "idle",
|
|
419
|
+
agentType: "codex",
|
|
420
|
+
pid: proc.pid,
|
|
421
|
+
tmuxSession,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
return sessions;
|
|
425
|
+
}
|
|
426
|
+
/** Discover all agent sessions (Claude Code + Codex) */
|
|
427
|
+
export function discoverAllSessions() {
|
|
428
|
+
const processes = findAgentProcesses();
|
|
429
|
+
const tmuxPidMap = buildTmuxPidMap();
|
|
430
|
+
return [
|
|
431
|
+
...discoverClaudeSessions(processes, tmuxPidMap),
|
|
432
|
+
...discoverCodexSessions(processes, tmuxPidMap),
|
|
433
|
+
];
|
|
434
|
+
}
|
|
435
|
+
// Keep backward compat
|
|
436
|
+
export const discoverClaudeSessionsAll = discoverAllSessions;
|
|
437
|
+
/** Post heartbeats for all discovered sessions */
|
|
438
|
+
export async function scanAndReportSessions(config) {
|
|
439
|
+
const sessions = discoverAllSessions();
|
|
440
|
+
for (const session of sessions) {
|
|
441
|
+
try {
|
|
442
|
+
await fetch(`${config.backendUrl}/api/session-heartbeat`, {
|
|
443
|
+
method: "POST",
|
|
444
|
+
headers: {
|
|
445
|
+
"Content-Type": "application/json",
|
|
446
|
+
"x-api-key": config.apiKey,
|
|
447
|
+
"x-device-id": config.deviceId,
|
|
448
|
+
},
|
|
449
|
+
body: JSON.stringify({
|
|
450
|
+
deviceId: config.deviceId,
|
|
451
|
+
sessionId: session.sessionId,
|
|
452
|
+
agentType: session.agentType,
|
|
453
|
+
project: session.project,
|
|
454
|
+
cwd: session.cwd,
|
|
455
|
+
branch: session.branch,
|
|
456
|
+
status: session.status,
|
|
457
|
+
model: session.model,
|
|
458
|
+
task: "",
|
|
459
|
+
tmuxSession: session.tmuxSession,
|
|
460
|
+
tmuxWindow: session.project,
|
|
461
|
+
}),
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
catch {
|
|
465
|
+
// Best-effort, don't spam errors
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
if (sessions.length > 0) {
|
|
469
|
+
console.log(`Session scanner: reported ${sessions.length} active session(s)`);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
const SCAN_INTERVAL_MS = 5_000; // 5 seconds
|
|
473
|
+
export function startSessionScanner(config) {
|
|
474
|
+
scanAndReportSessions(config).catch(() => { });
|
|
475
|
+
return setInterval(() => {
|
|
476
|
+
scanAndReportSessions(config).catch(() => { });
|
|
477
|
+
}, SCAN_INTERVAL_MS);
|
|
478
|
+
}
|
|
479
|
+
//# sourceMappingURL=sessionScanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessionScanner.js","sourceRoot":"","sources":["../src/sessionScanner.ts"],"names":[],"mappings":"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"}
|
package/dist/spawn.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { AgentConfig } from "./config.js";
|
|
2
|
+
export declare function spawnAgentSession(config: AgentConfig, params: {
|
|
3
|
+
projectPath: string;
|
|
4
|
+
projectName: string;
|
|
5
|
+
agentType: string;
|
|
6
|
+
task: string;
|
|
7
|
+
}): Promise<string>;
|
|
8
|
+
//# sourceMappingURL=spawn.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AA+B/C,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,WAAW,EACnB,MAAM,EAAE;IACN,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd,GACA,OAAO,CAAC,MAAM,CAAC,CAmDjB"}
|
package/dist/spawn.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from "uuid";
|
|
2
|
+
import { createSession, tmuxExec, sanitizeShellArg } from "./tmux.js";
|
|
3
|
+
import { getAdapter } from "./adapters/index.js";
|
|
4
|
+
import { sendHeartbeat } from "./heartbeat.js";
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import { execSync } from "node:child_process";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
const TMUX_SESSION_PREFIX = "aidash";
|
|
10
|
+
function findCodexThreadId(cwd, afterTimeSec) {
|
|
11
|
+
const dbPath = join(homedir(), ".codex", "state_5.sqlite");
|
|
12
|
+
if (!existsSync(dbPath))
|
|
13
|
+
return null;
|
|
14
|
+
try {
|
|
15
|
+
const safeDb = dbPath.replace(/'/g, "'\\''");
|
|
16
|
+
const safeCwd = cwd.replace(/'/g, "'\\''");
|
|
17
|
+
// Find the thread created for THIS spawn (created_at >= when we started spawning).
|
|
18
|
+
// ORDER BY ASC so we get the earliest match in case of multiple concurrent spawns.
|
|
19
|
+
const output = execSync(`sqlite3 '${safeDb}' "SELECT id FROM threads WHERE cwd='${safeCwd}' AND archived=0 AND created_at >= ${afterTimeSec} ORDER BY created_at ASC LIMIT 1;"`, { encoding: "utf-8", timeout: 3000, stdio: "pipe" }).trim();
|
|
20
|
+
return output || null;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function delay(ms) {
|
|
27
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
28
|
+
}
|
|
29
|
+
export async function spawnAgentSession(config, params) {
|
|
30
|
+
const sessionId = `sess_${uuidv4().slice(0, 8)}`;
|
|
31
|
+
const tmuxSessionName = `${TMUX_SESSION_PREFIX}-${sessionId}`;
|
|
32
|
+
const adapter = getAdapter(params.agentType);
|
|
33
|
+
const spawnCmd = adapter.buildSpawnCommand(params.task, params.projectPath);
|
|
34
|
+
// Record time before spawning so we can find the thread Codex creates for this session.
|
|
35
|
+
const spawnTimeSec = Math.floor(Date.now() / 1000);
|
|
36
|
+
// 1. Create tmux session and start the agent CLI (interactive mode)
|
|
37
|
+
createSession(tmuxSessionName, params.projectPath);
|
|
38
|
+
tmuxExec(`tmux send-keys -t '${sanitizeShellArg(tmuxSessionName)}' '${sanitizeShellArg(spawnCmd)}' Enter`);
|
|
39
|
+
// 2. Wait for the CLI to initialize, then send the task as the first message
|
|
40
|
+
// Use plain tmux send-keys (not hex mode) since this is a full string, not keystroke input
|
|
41
|
+
if (params.task) {
|
|
42
|
+
await delay(3000);
|
|
43
|
+
tmuxExec(`tmux send-keys -t '${sanitizeShellArg(tmuxSessionName)}' '${sanitizeShellArg(params.task)}' Enter`);
|
|
44
|
+
}
|
|
45
|
+
// 3. Capture the Codex thread ID created during initialization.
|
|
46
|
+
// Retry up to 4 times (8s total) in case Codex hasn't written its thread yet.
|
|
47
|
+
let codexThreadId = null;
|
|
48
|
+
if (params.agentType === "codex") {
|
|
49
|
+
for (let i = 0; i < 4; i++) {
|
|
50
|
+
codexThreadId = findCodexThreadId(params.projectPath, spawnTimeSec);
|
|
51
|
+
if (codexThreadId)
|
|
52
|
+
break;
|
|
53
|
+
await delay(2000);
|
|
54
|
+
}
|
|
55
|
+
console.log(`[spawn] codexThreadId=${codexThreadId ?? "null"}`);
|
|
56
|
+
}
|
|
57
|
+
await sendHeartbeat(config, {
|
|
58
|
+
deviceId: config.deviceId,
|
|
59
|
+
sessionId,
|
|
60
|
+
agentType: params.agentType,
|
|
61
|
+
project: params.projectName,
|
|
62
|
+
cwd: params.projectPath,
|
|
63
|
+
branch: "",
|
|
64
|
+
status: "working",
|
|
65
|
+
task: params.task,
|
|
66
|
+
tmuxSession: tmuxSessionName,
|
|
67
|
+
tmuxWindow: params.projectName,
|
|
68
|
+
codexThreadId,
|
|
69
|
+
});
|
|
70
|
+
console.log(`Spawned session ${sessionId} in tmux:${tmuxSessionName}`);
|
|
71
|
+
return sessionId;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=spawn.js.map
|