@ai-devkit/agent-manager 0.37.0 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/dist/AgentManager.d.ts +0 -4
  2. package/dist/AgentManager.d.ts.map +1 -1
  3. package/dist/AgentManager.js +2 -2
  4. package/dist/AgentManager.js.map +1 -1
  5. package/dist/__tests__/capacity/anthropic.test.js +115 -0
  6. package/dist/__tests__/capacity/anthropic.test.js.map +1 -0
  7. package/dist/__tests__/capacity/boundaries.test.js +45 -0
  8. package/dist/__tests__/capacity/boundaries.test.js.map +1 -0
  9. package/dist/__tests__/capacity/claude.test.js +3 -3
  10. package/dist/__tests__/capacity/claude.test.js.map +1 -1
  11. package/dist/__tests__/capacity/codex.test.js +3 -1
  12. package/dist/__tests__/capacity/codex.test.js.map +1 -1
  13. package/dist/__tests__/capacity/openai.test.js +21 -12
  14. package/dist/__tests__/capacity/openai.test.js.map +1 -1
  15. package/dist/__tests__/capacity/wham.test.js +1 -1
  16. package/dist/__tests__/capacity/wham.test.js.map +1 -1
  17. package/dist/__tests__/capacity/zai.test.js +7 -8
  18. package/dist/__tests__/capacity/zai.test.js.map +1 -1
  19. package/dist/__tests__/harnesses/claude/durable/ClaudePrintAgent.integration.test.js +2 -1
  20. package/dist/__tests__/harnesses/claude/durable/ClaudePrintAgent.integration.test.js.map +1 -1
  21. package/dist/__tests__/harnesses/claude/durable/ClaudePrintRunner.test.js.map +1 -1
  22. package/dist/__tests__/harnesses/codex/durable/CodexCliProbe.test.js +7 -15
  23. package/dist/__tests__/harnesses/codex/durable/CodexCliProbe.test.js.map +1 -1
  24. package/dist/__tests__/harnesses/codex/durable/CodexPrintAgent.integration.test.js +3 -1
  25. package/dist/__tests__/harnesses/codex/durable/CodexPrintAgent.integration.test.js.map +1 -1
  26. package/dist/__tests__/harnesses/codex/durable/CodexPrintAgentService.test.js +11 -26
  27. package/dist/__tests__/harnesses/codex/durable/CodexPrintAgentService.test.js.map +1 -1
  28. package/dist/__tests__/harnesses/codex/durable/CodexPrintRunner.test.js +3 -7
  29. package/dist/__tests__/harnesses/codex/durable/CodexPrintRunner.test.js.map +1 -1
  30. package/dist/__tests__/harnesses/pi/durable/PiCliProbe.test.js +5 -11
  31. package/dist/__tests__/harnesses/pi/durable/PiCliProbe.test.js.map +1 -1
  32. package/dist/__tests__/harnesses/pi/durable/PiPrintAgentService.test.js +9 -19
  33. package/dist/__tests__/harnesses/pi/durable/PiPrintAgentService.test.js.map +1 -1
  34. package/dist/__tests__/harnesses/pi/durable/PiPrintRunner.test.js +3 -7
  35. package/dist/__tests__/harnesses/pi/durable/PiPrintRunner.test.js.map +1 -1
  36. package/dist/__tests__/{readiness → harnesses/readiness}/AgentReadiness.test.js +31 -10
  37. package/dist/__tests__/harnesses/readiness/AgentReadiness.test.js.map +1 -0
  38. package/dist/__tests__/harnesses/runtimeProfiles.test.js +89 -0
  39. package/dist/__tests__/harnesses/runtimeProfiles.test.js.map +1 -0
  40. package/dist/__tests__/print/DurableAgent.test.js +4 -6
  41. package/dist/__tests__/print/DurableAgent.test.js.map +1 -1
  42. package/dist/__tests__/print/DurableAgentRepository.test.js +1 -12
  43. package/dist/__tests__/print/DurableAgentRepository.test.js.map +1 -1
  44. package/dist/__tests__/runtime/ManagedAgentRuntime.test.js.map +1 -1
  45. package/dist/__tests__/terminal/TmuxManager.test.js +1 -1
  46. package/dist/__tests__/terminal/TmuxManager.test.js.map +1 -1
  47. package/dist/capacity/index.d.ts +9 -19
  48. package/dist/capacity/index.d.ts.map +1 -1
  49. package/dist/capacity/index.js +11 -4
  50. package/dist/capacity/index.js.map +1 -1
  51. package/dist/capacity/providers/anthropic.d.ts +9 -0
  52. package/dist/capacity/providers/anthropic.d.ts.map +1 -0
  53. package/dist/capacity/{claude.js → providers/anthropic.js} +10 -128
  54. package/dist/capacity/providers/anthropic.js.map +1 -0
  55. package/dist/capacity/providers/openai-platform.d.ts +14 -0
  56. package/dist/capacity/providers/openai-platform.d.ts.map +1 -0
  57. package/dist/capacity/providers/openai-platform.js +120 -0
  58. package/dist/capacity/providers/openai-platform.js.map +1 -0
  59. package/dist/capacity/providers/openai-subscription.d.ts +20 -0
  60. package/dist/capacity/providers/openai-subscription.d.ts.map +1 -0
  61. package/dist/capacity/providers/openai-subscription.js +129 -0
  62. package/dist/capacity/providers/openai-subscription.js.map +1 -0
  63. package/dist/capacity/providers/zai.d.ts +8 -0
  64. package/dist/capacity/providers/zai.d.ts.map +1 -0
  65. package/dist/capacity/{zai.js → providers/zai.js} +17 -62
  66. package/dist/capacity/providers/zai.js.map +1 -0
  67. package/dist/capacity/sources/claude.d.ts +9 -0
  68. package/dist/capacity/sources/claude.d.ts.map +1 -0
  69. package/dist/capacity/sources/claude.js +17 -0
  70. package/dist/capacity/sources/claude.js.map +1 -0
  71. package/dist/capacity/{codex.d.ts → sources/codex.d.ts} +5 -9
  72. package/dist/capacity/sources/codex.d.ts.map +1 -0
  73. package/dist/capacity/{codex.js → sources/codex.js} +31 -84
  74. package/dist/capacity/sources/codex.js.map +1 -0
  75. package/dist/capacity/sources/pi.d.ts +30 -0
  76. package/dist/capacity/sources/pi.d.ts.map +1 -0
  77. package/dist/capacity/sources/pi.js +81 -0
  78. package/dist/capacity/sources/pi.js.map +1 -0
  79. package/dist/capacity/types.d.ts +2 -0
  80. package/dist/capacity/types.d.ts.map +1 -1
  81. package/dist/capacity/types.js +1 -1
  82. package/dist/capacity/types.js.map +1 -1
  83. package/dist/harnesses/claude/credentials.d.ts +10 -0
  84. package/dist/harnesses/claude/credentials.d.ts.map +1 -0
  85. package/dist/harnesses/claude/credentials.js +112 -0
  86. package/dist/harnesses/claude/credentials.js.map +1 -0
  87. package/dist/harnesses/claude/readiness.d.ts +3 -0
  88. package/dist/harnesses/claude/readiness.d.ts.map +1 -0
  89. package/dist/harnesses/claude/readiness.js +63 -0
  90. package/dist/harnesses/claude/readiness.js.map +1 -0
  91. package/dist/harnesses/codex/credentials.d.ts +15 -0
  92. package/dist/harnesses/codex/credentials.d.ts.map +1 -0
  93. package/dist/harnesses/codex/credentials.js +58 -0
  94. package/dist/harnesses/codex/credentials.js.map +1 -0
  95. package/dist/harnesses/codex/readiness.d.ts +3 -0
  96. package/dist/harnesses/codex/readiness.d.ts.map +1 -0
  97. package/dist/harnesses/codex/readiness.js +60 -0
  98. package/dist/harnesses/codex/readiness.js.map +1 -0
  99. package/dist/harnesses/copilot/readiness.d.ts +3 -0
  100. package/dist/harnesses/copilot/readiness.d.ts.map +1 -0
  101. package/dist/harnesses/copilot/readiness.js +39 -0
  102. package/dist/harnesses/copilot/readiness.js.map +1 -0
  103. package/dist/harnesses/opencode/readiness.d.ts +3 -0
  104. package/dist/harnesses/opencode/readiness.d.ts.map +1 -0
  105. package/dist/harnesses/opencode/readiness.js +50 -0
  106. package/dist/harnesses/opencode/readiness.js.map +1 -0
  107. package/dist/harnesses/pi/credentials.d.ts +22 -0
  108. package/dist/harnesses/pi/credentials.d.ts.map +1 -0
  109. package/dist/harnesses/pi/credentials.js +98 -0
  110. package/dist/harnesses/pi/credentials.js.map +1 -0
  111. package/dist/harnesses/pi/readiness.d.ts +3 -0
  112. package/dist/harnesses/pi/readiness.d.ts.map +1 -0
  113. package/dist/harnesses/pi/readiness.js +96 -0
  114. package/dist/harnesses/pi/readiness.js.map +1 -0
  115. package/dist/harnesses/readiness/AgentReadiness.d.ts +6 -0
  116. package/dist/harnesses/readiness/AgentReadiness.d.ts.map +1 -0
  117. package/dist/harnesses/readiness/AgentReadiness.js +68 -0
  118. package/dist/harnesses/readiness/AgentReadiness.js.map +1 -0
  119. package/dist/harnesses/readiness/checks.d.ts +31 -0
  120. package/dist/harnesses/readiness/checks.d.ts.map +1 -0
  121. package/dist/harnesses/readiness/checks.js +270 -0
  122. package/dist/harnesses/readiness/checks.js.map +1 -0
  123. package/dist/{readiness/AgentReadiness.d.ts → harnesses/readiness/types.d.ts} +19 -10
  124. package/dist/harnesses/readiness/types.d.ts.map +1 -0
  125. package/dist/harnesses/readiness/types.js +4 -0
  126. package/dist/harnesses/readiness/types.js.map +1 -0
  127. package/dist/harnesses/runtimeProfiles.d.ts +11 -0
  128. package/dist/harnesses/runtimeProfiles.d.ts.map +1 -0
  129. package/dist/harnesses/runtimeProfiles.js +99 -0
  130. package/dist/harnesses/runtimeProfiles.js.map +1 -0
  131. package/dist/index.d.ts +16 -42
  132. package/dist/index.d.ts.map +1 -1
  133. package/dist/index.js +9 -23
  134. package/dist/index.js.map +1 -1
  135. package/dist/runtime/ManagedAgentRuntime.d.ts +3 -3
  136. package/dist/runtime/ManagedAgentRuntime.d.ts.map +1 -1
  137. package/dist/runtime/ManagedAgentRuntime.js +6 -20
  138. package/dist/runtime/ManagedAgentRuntime.js.map +1 -1
  139. package/dist/runtime/herdr/HerdrAgentDiscovery.d.ts.map +1 -1
  140. package/dist/runtime/herdr/HerdrAgentDiscovery.js +2 -43
  141. package/dist/runtime/herdr/HerdrAgentDiscovery.js.map +1 -1
  142. package/dist/runtime/tmux/PidPolling.d.ts +1 -1
  143. package/dist/runtime/tmux/PidPolling.d.ts.map +1 -1
  144. package/dist/runtime/tmux/PidPolling.js.map +1 -1
  145. package/dist/runtime/tmux/TmuxAgentRuntime.d.ts +1 -1
  146. package/dist/runtime/tmux/TmuxAgentRuntime.d.ts.map +1 -1
  147. package/dist/runtime/tmux/TmuxAgentRuntime.js +1 -1
  148. package/dist/runtime/tmux/TmuxAgentRuntime.js.map +1 -1
  149. package/dist/runtime/tmux/TmuxManager.d.ts +12 -0
  150. package/dist/runtime/tmux/TmuxManager.d.ts.map +1 -0
  151. package/dist/{terminal → runtime/tmux}/TmuxManager.js +9 -28
  152. package/dist/runtime/tmux/TmuxManager.js.map +1 -0
  153. package/dist/runtime/types.d.ts +1 -1
  154. package/dist/runtime/types.d.ts.map +1 -1
  155. package/dist/runtime/types.js.map +1 -1
  156. package/dist/terminal/TerminalFocusManager.d.ts +4 -32
  157. package/dist/terminal/TerminalFocusManager.d.ts.map +1 -1
  158. package/dist/terminal/TerminalFocusManager.js +17 -267
  159. package/dist/terminal/TerminalFocusManager.js.map +1 -1
  160. package/dist/terminal/TtyWriter.d.ts +1 -44
  161. package/dist/terminal/TtyWriter.d.ts.map +1 -1
  162. package/dist/terminal/TtyWriter.js +11 -347
  163. package/dist/terminal/TtyWriter.js.map +1 -1
  164. package/dist/terminal/backends/index.d.ts +5 -0
  165. package/dist/terminal/backends/index.d.ts.map +1 -0
  166. package/dist/terminal/backends/index.js +19 -0
  167. package/dist/terminal/backends/index.js.map +1 -0
  168. package/dist/terminal/backends/iterm2.d.ts +3 -0
  169. package/dist/terminal/backends/iterm2.d.ts.map +1 -0
  170. package/dist/terminal/backends/iterm2.js +135 -0
  171. package/dist/terminal/backends/iterm2.js.map +1 -0
  172. package/dist/terminal/backends/shared.d.ts +7 -0
  173. package/dist/terminal/backends/shared.d.ts.map +1 -0
  174. package/dist/terminal/backends/shared.js +35 -0
  175. package/dist/terminal/backends/shared.js.map +1 -0
  176. package/dist/terminal/backends/terminal-app.d.ts +3 -0
  177. package/dist/terminal/backends/terminal-app.d.ts.map +1 -0
  178. package/dist/terminal/backends/terminal-app.js +129 -0
  179. package/dist/terminal/backends/terminal-app.js.map +1 -0
  180. package/dist/terminal/backends/tmux.d.ts +3 -0
  181. package/dist/terminal/backends/tmux.d.ts.map +1 -0
  182. package/dist/terminal/backends/tmux.js +76 -0
  183. package/dist/terminal/backends/tmux.js.map +1 -0
  184. package/dist/terminal/backends/wezterm.d.ts +3 -0
  185. package/dist/terminal/backends/wezterm.d.ts.map +1 -0
  186. package/dist/terminal/backends/wezterm.js +71 -0
  187. package/dist/terminal/backends/wezterm.js.map +1 -0
  188. package/dist/terminal/types.d.ts +21 -0
  189. package/dist/terminal/types.d.ts.map +1 -0
  190. package/dist/terminal/types.js +10 -0
  191. package/dist/terminal/types.js.map +1 -0
  192. package/package.json +1 -1
  193. package/src/AgentManager.ts +1 -1
  194. package/src/__tests__/capacity/anthropic.test.ts +105 -0
  195. package/src/__tests__/capacity/boundaries.test.ts +36 -0
  196. package/src/__tests__/capacity/claude.test.ts +3 -8
  197. package/src/__tests__/capacity/codex.test.ts +12 -35
  198. package/src/__tests__/capacity/fixtures/openai-codex-auth.json +1 -1
  199. package/src/__tests__/capacity/openai.test.ts +15 -15
  200. package/src/__tests__/capacity/wham.test.ts +1 -1
  201. package/src/__tests__/capacity/zai.test.ts +27 -62
  202. package/src/__tests__/harnesses/claude/durable/ClaudePrintAgent.integration.test.ts +2 -2
  203. package/src/__tests__/harnesses/claude/durable/ClaudePrintRunner.test.ts +1 -1
  204. package/src/__tests__/harnesses/codex/durable/CodexCliProbe.test.ts +14 -19
  205. package/src/__tests__/harnesses/codex/durable/CodexPrintAgent.integration.test.ts +3 -3
  206. package/src/__tests__/harnesses/codex/durable/CodexPrintAgentService.test.ts +24 -30
  207. package/src/__tests__/harnesses/codex/durable/CodexPrintRunner.test.ts +4 -8
  208. package/src/__tests__/harnesses/pi/durable/PiCliProbe.test.ts +7 -11
  209. package/src/__tests__/harnesses/pi/durable/PiPrintAgentService.test.ts +19 -22
  210. package/src/__tests__/harnesses/pi/durable/PiPrintRunner.test.ts +7 -8
  211. package/src/__tests__/{readiness → harnesses/readiness}/AgentReadiness.test.ts +36 -9
  212. package/src/__tests__/harnesses/runtimeProfiles.test.ts +110 -0
  213. package/src/__tests__/print/DurableAgent.test.ts +4 -13
  214. package/src/__tests__/print/DurableAgentRepository.test.ts +1 -13
  215. package/src/__tests__/runtime/ManagedAgentRuntime.test.ts +1 -1
  216. package/src/__tests__/terminal/TmuxManager.test.ts +1 -1
  217. package/src/capacity/index.ts +29 -23
  218. package/src/capacity/{claude.ts → providers/anthropic.ts} +16 -131
  219. package/src/capacity/providers/openai-platform.ts +150 -0
  220. package/src/capacity/{wham.ts → providers/openai-subscription.ts} +86 -8
  221. package/src/capacity/{zai.ts → providers/zai.ts} +29 -111
  222. package/src/capacity/sources/claude.ts +30 -0
  223. package/src/capacity/{codex.ts → sources/codex.ts} +54 -124
  224. package/src/capacity/sources/pi.ts +131 -0
  225. package/src/capacity/types.ts +3 -0
  226. package/src/harnesses/claude/credentials.ts +122 -0
  227. package/src/harnesses/claude/readiness.ts +69 -0
  228. package/src/harnesses/codex/credentials.ts +81 -0
  229. package/src/harnesses/codex/readiness.ts +72 -0
  230. package/src/harnesses/copilot/readiness.ts +33 -0
  231. package/src/harnesses/opencode/readiness.ts +48 -0
  232. package/src/harnesses/pi/credentials.ts +129 -0
  233. package/src/harnesses/pi/readiness.ts +104 -0
  234. package/src/harnesses/readiness/AgentReadiness.ts +87 -0
  235. package/src/harnesses/readiness/checks.ts +336 -0
  236. package/src/harnesses/readiness/types.ts +90 -0
  237. package/src/{utils/agents.ts → harnesses/runtimeProfiles.ts} +34 -38
  238. package/src/index.ts +15 -128
  239. package/src/runtime/ManagedAgentRuntime.ts +7 -22
  240. package/src/runtime/herdr/HerdrAgentDiscovery.ts +3 -19
  241. package/src/runtime/tmux/PidPolling.ts +1 -1
  242. package/src/runtime/tmux/TmuxAgentRuntime.ts +1 -1
  243. package/src/{terminal → runtime/tmux}/TmuxManager.ts +9 -30
  244. package/src/runtime/types.ts +1 -1
  245. package/src/terminal/TerminalFocusManager.ts +19 -292
  246. package/src/terminal/TtyWriter.ts +16 -336
  247. package/src/terminal/backends/index.ts +21 -0
  248. package/src/terminal/backends/iterm2.ts +126 -0
  249. package/src/terminal/backends/shared.ts +42 -0
  250. package/src/terminal/backends/terminal-app.ts +122 -0
  251. package/src/terminal/backends/tmux.ts +62 -0
  252. package/src/terminal/backends/wezterm.ts +65 -0
  253. package/src/terminal/types.ts +22 -0
  254. package/dist/__tests__/readiness/AgentReadiness.test.js.map +0 -1
  255. package/dist/__tests__/utils/agents.test.js +0 -93
  256. package/dist/__tests__/utils/agents.test.js.map +0 -1
  257. package/dist/capacity/claude.d.ts +0 -17
  258. package/dist/capacity/claude.d.ts.map +0 -1
  259. package/dist/capacity/claude.js.map +0 -1
  260. package/dist/capacity/codex.d.ts.map +0 -1
  261. package/dist/capacity/codex.js.map +0 -1
  262. package/dist/capacity/openai.d.ts +0 -47
  263. package/dist/capacity/openai.d.ts.map +0 -1
  264. package/dist/capacity/openai.js +0 -307
  265. package/dist/capacity/openai.js.map +0 -1
  266. package/dist/capacity/wham.d.ts +0 -16
  267. package/dist/capacity/wham.d.ts.map +0 -1
  268. package/dist/capacity/wham.js +0 -66
  269. package/dist/capacity/wham.js.map +0 -1
  270. package/dist/capacity/zai.d.ts +0 -14
  271. package/dist/capacity/zai.d.ts.map +0 -1
  272. package/dist/capacity/zai.js.map +0 -1
  273. package/dist/readiness/AgentReadiness.d.ts.map +0 -1
  274. package/dist/readiness/AgentReadiness.js +0 -614
  275. package/dist/readiness/AgentReadiness.js.map +0 -1
  276. package/dist/terminal/TmuxManager.d.ts +0 -28
  277. package/dist/terminal/TmuxManager.d.ts.map +0 -1
  278. package/dist/terminal/TmuxManager.js.map +0 -1
  279. package/dist/terminal/index.d.ts +0 -5
  280. package/dist/terminal/index.d.ts.map +0 -1
  281. package/dist/terminal/index.js +0 -5
  282. package/dist/terminal/index.js.map +0 -1
  283. package/dist/utils/agents.d.ts +0 -16
  284. package/dist/utils/agents.d.ts.map +0 -1
  285. package/dist/utils/agents.js +0 -117
  286. package/dist/utils/agents.js.map +0 -1
  287. package/dist/utils/index.d.ts +0 -8
  288. package/dist/utils/index.d.ts.map +0 -1
  289. package/dist/utils/index.js +0 -6
  290. package/dist/utils/index.js.map +0 -1
  291. package/src/__tests__/utils/agents.test.ts +0 -105
  292. package/src/capacity/openai.ts +0 -355
  293. package/src/readiness/AgentReadiness.ts +0 -730
  294. package/src/terminal/index.ts +0 -4
  295. package/src/utils/index.ts +0 -14
