@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.
Files changed (109) hide show
  1. package/dist/__tests__/codexAdapterFindFile.test.d.ts +2 -0
  2. package/dist/__tests__/codexAdapterFindFile.test.d.ts.map +1 -0
  3. package/dist/__tests__/codexAdapterFindFile.test.js +298 -0
  4. package/dist/__tests__/codexAdapterFindFile.test.js.map +1 -0
  5. package/dist/__tests__/enrichers.test.d.ts +2 -0
  6. package/dist/__tests__/enrichers.test.d.ts.map +1 -0
  7. package/dist/__tests__/enrichers.test.js +47 -0
  8. package/dist/__tests__/enrichers.test.js.map +1 -0
  9. package/dist/__tests__/tmux.integration.test.d.ts +2 -0
  10. package/dist/__tests__/tmux.integration.test.d.ts.map +1 -0
  11. package/dist/__tests__/tmux.integration.test.js +112 -0
  12. package/dist/__tests__/tmux.integration.test.js.map +1 -0
  13. package/dist/__tests__/tmux.test.d.ts +2 -0
  14. package/dist/__tests__/tmux.test.d.ts.map +1 -0
  15. package/dist/__tests__/tmux.test.js +26 -0
  16. package/dist/__tests__/tmux.test.js.map +1 -0
  17. package/dist/__tests__/transcriptAdapters.test.d.ts +2 -0
  18. package/dist/__tests__/transcriptAdapters.test.d.ts.map +1 -0
  19. package/dist/__tests__/transcriptAdapters.test.js +133 -0
  20. package/dist/__tests__/transcriptAdapters.test.js.map +1 -0
  21. package/dist/adapters/claude-code.d.ts +3 -0
  22. package/dist/adapters/claude-code.d.ts.map +1 -0
  23. package/dist/adapters/claude-code.js +48 -0
  24. package/dist/adapters/claude-code.js.map +1 -0
  25. package/dist/adapters/codex.d.ts +3 -0
  26. package/dist/adapters/codex.d.ts.map +1 -0
  27. package/dist/adapters/codex.js +33 -0
  28. package/dist/adapters/codex.js.map +1 -0
  29. package/dist/adapters/generic.d.ts +3 -0
  30. package/dist/adapters/generic.d.ts.map +1 -0
  31. package/dist/adapters/generic.js +38 -0
  32. package/dist/adapters/generic.js.map +1 -0
  33. package/dist/adapters/index.d.ts +7 -0
  34. package/dist/adapters/index.d.ts.map +1 -0
  35. package/dist/adapters/index.js +12 -0
  36. package/dist/adapters/index.js.map +1 -0
  37. package/dist/agent.d.ts +3 -0
  38. package/dist/agent.d.ts.map +1 -0
  39. package/dist/agent.js +49 -6
  40. package/dist/agent.js.map +1 -0
  41. package/dist/cli.d.ts +11 -0
  42. package/dist/cli.d.ts.map +1 -0
  43. package/dist/cli.js +120 -186
  44. package/dist/cli.js.map +1 -0
  45. package/dist/config.d.ts +26 -0
  46. package/dist/config.d.ts.map +1 -0
  47. package/dist/config.js +48 -0
  48. package/dist/config.js.map +1 -0
  49. package/dist/enrichers/git.d.ts +6 -0
  50. package/dist/enrichers/git.d.ts.map +1 -0
  51. package/dist/enrichers/git.js +20 -0
  52. package/dist/enrichers/git.js.map +1 -0
  53. package/dist/enrichers/pr.d.ts +2 -0
  54. package/dist/enrichers/pr.d.ts.map +1 -0
  55. package/dist/enrichers/pr.js +10 -0
  56. package/dist/enrichers/pr.js.map +1 -0
  57. package/dist/enrichers/transcript.d.ts +8 -0
  58. package/dist/enrichers/transcript.d.ts.map +1 -0
  59. package/dist/enrichers/transcript.js +33 -0
  60. package/dist/enrichers/transcript.js.map +1 -0
  61. package/dist/heartbeat.d.ts +4 -0
  62. package/dist/heartbeat.d.ts.map +1 -0
  63. package/dist/heartbeat.js +15 -0
  64. package/dist/heartbeat.js.map +1 -0
  65. package/dist/init.d.ts +10 -0
  66. package/dist/init.d.ts.map +1 -0
  67. package/dist/init.js +70 -0
  68. package/dist/init.js.map +1 -0
  69. package/dist/register.d.ts +3 -0
  70. package/dist/register.d.ts.map +1 -0
  71. package/dist/register.js +22 -0
  72. package/dist/register.js.map +1 -0
  73. package/dist/sessionScanner.d.ts +20 -0
  74. package/dist/sessionScanner.d.ts.map +1 -0
  75. package/dist/sessionScanner.js +479 -0
  76. package/dist/sessionScanner.js.map +1 -0
  77. package/dist/spawn.d.ts +8 -0
  78. package/dist/spawn.d.ts.map +1 -0
  79. package/dist/spawn.js +73 -0
  80. package/dist/spawn.js.map +1 -0
  81. package/dist/syncProjects.d.ts +3 -0
  82. package/dist/syncProjects.d.ts.map +1 -0
  83. package/dist/syncProjects.js +91 -0
  84. package/dist/syncProjects.js.map +1 -0
  85. package/dist/terminalBridge.d.ts +4 -0
  86. package/dist/terminalBridge.d.ts.map +1 -0
  87. package/dist/terminalBridge.js +42 -0
  88. package/dist/terminalBridge.js.map +1 -0
  89. package/dist/tmux.d.ts +21 -0
  90. package/dist/tmux.d.ts.map +1 -0
  91. package/dist/tmux.js +89 -0
  92. package/dist/tmux.js.map +1 -0
  93. package/dist/transcriptAdapters/claude-code.d.ts +3 -0
  94. package/dist/transcriptAdapters/claude-code.d.ts.map +1 -0
  95. package/dist/transcriptAdapters/claude-code.js +147 -0
  96. package/dist/transcriptAdapters/claude-code.js.map +1 -0
  97. package/dist/transcriptAdapters/codex.d.ts +3 -0
  98. package/dist/transcriptAdapters/codex.d.ts.map +1 -0
  99. package/dist/transcriptAdapters/codex.js +251 -0
  100. package/dist/transcriptAdapters/codex.js.map +1 -0
  101. package/dist/transcriptAdapters/index.d.ts +17 -0
  102. package/dist/transcriptAdapters/index.d.ts.map +1 -0
  103. package/dist/transcriptAdapters/index.js +9 -0
  104. package/dist/transcriptAdapters/index.js.map +1 -0
  105. package/dist/wsClient.d.ts +6 -0
  106. package/dist/wsClient.d.ts.map +1 -0
  107. package/dist/wsClient.js +156 -0
  108. package/dist/wsClient.js.map +1 -0
  109. package/package.json +6 -2
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { AgentConfig } from "./config.js";
2
+ export declare function registerDevice(config: AgentConfig): Promise<void>;
3
+ //# sourceMappingURL=register.d.ts.map
@@ -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"}
@@ -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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"}
@@ -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