@@ -1,325 +1,52 @@
1
- import { execFile } from "child_process";
2
- import { promisify } from "util";
3
1
  import { getProcessTty } from "../utils/process.js";
4
- import { escapeAppleScript } from "../utils/applescript.js";
2
+ import { TERMINAL_BACKENDS, terminalBackendFor } from "./backends/index.js";
3
+ import { TerminalType, type TerminalLocation } from "./types.js";
5
4
 
6
- const execFileAsync = promisify(execFile);
5
+ export { TerminalType };
6
+ export type { TerminalLocation };
7
7
 
8
- export enum TerminalType {
9
- TMUX = "tmux",
10
- WEZTERM = "wezterm",
11
- ITERM2 = "iterm2",
12
- TERMINAL_APP = "terminal-app",
13
- UNKNOWN = "unknown",
14
- }
15
-
16
- export interface TerminalLocation {
17
- type: TerminalType;
18
- identifier: string; // e.g., "session:window.pane" for tmux, WezTerm pane id, or TTY for others
19
- tty: string; // e.g., "/dev/ttys030"
20
- }
21
-
22
- /**
23
- * Subset of a `wezterm cli list --format json` entry. Only `pane_id` and
24
- * `tty_name` are read; extra fields are ignored so schema additions across
25
- * WezTerm versions don't break parsing. (The TTY is exposed as `tty_name` in
26
- * the JSON, not `tty`.)
27
- */
28
- interface WeztermPaneEntry {
29
- pane_id?: number;
30
- tty_name?: string | null;
31
- }
32
-
33
- /**
34
- * Optional trace sink. When provided to {@link TerminalFocusManager}, each
35
- * discovery/focus step reports a human-readable line so callers (e.g. the
36
- * `agent open --debug` command) can inspect the matching/focus decision path.
37
- */
8
+ /** Optional trace sink for terminal discovery and focus decisions. */
38
9
  export type TerminalDebugLogger = (message: string) => void;
39
10
 
40
11
  export class TerminalFocusManager {
41
12
  constructor(private readonly debug?: TerminalDebugLogger) {}
42
13
 
43
- /**
44
- * Find the terminal location (emulator info) for a given process ID
45
- */
46
14
  async findTerminal(pid: number): Promise<TerminalLocation | null> {
47
- const ttyShort = getProcessTty(pid);
48
-
49
- // If no TTY or invalid, we can't find the terminal
50
- if (!ttyShort || ttyShort === "?") {
15
+ const tty = getProcessTty(pid);
16
+ if (!tty || tty === "?") {
51
17
  this.debug?.(`findTerminal(pid=${pid}): no usable TTY, cannot resolve terminal`);
52
18
  return null;
53
19
  }
54
20
 
55
- const fullTty = `/dev/${ttyShort}`;
21
+ const fullTty = `/dev/${tty}`;
56
22
  this.debug?.(`findTerminal(pid=${pid}): resolving terminal for ${fullTty}`);
57
23
 
58
- // 1. Check tmux (most specific if running inside it)
59
- const tmuxLocation = await this.findTmuxPane(fullTty);
60
- if (tmuxLocation) {
61
- this.debug?.(`findTerminal: matched tmux (identifier=${tmuxLocation.identifier})`);
62
- return tmuxLocation;
63
- }
64
- this.debug?.("findTerminal: tmux no match");
65
-
66
- // 2. Check WezTerm (cross-platform, via its CLI — no AppleScript)
67
- const weztermLocation = await this.findWeztermPane(fullTty);
68
- if (weztermLocation) {
69
- this.debug?.(`findTerminal: matched wezterm (pane_id=${weztermLocation.identifier})`);
70
- return weztermLocation;
71
- }
72
- this.debug?.("findTerminal: wezterm no match");
73
-
74
- // 3. Check iTerm2
75
- const itermLocation = await this.findITerm2Session(fullTty);
76
- if (itermLocation) {
77
- this.debug?.(`findTerminal: matched iTerm2 (tty=${itermLocation.tty})`);
78
- return itermLocation;
79
- }
80
- this.debug?.("findTerminal: iTerm2 no match");
81
-
82
- // 4. Check Terminal.app
83
- const terminalAppLocation = await this.findTerminalAppWindow(fullTty);
84
- if (terminalAppLocation) {
85
- this.debug?.(`findTerminal: matched Terminal.app (tty=${terminalAppLocation.tty})`);
86
- return terminalAppLocation;
24
+ for (const backend of TERMINAL_BACKENDS) {
25
+ const location = await backend.find(fullTty);
26
+ if (location) {
27
+ this.debug?.(`findTerminal: matched ${backend.label} (identifier=${location.identifier})`);
28
+ return location;
29
+ }
30
+ this.debug?.(`findTerminal: ${backend.label} no match`);
87
31
  }
88
- this.debug?.("findTerminal: Terminal.app no match");
89
32
 
90
- // 5. Fallback: we know the TTY but not the emulator wrapper
91
33
  this.debug?.("findTerminal: no emulator matched; returning UNKNOWN");
92
- return {
93
- type: TerminalType.UNKNOWN,
94
- identifier: "",
95
- tty: fullTty,
96
- };
34
+ return { type: TerminalType.UNKNOWN, identifier: "", tty: fullTty };
97
35
  }
98
36
 
99
- /**
100
- * Focus the terminal identified by the location
101
- */
102
37
  async focusTerminal(location: TerminalLocation): Promise<boolean> {
103
38
  this.debug?.(
104
39
  `focusTerminal: focusing ${location.type} (identifier=${location.identifier}, tty=${location.tty})`,
105
40
  );
41
+
106
42
  let success = false;
107
43
  try {
108
- switch (location.type) {
109
- case TerminalType.TMUX:
110
- success = await this.focusTmuxPane(location.identifier);
111
- break;
112
- case TerminalType.WEZTERM:
113
- success = await this.focusWeztermPane(location.identifier);
114
- break;
115
- case TerminalType.ITERM2:
116
- success = await this.focusITerm2Session(location.tty);
117
- break;
118
- case TerminalType.TERMINAL_APP:
119
- success = await this.focusTerminalAppWindow(location.tty);
120
- break;
121
- default:
122
- success = false;
123
- }
44
+ success = (await terminalBackendFor(location.type)?.focus(location)) ?? false;
124
45
  } catch {
125
46
  success = false;
126
47
  }
48
+
127
49
  this.debug?.(`focusTerminal: ${success ? "succeeded" : "failed"} for ${location.type}`);
128
50
  return success;
129
51
  }
130
-
131
- private async findWeztermPane(tty: string): Promise<TerminalLocation | null> {
132
- try {
133
- const { stdout } = await execFileAsync("wezterm", ["cli", "list", "--format", "json"]);
134
-
135
- const panes = JSON.parse(stdout) as WeztermPaneEntry[];
136
- if (!Array.isArray(panes)) return null;
137
-
138
- for (const pane of panes) {
139
- if (
140
- pane &&
141
- typeof pane.tty_name === "string" &&
142
- pane.tty_name === tty &&
143
- pane.pane_id != null
144
- ) {
145
- return {
146
- type: TerminalType.WEZTERM,
147
- identifier: String(pane.pane_id),
148
- tty,
149
- };
150
- }
151
- }
152
- } catch {
153
- // wezterm not installed, not running, or returned invalid JSON
154
- }
155
- return null;
156
- }
157
-
158
- private async focusWeztermPane(paneId: string): Promise<boolean> {
159
- try {
160
- await execFileAsync("wezterm", ["cli", "activate-pane", "--pane-id", paneId]);
161
- return true;
162
- } catch {
163
- return false;
164
- }
165
- }
166
-
167
- private async findTmuxPane(tty: string): Promise<TerminalLocation | null> {
168
- try {
169
- const { stdout } = await execFileAsync("tmux", [
170
- "list-panes",
171
- "-a",
172
- "-F",
173
- "#{pane_tty}|#{session_name}:#{window_index}.#{pane_index}",
174
- ]);
175
-
176
- const lines = stdout.trim().split("\n");
177
- for (const line of lines) {
178
- if (!line.trim()) continue;
179
- const [paneTty, identifier] = line.split("|");
180
- if (paneTty === tty && identifier) {
181
- return {
182
- type: TerminalType.TMUX,
183
- identifier,
184
- tty,
185
- };
186
- }
187
- }
188
- } catch {
189
- // tmux might not be installed or running
190
- }
191
- return null;
192
- }
193
-
194
- private async findITerm2Session(tty: string): Promise<TerminalLocation | null> {
195
- try {
196
- // Check if iTerm2 is running first to avoid launching it.
197
- if (!(await this.isProcessRunning("iTerm2"))) return null;
198
- } catch {
199
- return null;
200
- }
201
-
202
- try {
203
- const escapedTty = escapeAppleScript(tty);
204
- const script = `
205
- tell application "iTerm"
206
- repeat with w in windows
207
- repeat with t in tabs of w
208
- repeat with s in sessions of t
209
- if tty of s is "${escapedTty}" then
210
- return "found"
211
- end if
212
- end repeat
213
- end repeat
214
- end repeat
215
- end tell
216
- `;
217
-
218
- const { stdout } = await execFileAsync("osascript", ["-e", script]);
219
- if (stdout.trim() === "found") {
220
- return {
221
- type: TerminalType.ITERM2,
222
- identifier: tty,
223
- tty,
224
- };
225
- }
226
- } catch {
227
- // iTerm2 script failed
228
- }
229
- return null;
230
- }
231
-
232
- private async findTerminalAppWindow(tty: string): Promise<TerminalLocation | null> {
233
- try {
234
- // Check if Terminal.app is running
235
- if (!(await this.isProcessRunning("Terminal"))) return null;
236
- } catch {
237
- return null;
238
- }
239
-
240
- try {
241
- const escapedTty = escapeAppleScript(tty);
242
- const script = `
243
- tell application "Terminal"
244
- repeat with w in windows
245
- repeat with t in tabs of w
246
- if tty of t is "${escapedTty}" then
247
- return "found"
248
- end if
249
- end repeat
250
- end repeat
251
- end tell
252
- `;
253
-
254
- const { stdout } = await execFileAsync("osascript", ["-e", script]);
255
- if (stdout.trim() === "found") {
256
- return {
257
- type: TerminalType.TERMINAL_APP,
258
- identifier: tty,
259
- tty,
260
- };
261
- }
262
- } catch {
263
- // Terminal.app script failed
264
- }
265
- return null;
266
- }
267
-
268
- private async isProcessRunning(name: string): Promise<boolean> {
269
- const { stdout } = await execFileAsync("ps", ["-Axo", "comm"]);
270
- return stdout
271
- .split("\n")
272
- .map((line) => line.trim())
273
- .some((command) => command === name || command.endsWith(`/${name}`));
274
- }
275
-
276
- private async focusTmuxPane(identifier: string): Promise<boolean> {
277
- try {
278
- await execFileAsync("tmux", ["switch-client", "-t", identifier]);
279
- return true;
280
- } catch {
281
- return false;
282
- }
283
- }
284
-
285
- private async focusITerm2Session(tty: string): Promise<boolean> {
286
- const escapedTty = escapeAppleScript(tty);
287
- const script = `
288
- tell application "iTerm"
289
- activate
290
- repeat with w in windows
291
- repeat with t in tabs of w
292
- repeat with s in sessions of t
293
- if tty of s is "${escapedTty}" then
294
- select s
295
- return "true"
296
- end if
297
- end repeat
298
- end repeat
299
- end repeat
300
- end tell
301
- `;
302
- const { stdout } = await execFileAsync("osascript", ["-e", script]);
303
- return stdout.trim() === "true";
304
- }
305
-
306
- private async focusTerminalAppWindow(tty: string): Promise<boolean> {
307
- const escapedTty = escapeAppleScript(tty);
308
- const script = `
309
- tell application "Terminal"
310
- activate
311
- repeat with w in windows
312
- repeat with t in tabs of w
313
- if tty of t is "${escapedTty}" then
314
- set index of w to 1
315
- set selected tab of w to t
316
- return "true"
317
- end if
318
- end repeat
319
- end repeat
320
- end tell
321
- `;
322
- const { stdout } = await execFileAsync("osascript", ["-e", script]);
323
- return stdout.trim() === "true";
324
- }
325
52
  }