@gigaflow/gmux 0.15.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.
- package/CHANGELOG.md +654 -0
- package/LICENSE +21 -0
- package/README.md +351 -0
- package/dist/adapters/claude-code.d.ts +52 -0
- package/dist/adapters/claude-code.js +315 -0
- package/dist/adapters/claude-code.js.map +1 -0
- package/dist/adapters/codex.d.ts +39 -0
- package/dist/adapters/codex.js +233 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/jsonl.d.ts +40 -0
- package/dist/adapters/jsonl.js +92 -0
- package/dist/adapters/jsonl.js.map +1 -0
- package/dist/adapters/registry.d.ts +11 -0
- package/dist/adapters/registry.js +21 -0
- package/dist/adapters/registry.js.map +1 -0
- package/dist/adapters/types.d.ts +43 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/cli/border-client.d.ts +8 -0
- package/dist/cli/border-client.js +11 -0
- package/dist/cli/border-client.js.map +1 -0
- package/dist/cli/commands/__ask-cancel.d.ts +35 -0
- package/dist/cli/commands/__ask-cancel.js +91 -0
- package/dist/cli/commands/__ask-cancel.js.map +1 -0
- package/dist/cli/commands/__ask-refresh.d.ts +40 -0
- package/dist/cli/commands/__ask-refresh.js +54 -0
- package/dist/cli/commands/__ask-refresh.js.map +1 -0
- package/dist/cli/commands/__ask-run.d.ts +43 -0
- package/dist/cli/commands/__ask-run.js +126 -0
- package/dist/cli/commands/__ask-run.js.map +1 -0
- package/dist/cli/commands/__ask-send.d.ts +50 -0
- package/dist/cli/commands/__ask-send.js +138 -0
- package/dist/cli/commands/__ask-send.js.map +1 -0
- package/dist/cli/commands/__preview-card.d.ts +31 -0
- package/dist/cli/commands/__preview-card.js +72 -0
- package/dist/cli/commands/__preview-card.js.map +1 -0
- package/dist/cli/commands/ask.d.ts +50 -0
- package/dist/cli/commands/ask.js +240 -0
- package/dist/cli/commands/ask.js.map +1 -0
- package/dist/cli/commands/auto.d.ts +12 -0
- package/dist/cli/commands/auto.js +28 -0
- package/dist/cli/commands/auto.js.map +1 -0
- package/dist/cli/commands/cockpit.d.ts +14 -0
- package/dist/cli/commands/cockpit.js +52 -0
- package/dist/cli/commands/cockpit.js.map +1 -0
- package/dist/cli/commands/daemon.d.ts +75 -0
- package/dist/cli/commands/daemon.js +258 -0
- package/dist/cli/commands/daemon.js.map +1 -0
- package/dist/cli/commands/doctor.d.ts +6 -0
- package/dist/cli/commands/doctor.js +159 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/commands/grep.d.ts +2 -0
- package/dist/cli/commands/grep.js +52 -0
- package/dist/cli/commands/grep.js.map +1 -0
- package/dist/cli/commands/index-cmd.d.ts +2 -0
- package/dist/cli/commands/index-cmd.js +29 -0
- package/dist/cli/commands/index-cmd.js.map +1 -0
- package/dist/cli/commands/ls.d.ts +30 -0
- package/dist/cli/commands/ls.js +95 -0
- package/dist/cli/commands/ls.js.map +1 -0
- package/dist/cli/commands/overlay.d.ts +26 -0
- package/dist/cli/commands/overlay.js +216 -0
- package/dist/cli/commands/overlay.js.map +1 -0
- package/dist/cli/commands/pick.d.ts +59 -0
- package/dist/cli/commands/pick.js +254 -0
- package/dist/cli/commands/pick.js.map +1 -0
- package/dist/cli/commands/picker-rows.d.ts +17 -0
- package/dist/cli/commands/picker-rows.js +47 -0
- package/dist/cli/commands/picker-rows.js.map +1 -0
- package/dist/cli/commands/resume.d.ts +25 -0
- package/dist/cli/commands/resume.js +88 -0
- package/dist/cli/commands/resume.js.map +1 -0
- package/dist/cli/commands/run.d.ts +13 -0
- package/dist/cli/commands/run.js +78 -0
- package/dist/cli/commands/run.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +37 -0
- package/dist/cli/commands/setup.js +200 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/commands/show.d.ts +2 -0
- package/dist/cli/commands/show.js +24 -0
- package/dist/cli/commands/show.js.map +1 -0
- package/dist/cli/commands/summarize.d.ts +2 -0
- package/dist/cli/commands/summarize.js +59 -0
- package/dist/cli/commands/summarize.js.map +1 -0
- package/dist/cli/commands/tmux.d.ts +11 -0
- package/dist/cli/commands/tmux.js +90 -0
- package/dist/cli/commands/tmux.js.map +1 -0
- package/dist/cli/commands/watch.d.ts +2 -0
- package/dist/cli/commands/watch.js +76 -0
- package/dist/cli/commands/watch.js.map +1 -0
- package/dist/cli/format.d.ts +74 -0
- package/dist/cli/format.js +242 -0
- package/dist/cli/format.js.map +1 -0
- package/dist/cli/gmux-render.d.ts +11 -0
- package/dist/cli/gmux-render.js +60 -0
- package/dist/cli/gmux-render.js.map +1 -0
- package/dist/cli/main.d.ts +8 -0
- package/dist/cli/main.js +145 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/overlay-ask.d.ts +13 -0
- package/dist/cli/overlay-ask.js +32 -0
- package/dist/cli/overlay-ask.js.map +1 -0
- package/dist/cli/overlay.d.ts +26 -0
- package/dist/cli/overlay.js +149 -0
- package/dist/cli/overlay.js.map +1 -0
- package/dist/cli/picker.d.ts +308 -0
- package/dist/cli/picker.js +642 -0
- package/dist/cli/picker.js.map +1 -0
- package/dist/cli/preview.d.ts +87 -0
- package/dist/cli/preview.js +292 -0
- package/dist/cli/preview.js.map +1 -0
- package/dist/cli/tmux-label.d.ts +50 -0
- package/dist/cli/tmux-label.js +146 -0
- package/dist/cli/tmux-label.js.map +1 -0
- package/dist/core/errors.d.ts +54 -0
- package/dist/core/errors.js +91 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/fingerprint.d.ts +21 -0
- package/dist/core/fingerprint.js +59 -0
- package/dist/core/fingerprint.js.map +1 -0
- package/dist/core/gmux-types.d.ts +102 -0
- package/dist/core/gmux-types.js +13 -0
- package/dist/core/gmux-types.js.map +1 -0
- package/dist/core/pane-state.d.ts +5 -0
- package/dist/core/pane-state.js +15 -0
- package/dist/core/pane-state.js.map +1 -0
- package/dist/core/paths.d.ts +68 -0
- package/dist/core/paths.js +106 -0
- package/dist/core/paths.js.map +1 -0
- package/dist/core/proc-tree.d.ts +9 -0
- package/dist/core/proc-tree.js +33 -0
- package/dist/core/proc-tree.js.map +1 -0
- package/dist/core/text.d.ts +34 -0
- package/dist/core/text.js +126 -0
- package/dist/core/text.js.map +1 -0
- package/dist/core/types.d.ts +322 -0
- package/dist/core/types.js +10 -0
- package/dist/core/types.js.map +1 -0
- package/dist/services/ask-transcript.d.ts +256 -0
- package/dist/services/ask-transcript.js +544 -0
- package/dist/services/ask-transcript.js.map +1 -0
- package/dist/services/ask.d.ts +54 -0
- package/dist/services/ask.js +161 -0
- package/dist/services/ask.js.map +1 -0
- package/dist/services/auto-summarize.d.ts +188 -0
- package/dist/services/auto-summarize.js +415 -0
- package/dist/services/auto-summarize.js.map +1 -0
- package/dist/services/concurrency.d.ts +2 -0
- package/dist/services/concurrency.js +16 -0
- package/dist/services/concurrency.js.map +1 -0
- package/dist/services/config.d.ts +131 -0
- package/dist/services/config.js +325 -0
- package/dist/services/config.js.map +1 -0
- package/dist/services/daemon-client.d.ts +9 -0
- package/dist/services/daemon-client.js +34 -0
- package/dist/services/daemon-client.js.map +1 -0
- package/dist/services/daemon-socket.d.ts +30 -0
- package/dist/services/daemon-socket.js +76 -0
- package/dist/services/daemon-socket.js.map +1 -0
- package/dist/services/daemon.d.ts +43 -0
- package/dist/services/daemon.js +106 -0
- package/dist/services/daemon.js.map +1 -0
- package/dist/services/distill.d.ts +39 -0
- package/dist/services/distill.js +93 -0
- package/dist/services/distill.js.map +1 -0
- package/dist/services/guardian.d.ts +26 -0
- package/dist/services/guardian.js +71 -0
- package/dist/services/guardian.js.map +1 -0
- package/dist/services/index-store.d.ts +36 -0
- package/dist/services/index-store.js +125 -0
- package/dist/services/index-store.js.map +1 -0
- package/dist/services/log-rotation.d.ts +3 -0
- package/dist/services/log-rotation.js +28 -0
- package/dist/services/log-rotation.js.map +1 -0
- package/dist/services/pane-links.d.ts +10 -0
- package/dist/services/pane-links.js +47 -0
- package/dist/services/pane-links.js.map +1 -0
- package/dist/services/pane-process.d.ts +95 -0
- package/dist/services/pane-process.js +183 -0
- package/dist/services/pane-process.js.map +1 -0
- package/dist/services/pane-registry.d.ts +24 -0
- package/dist/services/pane-registry.js +40 -0
- package/dist/services/pane-registry.js.map +1 -0
- package/dist/services/provider-process.d.ts +37 -0
- package/dist/services/provider-process.js +105 -0
- package/dist/services/provider-process.js.map +1 -0
- package/dist/services/providers.d.ts +76 -0
- package/dist/services/providers.js +104 -0
- package/dist/services/providers.js.map +1 -0
- package/dist/services/resolve.d.ts +3 -0
- package/dist/services/resolve.js +18 -0
- package/dist/services/resolve.js.map +1 -0
- package/dist/services/resources.d.ts +17 -0
- package/dist/services/resources.js +113 -0
- package/dist/services/resources.js.map +1 -0
- package/dist/services/search.d.ts +29 -0
- package/dist/services/search.js +135 -0
- package/dist/services/search.js.map +1 -0
- package/dist/services/semantic-gate.d.ts +13 -0
- package/dist/services/semantic-gate.js +23 -0
- package/dist/services/semantic-gate.js.map +1 -0
- package/dist/services/semantic.d.ts +113 -0
- package/dist/services/semantic.js +216 -0
- package/dist/services/semantic.js.map +1 -0
- package/dist/services/sensors.d.ts +92 -0
- package/dist/services/sensors.js +246 -0
- package/dist/services/sensors.js.map +1 -0
- package/dist/services/summarize.d.ts +84 -0
- package/dist/services/summarize.js +230 -0
- package/dist/services/summarize.js.map +1 -0
- package/dist/services/tmux-gateway.d.ts +17 -0
- package/dist/services/tmux-gateway.js +10 -0
- package/dist/services/tmux-gateway.js.map +1 -0
- package/dist/services/tmux-resolve.d.ts +54 -0
- package/dist/services/tmux-resolve.js +0 -0
- package/dist/services/tmux-resolve.js.map +1 -0
- package/dist/services/tmux.d.ts +32 -0
- package/dist/services/tmux.js +107 -0
- package/dist/services/tmux.js.map +1 -0
- package/dist/services/views.d.ts +7 -0
- package/dist/services/views.js +18 -0
- package/dist/services/views.js.map +1 -0
- package/dist/services/watch.d.ts +36 -0
- package/dist/services/watch.js +98 -0
- package/dist/services/watch.js.map +1 -0
- package/dist/services/workspace.d.ts +19 -0
- package/dist/services/workspace.js +88 -0
- package/dist/services/workspace.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read which session a tmux pane is running from the pane's own process tree.
|
|
3
|
+
*
|
|
4
|
+
* The agent process carries the session id in its command line — `codex resume
|
|
5
|
+
* <id>`, `claude --resume <id>` — and both ids are exactly gmux's session
|
|
6
|
+
* id. That is a far stronger signal than the pane's working directory, which is
|
|
7
|
+
* the *shell's* cwd (usually `~`), not the agent's. Where the argv has no id (a
|
|
8
|
+
* fresh session), the agent process's real cwd stands in.
|
|
9
|
+
*
|
|
10
|
+
* The parsing here is pure and tested; `panePid`/`descendants`/`processCwd` are
|
|
11
|
+
* thin shells over `tmux`/`pgrep`/`ps`/`lsof`, and every one of them degrades to
|
|
12
|
+
* a null rather than throwing — a resolve must never fail because a process
|
|
13
|
+
* vanished mid-walk.
|
|
14
|
+
*/
|
|
15
|
+
import { execFile } from "node:child_process";
|
|
16
|
+
import { promisify } from "node:util";
|
|
17
|
+
const run = promisify(execFile);
|
|
18
|
+
const UUID = "[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}";
|
|
19
|
+
/** A harness binary invoked as a token — excludes MCP children and unrelated node procs. */
|
|
20
|
+
const HARNESS_TOKEN = /(?:^|\/|\s)(?:claude|codex)(?:\s|$)/;
|
|
21
|
+
/**
|
|
22
|
+
* The session id on an agent's command line, or null.
|
|
23
|
+
*
|
|
24
|
+
* Codex writes `codex resume <uuid>`; Claude Code writes `claude --resume <uuid>`
|
|
25
|
+
* (or `-r`). The id is the standard 8-4-4-4-12 hex form; anything else is not a
|
|
26
|
+
* session id we can trust, so it reads as null and the caller falls back to cwd.
|
|
27
|
+
*/
|
|
28
|
+
export function parseAgentSession(command) {
|
|
29
|
+
if (/(?:^|\/|\s)codex(?:\s|$)/.test(command)) {
|
|
30
|
+
const match = command.match(new RegExp(`resume\\s+(${UUID})`, "i"));
|
|
31
|
+
if (match)
|
|
32
|
+
return { harness: "codex", sessionId: match[1].toLowerCase() };
|
|
33
|
+
}
|
|
34
|
+
if (/(?:^|\/|\s)claude(?:\s|$)/.test(command)) {
|
|
35
|
+
const match = command.match(new RegExp(`(?:--resume|-r)\\s+(${UUID})`, "i"));
|
|
36
|
+
if (match)
|
|
37
|
+
return { harness: "claude-code", sessionId: match[1].toLowerCase() };
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
/** The harness a command names (claude / codex), even without a session id. */
|
|
42
|
+
export function harnessFromCommand(command) {
|
|
43
|
+
if (/(?:^|\/|\s)claude(?:\s|$)/.test(command))
|
|
44
|
+
return "claude-code";
|
|
45
|
+
if (/(?:^|\/|\s)codex(?:\s|$)/.test(command))
|
|
46
|
+
return "codex";
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The harness process among a pane's descendants, or null.
|
|
51
|
+
*
|
|
52
|
+
* Prefer a process whose argv already yields a session id — that is definitive.
|
|
53
|
+
* Otherwise the first process that looks like a harness invocation, for its cwd.
|
|
54
|
+
* An MCP-server child (`node ./mcp/server.mjs`) names no harness, so it is never
|
|
55
|
+
* chosen over the agent that spawned it.
|
|
56
|
+
*/
|
|
57
|
+
export function pickAgentProcess(procs) {
|
|
58
|
+
return (procs.find((p) => parseAgentSession(p.command) !== null) ??
|
|
59
|
+
procs.find((p) => HARNESS_TOKEN.test(p.command)) ??
|
|
60
|
+
null);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Parse a `ps` `etime` field — `[[DD-]HH:]MM:SS` — into seconds.
|
|
64
|
+
*
|
|
65
|
+
* We use `etime`, not `etimes`: BSD `ps` on macOS has no `etimes` keyword (it
|
|
66
|
+
* silently drops the column), while `etime` is portable across macOS and Linux.
|
|
67
|
+
* Only relative order matters to the caller, so absolute wall-clock is not needed.
|
|
68
|
+
*/
|
|
69
|
+
export function parseElapsedSeconds(etime) {
|
|
70
|
+
const match = etime.trim().match(/^(?:(\d+)-)?(?:(\d+):)?(\d+):(\d+)$/);
|
|
71
|
+
if (!match)
|
|
72
|
+
return null;
|
|
73
|
+
const days = Number(match[1] ?? 0);
|
|
74
|
+
const hours = Number(match[2] ?? 0);
|
|
75
|
+
const mins = Number(match[3]);
|
|
76
|
+
const secs = Number(match[4]);
|
|
77
|
+
return days * 86400 + hours * 3600 + mins * 60 + secs;
|
|
78
|
+
}
|
|
79
|
+
export function parseProcessSnapshot(output) {
|
|
80
|
+
const snapshot = new Map();
|
|
81
|
+
for (const line of output.split("\n")) {
|
|
82
|
+
const match = line.match(/^\s*(\d+)\s+(\d+)\s+(\S+)\s+(.*)$/);
|
|
83
|
+
if (!match)
|
|
84
|
+
continue;
|
|
85
|
+
snapshot.set(Number(match[1]), {
|
|
86
|
+
ppid: Number(match[2]),
|
|
87
|
+
command: match[4].trim(),
|
|
88
|
+
elapsedSeconds: parseElapsedSeconds(match[3]),
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return snapshot;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* One snapshot of every process, taken once and walked in memory — instead of a
|
|
95
|
+
* `pgrep` per node down a deep agent tree, which spawned dozens of processes and
|
|
96
|
+
* dominated resolve latency.
|
|
97
|
+
*/
|
|
98
|
+
export async function processSnapshot() {
|
|
99
|
+
try {
|
|
100
|
+
const { stdout } = await run("ps", ["-eo", "pid=,ppid=,etime=,command="]);
|
|
101
|
+
return parseProcessSnapshot(stdout);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return new Map();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/** Every descendant of `rootPid` in the snapshot (pure breadth-first, cycle-safe). */
|
|
108
|
+
export function descendantsOf(rootPid, snapshot) {
|
|
109
|
+
const childrenByParent = new Map();
|
|
110
|
+
for (const [pid, entry] of snapshot) {
|
|
111
|
+
const siblings = childrenByParent.get(entry.ppid);
|
|
112
|
+
if (siblings)
|
|
113
|
+
siblings.push(pid);
|
|
114
|
+
else
|
|
115
|
+
childrenByParent.set(entry.ppid, [pid]);
|
|
116
|
+
}
|
|
117
|
+
const out = [];
|
|
118
|
+
const seen = new Set([rootPid]);
|
|
119
|
+
let frontier = [rootPid];
|
|
120
|
+
while (frontier.length > 0) {
|
|
121
|
+
const next = [];
|
|
122
|
+
for (const parent of frontier) {
|
|
123
|
+
for (const child of childrenByParent.get(parent) ?? []) {
|
|
124
|
+
if (seen.has(child))
|
|
125
|
+
continue;
|
|
126
|
+
seen.add(child);
|
|
127
|
+
out.push({ pid: child, command: snapshot.get(child)?.command ?? "" });
|
|
128
|
+
next.push(child);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
frontier = next;
|
|
132
|
+
}
|
|
133
|
+
return out;
|
|
134
|
+
}
|
|
135
|
+
/** A process's real working directory: /proc on Linux, lsof on macOS. Null if unknown. */
|
|
136
|
+
export async function processCwd(pid) {
|
|
137
|
+
if (process.platform === "linux") {
|
|
138
|
+
try {
|
|
139
|
+
const { readlink } = await import("node:fs/promises");
|
|
140
|
+
return await readlink(`/proc/${pid}/cwd`);
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const { stdout } = await run("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"]);
|
|
148
|
+
const line = stdout.split("\n").find((l) => l.startsWith("n"));
|
|
149
|
+
return line ? line.slice(1) : null;
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const EMPTY_HINT = {
|
|
156
|
+
argvSession: null,
|
|
157
|
+
agentHarness: null,
|
|
158
|
+
agentCwd: null,
|
|
159
|
+
agentElapsedSeconds: null,
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* What the pane's running agent tells us about its session, from a shared process
|
|
163
|
+
* snapshot. Never throws — every failure (no agent, no permission) degrades to an
|
|
164
|
+
* empty hint, and the resolver falls back to its cwd heuristics.
|
|
165
|
+
*/
|
|
166
|
+
export async function paneProcessHint(panePid, snapshot) {
|
|
167
|
+
try {
|
|
168
|
+
const agent = pickAgentProcess(descendantsOf(panePid, snapshot));
|
|
169
|
+
if (!agent)
|
|
170
|
+
return EMPTY_HINT;
|
|
171
|
+
const argvSession = parseAgentSession(agent.command);
|
|
172
|
+
const agentHarness = argvSession?.harness ?? harnessFromCommand(agent.command);
|
|
173
|
+
// Only pay for the cwd lookup (lsof on macOS, ~100ms) when the argv had no
|
|
174
|
+
// session id — a resumed session is already resolved exactly, so skip it.
|
|
175
|
+
const agentCwd = argvSession ? null : await processCwd(agent.pid);
|
|
176
|
+
const agentElapsedSeconds = snapshot.get(agent.pid)?.elapsedSeconds ?? null;
|
|
177
|
+
return { argvSession, agentHarness, agentCwd, agentElapsedSeconds };
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
return EMPTY_HINT;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=pane-process.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pane-process.js","sourceRoot":"","sources":["../../src/services/pane-process.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diffs the live tmux pane set into durable `PaneIdentity` records, resolving
|
|
3
|
+
* each pane's harness/session, and reports which panes appeared or vanished
|
|
4
|
+
* since the last diff.
|
|
5
|
+
*/
|
|
6
|
+
import type { PaneLink, SessionRecord, TmuxPane } from "../core/types.js";
|
|
7
|
+
import type { PaneIdentity } from "../core/gmux-types.js";
|
|
8
|
+
import type { TmuxGateway } from "./tmux-gateway.js";
|
|
9
|
+
export interface RegistryDiff {
|
|
10
|
+
present: PaneIdentity[];
|
|
11
|
+
appeared: string[];
|
|
12
|
+
vanished: string[];
|
|
13
|
+
}
|
|
14
|
+
export type ResolveFn = (panes: TmuxPane[], records: SessionRecord[], links: PaneLink[]) => Promise<Array<{
|
|
15
|
+
pane: TmuxPane;
|
|
16
|
+
record: SessionRecord | null;
|
|
17
|
+
}>>;
|
|
18
|
+
export declare class PaneRegistry {
|
|
19
|
+
private readonly gateway;
|
|
20
|
+
private readonly resolve;
|
|
21
|
+
private known;
|
|
22
|
+
constructor(gateway: TmuxGateway, resolve?: ResolveFn);
|
|
23
|
+
diff(): Promise<RegistryDiff>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Diffs the live tmux pane set into durable `PaneIdentity` records, resolving
|
|
3
|
+
* each pane's harness/session, and reports which panes appeared or vanished
|
|
4
|
+
* since the last diff.
|
|
5
|
+
*/
|
|
6
|
+
import { cachedRecords } from "./index-store.js";
|
|
7
|
+
import { prunePaneLinks, readPaneLinks } from "./pane-links.js";
|
|
8
|
+
import { resolvePanesLive } from "./tmux-resolve.js";
|
|
9
|
+
export class PaneRegistry {
|
|
10
|
+
gateway;
|
|
11
|
+
resolve;
|
|
12
|
+
known = new Set();
|
|
13
|
+
constructor(gateway, resolve = resolvePanesLive) {
|
|
14
|
+
this.gateway = gateway;
|
|
15
|
+
this.resolve = resolve;
|
|
16
|
+
}
|
|
17
|
+
async diff() {
|
|
18
|
+
const panes = await this.gateway.listPanes();
|
|
19
|
+
const liveIds = panes.map((p) => p.paneId);
|
|
20
|
+
const links = await prunePaneLinks(liveIds).catch(() => readPaneLinks());
|
|
21
|
+
const records = await cachedRecords().catch(() => []);
|
|
22
|
+
const resolved = await this.resolve(panes, records, links);
|
|
23
|
+
const present = resolved.map(({ pane, record }) => ({
|
|
24
|
+
paneId: pane.paneId,
|
|
25
|
+
windowId: pane.windowId,
|
|
26
|
+
active: pane.active,
|
|
27
|
+
harness: record?.harness ?? null,
|
|
28
|
+
sessionId: record?.sessionId ?? null,
|
|
29
|
+
cwd: pane.cwd,
|
|
30
|
+
command: pane.command,
|
|
31
|
+
pid: pane.pid,
|
|
32
|
+
}));
|
|
33
|
+
const liveSet = new Set(liveIds);
|
|
34
|
+
const appeared = liveIds.filter((id) => !this.known.has(id));
|
|
35
|
+
const vanished = [...this.known].filter((id) => !liveSet.has(id));
|
|
36
|
+
this.known = liveSet;
|
|
37
|
+
return { present, appeared, vanished };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=pane-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pane-registry.js","sourceRoot":"","sources":["../../src/services/pane-registry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAerD,MAAM,OAAO,YAAY;IAIJ;IACA;IAJX,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,YACmB,OAAoB,EACpB,UAAqB,gBAAgB;QADrC,YAAO,GAAP,OAAO,CAAa;QACpB,YAAO,GAAP,OAAO,CAA8B;IACrD,CAAC;IAEJ,KAAK,CAAC,IAAI;QACR,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;QAC7C,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,aAAa,EAAE,CAAC,CAAC;QACzE,MAAM,OAAO,GAAG,MAAM,aAAa,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAqB,CAAC,CAAC;QACzE,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QAE3D,MAAM,OAAO,GAAmB,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;YAClE,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,OAAO,EAAE,MAAM,EAAE,OAAO,IAAI,IAAI;YAChC,SAAS,EAAE,MAAM,EAAE,SAAS,IAAI,IAAI;YACpC,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,GAAG,EAAE,IAAI,CAAC,GAAG;SACd,CAAC,CAAC,CAAC;QAEJ,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;QACjC,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7D,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QAClE,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC;QACrB,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACzC,CAAC;CACF"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Running a provider CLI: prompt in on stdin, text out on stdout.
|
|
3
|
+
*
|
|
4
|
+
* The whole vendor abstraction lives here. Both consumers — summaries and
|
|
5
|
+
* `gmux ask` — spawn the same way and differ only in argv and timeout, so the
|
|
6
|
+
* spawn, the timeout, the kill and the stderr capture are written once.
|
|
7
|
+
*
|
|
8
|
+
* Errors are plain `Error`s with a readable message; callers wrap them in their
|
|
9
|
+
* own typed error so the `fix` line can say something specific to what failed.
|
|
10
|
+
*/
|
|
11
|
+
export interface RunProviderOptions {
|
|
12
|
+
timeoutMs: number;
|
|
13
|
+
/**
|
|
14
|
+
* Environment for the child.
|
|
15
|
+
*
|
|
16
|
+
* Defaults to `childEnv()` — ours plus GMUX_CHILD=1 — because every
|
|
17
|
+
* provider we spawn is an agent that may shell back into `gmux`, and a nested
|
|
18
|
+
* `gmux` must not start a background summarize pass of its own.
|
|
19
|
+
*/
|
|
20
|
+
env?: NodeJS.ProcessEnv;
|
|
21
|
+
/**
|
|
22
|
+
* Decoded stdout as it arrives, in order, never a partial UTF-8 sequence,
|
|
23
|
+
* never after the promise settles. Throwing here is swallowed.
|
|
24
|
+
*
|
|
25
|
+
* Every byte handed here is also in the resolved string, in the same order:
|
|
26
|
+
* `resolved === chunks.join("")`. That is what lets a caller record the
|
|
27
|
+
* answer from the resolved string without a late chunk racing it.
|
|
28
|
+
*
|
|
29
|
+
* A tee, not a mechanism. `claude -p` buffers, so against it this fires
|
|
30
|
+
* exactly once — but gmux is provider-agnostic, and a provider that trickles
|
|
31
|
+
* gets incremental rendering out of it for free, with no second code path.
|
|
32
|
+
*/
|
|
33
|
+
onChunk?: (text: string) => void;
|
|
34
|
+
/** Aborting SIGKILLs the child and rejects. */
|
|
35
|
+
signal?: AbortSignal;
|
|
36
|
+
}
|
|
37
|
+
export declare function runProviderCommand(argv: readonly string[], prompt: string, options: RunProviderOptions): Promise<string>;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Running a provider CLI: prompt in on stdin, text out on stdout.
|
|
3
|
+
*
|
|
4
|
+
* The whole vendor abstraction lives here. Both consumers — summaries and
|
|
5
|
+
* `gmux ask` — spawn the same way and differ only in argv and timeout, so the
|
|
6
|
+
* spawn, the timeout, the kill and the stderr capture are written once.
|
|
7
|
+
*
|
|
8
|
+
* Errors are plain `Error`s with a readable message; callers wrap them in their
|
|
9
|
+
* own typed error so the `fix` line can say something specific to what failed.
|
|
10
|
+
*/
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
12
|
+
import { StringDecoder } from "node:string_decoder";
|
|
13
|
+
import { childEnv } from "./config.js";
|
|
14
|
+
export async function runProviderCommand(argv, prompt, options) {
|
|
15
|
+
const [binary, ...args] = argv;
|
|
16
|
+
if (!binary)
|
|
17
|
+
throw new Error("provider command is empty");
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
// Checked before the spawn: a caller that aborts first must not leave a
|
|
20
|
+
// provider running that nobody is waiting for — it would keep billing.
|
|
21
|
+
if (options.signal?.aborted) {
|
|
22
|
+
reject(new Error("aborted"));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const child = spawn(binary, args, {
|
|
26
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
27
|
+
env: options.env ?? childEnv(),
|
|
28
|
+
});
|
|
29
|
+
// Not `String(chunk)`: Node splits a stream at arbitrary byte boundaries, so
|
|
30
|
+
// a 3-byte `—` straddling one decodes as `��` on both sides. The decoder
|
|
31
|
+
// holds the incomplete tail until the rest of it arrives.
|
|
32
|
+
const outDecoder = new StringDecoder("utf8");
|
|
33
|
+
const errDecoder = new StringDecoder("utf8");
|
|
34
|
+
let stdout = "";
|
|
35
|
+
let stderr = "";
|
|
36
|
+
let settled = false;
|
|
37
|
+
const timer = setTimeout(() => {
|
|
38
|
+
finish(() => {
|
|
39
|
+
child.kill("SIGKILL");
|
|
40
|
+
reject(new Error(`timed out after ${options.timeoutMs}ms`));
|
|
41
|
+
});
|
|
42
|
+
}, options.timeoutMs);
|
|
43
|
+
// Not `detached: true`, and that is deliberate: the caller that cancels us
|
|
44
|
+
// is itself the group leader (`spawn(…, { detached: true })` in the worker),
|
|
45
|
+
// and it kills the whole group. A provider in a group of its own would
|
|
46
|
+
// survive that kill — the exact orphan the group kill exists to prevent.
|
|
47
|
+
const onAbort = () => {
|
|
48
|
+
finish(() => {
|
|
49
|
+
child.kill("SIGKILL");
|
|
50
|
+
reject(new Error("aborted"));
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
54
|
+
function finish(fn) {
|
|
55
|
+
if (settled)
|
|
56
|
+
return;
|
|
57
|
+
settled = true;
|
|
58
|
+
clearTimeout(timer);
|
|
59
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
60
|
+
fn();
|
|
61
|
+
}
|
|
62
|
+
// Ordered before the string it tees, so the resolved value can never contain
|
|
63
|
+
// a byte the caller was not handed first. Throwing is the caller's problem,
|
|
64
|
+
// not the provider run's.
|
|
65
|
+
const tee = (text) => {
|
|
66
|
+
if (settled || !text)
|
|
67
|
+
return;
|
|
68
|
+
try {
|
|
69
|
+
options.onChunk?.(text);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
/* swallowed */
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
child.stdout.on("data", (chunk) => {
|
|
76
|
+
const text = outDecoder.write(chunk);
|
|
77
|
+
stdout += text;
|
|
78
|
+
tee(text);
|
|
79
|
+
});
|
|
80
|
+
child.stderr.on("data", (chunk) => (stderr += errDecoder.write(chunk)));
|
|
81
|
+
child.on("error", (error) => finish(() => reject(new Error(error.message))));
|
|
82
|
+
child.on("close", (code) => {
|
|
83
|
+
// `end()` flushes a trailing incomplete sequence — a truncated output's
|
|
84
|
+
// last bytes become `�` rather than vanishing. Teed before settle, so the
|
|
85
|
+
// ordering guarantee holds through the flush.
|
|
86
|
+
const rest = outDecoder.end();
|
|
87
|
+
stdout += rest;
|
|
88
|
+
tee(rest);
|
|
89
|
+
stderr += errDecoder.end();
|
|
90
|
+
finish(() => {
|
|
91
|
+
if (code === 0)
|
|
92
|
+
resolve(stdout);
|
|
93
|
+
else
|
|
94
|
+
reject(new Error(stderr.trim() || `exited with code ${code}`));
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
// EPIPE: a provider that exits before reading its prompt would otherwise
|
|
98
|
+
// take the whole process down with an unhandled error event, turning a
|
|
99
|
+
// provider bug into a gmux crash.
|
|
100
|
+
child.stdin.on("error", () => { });
|
|
101
|
+
child.stdin.write(prompt);
|
|
102
|
+
child.stdin.end();
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=provider-process.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-process.js","sourceRoot":"","sources":["../../src/services/provider-process.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AA6BvC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,IAAuB,EACvB,MAAc,EACd,OAA2B;IAE3B,MAAM,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;IAC/B,IAAI,CAAC,MAAM;QAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAE1D,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC7C,wEAAwE;QACxE,uEAAuE;QACvE,IAAI,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;YAC7B,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,IAAI,EAAE;YAChC,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;YAC/B,GAAG,EAAE,OAAO,CAAC,GAAG,IAAI,QAAQ,EAAE;SAC/B,CAAC,CAAC;QAEH,6EAA6E;QAC7E,yEAAyE;QACzE,0DAA0D;QAC1D,MAAM,UAAU,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;QAC7C,MAAM,UAAU,GAAG,IAAI,aAAa,CAAC,MAAM,CAAC,CAAC;QAE7C,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,OAAO,GAAG,KAAK,CAAC;QAEpB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,MAAM,CAAC,GAAG,EAAE;gBACV,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACtB,MAAM,CAAC,IAAI,KAAK,CAAC,mBAAmB,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC;YAC9D,CAAC,CAAC,CAAC;QACL,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAEtB,2EAA2E;QAC3E,6EAA6E;QAC7E,uEAAuE;QACvE,yEAAyE;QACzE,MAAM,OAAO,GAAG,GAAS,EAAE;YACzB,MAAM,CAAC,GAAG,EAAE;gBACV,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACtB,MAAM,CAAC,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC;YAC/B,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QACF,OAAO,CAAC,MAAM,EAAE,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAEnE,SAAS,MAAM,CAAC,EAAc;YAC5B,IAAI,OAAO;gBAAE,OAAO;YACpB,OAAO,GAAG,IAAI,CAAC;YACf,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC,MAAM,EAAE,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACtD,EAAE,EAAE,CAAC;QACP,CAAC;QAED,6EAA6E;QAC7E,4EAA4E;QAC5E,0BAA0B;QAC1B,MAAM,GAAG,GAAG,CAAC,IAAY,EAAQ,EAAE;YACjC,IAAI,OAAO,IAAI,CAAC,IAAI;gBAAE,OAAO;YAC7B,IAAI,CAAC;gBACH,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;YAAC,MAAM,CAAC;gBACP,eAAe;YACjB,CAAC;QACH,CAAC,CAAC;QAEF,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YACxC,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YACrC,MAAM,IAAI,IAAI,CAAC;YACf,GAAG,CAAC,IAAI,CAAC,CAAC;QACZ,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,CAAC,MAAM,IAAI,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAChF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QAC7E,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,wEAAwE;YACxE,0EAA0E;YAC1E,8CAA8C;YAC9C,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC;YAC9B,MAAM,IAAI,IAAI,CAAC;YACf,GAAG,CAAC,IAAI,CAAC,CAAC;YACV,MAAM,IAAI,UAAU,CAAC,GAAG,EAAE,CAAC;YAC3B,MAAM,CAAC,GAAG,EAAE;gBACV,IAAI,IAAI,KAAK,CAAC;oBAAE,OAAO,CAAC,MAAM,CAAC,CAAC;;oBAC3B,MAAM,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,oBAAoB,IAAI,EAAE,CAAC,CAAC,CAAC;YACtE,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,yEAAyE;QACzE,uEAAuE;QACvE,kCAAkC;QAClC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAClC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC1B,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The catalog of model CLIs gmux knows how to call.
|
|
3
|
+
*
|
|
4
|
+
* This is deliberately NOT `adapters/registry.ts`, and the two must not be
|
|
5
|
+
* merged. They are different axes that only coincidentally share names today:
|
|
6
|
+
*
|
|
7
|
+
* - An **adapter** reads sessions off disk. It answers "what happened?"
|
|
8
|
+
* - A **provider** makes a model call. It answers "what does it mean?"
|
|
9
|
+
*
|
|
10
|
+
* You might read Claude Code transcripts while running summaries through Codex.
|
|
11
|
+
* A provider may exist that no adapter parses. Conflating them would couple two
|
|
12
|
+
* things that have no reason to move together.
|
|
13
|
+
*
|
|
14
|
+
* A provider is described by argv, not by an SDK. That is the entire abstraction
|
|
15
|
+
* — see docs/architecture.md — and it is why adding one is four lines here.
|
|
16
|
+
*/
|
|
17
|
+
import type { ProviderChoice } from "../core/types.js";
|
|
18
|
+
export interface ProviderSpec {
|
|
19
|
+
/** Stable id, recorded in config. */
|
|
20
|
+
id: string;
|
|
21
|
+
displayName: string;
|
|
22
|
+
/** The binary probed on PATH. */
|
|
23
|
+
binary: string;
|
|
24
|
+
/** argv for a one-shot summary call: prompt on stdin, text on stdout. */
|
|
25
|
+
summaryArgv: string[];
|
|
26
|
+
/**
|
|
27
|
+
* argv for an `gmux ask` call.
|
|
28
|
+
*
|
|
29
|
+
* Differs from `summaryArgv` in exactly one way: it grants the model
|
|
30
|
+
* permission to run `gmux grep`, so it can dig past the summaries into the
|
|
31
|
+
* transcripts. The tool loop is the harness's — we parse no tool calls.
|
|
32
|
+
*/
|
|
33
|
+
askArgv: string[];
|
|
34
|
+
/** What to tell a user who hasn't got it. */
|
|
35
|
+
install: string;
|
|
36
|
+
}
|
|
37
|
+
/** The id used when a user supplies their own command. */
|
|
38
|
+
export declare const CUSTOM_PROVIDER_ID = "custom";
|
|
39
|
+
/**
|
|
40
|
+
* Known providers, in preference order — the first one detected becomes the
|
|
41
|
+
* default when there is no config, which reproduces today's `claude -p`.
|
|
42
|
+
*/
|
|
43
|
+
export declare const PROVIDERS: readonly ProviderSpec[];
|
|
44
|
+
/** True when `binary` resolves on PATH. */
|
|
45
|
+
export declare function onPath(binary: string): boolean;
|
|
46
|
+
export declare function providerById(id: string): ProviderSpec | null;
|
|
47
|
+
/** Every catalog provider actually installed on this machine, in catalog order. */
|
|
48
|
+
export declare function detectProviders(): ProviderSpec[];
|
|
49
|
+
/** The provider we'd pick for someone who has never run `gmux setup`. */
|
|
50
|
+
export declare function firstDetected(): ProviderSpec | null;
|
|
51
|
+
/**
|
|
52
|
+
* The summary argv for a configured choice.
|
|
53
|
+
*
|
|
54
|
+
* A catalog provider gets its current `summaryArgv` — NOT the `command` frozen
|
|
55
|
+
* into config at setup time. That stored command is only ever a snapshot of the
|
|
56
|
+
* catalog, and a snapshot goes stale: codex gained `--skip-git-repo-check` after
|
|
57
|
+
* it shipped, and a config written before that would otherwise call codex
|
|
58
|
+
* without the flag forever, failing every summary with "not inside a trusted
|
|
59
|
+
* directory". The catalog is the source of truth for a known id; the stored
|
|
60
|
+
* command matters only for a `custom` provider, whose flags we cannot invent.
|
|
61
|
+
*
|
|
62
|
+
* The exact mirror of `askArgvFor` — the two must move together, or ask
|
|
63
|
+
* self-heals across catalog fixes while summarize silently does not.
|
|
64
|
+
*/
|
|
65
|
+
export declare function summaryArgvFor(choice: ProviderChoice): string[];
|
|
66
|
+
/**
|
|
67
|
+
* The ask argv for a configured choice.
|
|
68
|
+
*
|
|
69
|
+
* A catalog provider gets its `askArgv` — the variant with the grep grant. A
|
|
70
|
+
* custom command cannot: we don't know its flags, so we run it exactly as the
|
|
71
|
+
* user wrote it. It may still answer from the summaries alone, which is the
|
|
72
|
+
* degraded-but-working outcome rather than a broken one.
|
|
73
|
+
*/
|
|
74
|
+
export declare function askArgvFor(choice: ProviderChoice): string[];
|
|
75
|
+
/** Turn a catalog entry into the thing that gets written to config. */
|
|
76
|
+
export declare function toChoice(spec: ProviderSpec): ProviderChoice;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The catalog of model CLIs gmux knows how to call.
|
|
3
|
+
*
|
|
4
|
+
* This is deliberately NOT `adapters/registry.ts`, and the two must not be
|
|
5
|
+
* merged. They are different axes that only coincidentally share names today:
|
|
6
|
+
*
|
|
7
|
+
* - An **adapter** reads sessions off disk. It answers "what happened?"
|
|
8
|
+
* - A **provider** makes a model call. It answers "what does it mean?"
|
|
9
|
+
*
|
|
10
|
+
* You might read Claude Code transcripts while running summaries through Codex.
|
|
11
|
+
* A provider may exist that no adapter parses. Conflating them would couple two
|
|
12
|
+
* things that have no reason to move together.
|
|
13
|
+
*
|
|
14
|
+
* A provider is described by argv, not by an SDK. That is the entire abstraction
|
|
15
|
+
* — see docs/architecture.md — and it is why adding one is four lines here.
|
|
16
|
+
*/
|
|
17
|
+
import { spawnSync } from "node:child_process";
|
|
18
|
+
/** The id used when a user supplies their own command. */
|
|
19
|
+
export const CUSTOM_PROVIDER_ID = "custom";
|
|
20
|
+
/**
|
|
21
|
+
* Known providers, in preference order — the first one detected becomes the
|
|
22
|
+
* default when there is no config, which reproduces today's `claude -p`.
|
|
23
|
+
*/
|
|
24
|
+
export const PROVIDERS = [
|
|
25
|
+
{
|
|
26
|
+
id: "claude-code",
|
|
27
|
+
displayName: "Claude Code",
|
|
28
|
+
binary: "claude",
|
|
29
|
+
summaryArgv: ["claude", "-p"],
|
|
30
|
+
// Bash is scoped to `gmux grep` alone: the model may read what it already has
|
|
31
|
+
// the ids for, and nothing else. A blanket Bash grant would hand a session
|
|
32
|
+
// summariser the whole machine.
|
|
33
|
+
askArgv: ["claude", "-p", "--allowedTools", "Bash(gmux grep:*)"],
|
|
34
|
+
install: "npm install -g @anthropic-ai/claude-code",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: "codex",
|
|
38
|
+
displayName: "Codex",
|
|
39
|
+
binary: "codex",
|
|
40
|
+
// These prompts describe sessions, not the repository gmux happens to be
|
|
41
|
+
// launched from. Codex must therefore work outside a trusted git checkout.
|
|
42
|
+
summaryArgv: ["codex", "exec", "--skip-git-repo-check"],
|
|
43
|
+
askArgv: [
|
|
44
|
+
"codex",
|
|
45
|
+
"exec",
|
|
46
|
+
"--sandbox",
|
|
47
|
+
"read-only",
|
|
48
|
+
"--skip-git-repo-check",
|
|
49
|
+
],
|
|
50
|
+
install: "npm install -g @openai/codex",
|
|
51
|
+
},
|
|
52
|
+
];
|
|
53
|
+
/** True when `binary` resolves on PATH. */
|
|
54
|
+
export function onPath(binary) {
|
|
55
|
+
if (binary.trim() === "")
|
|
56
|
+
return false;
|
|
57
|
+
return spawnSync("which", [binary], { stdio: "ignore" }).status === 0;
|
|
58
|
+
}
|
|
59
|
+
export function providerById(id) {
|
|
60
|
+
return PROVIDERS.find((p) => p.id === id) ?? null;
|
|
61
|
+
}
|
|
62
|
+
/** Every catalog provider actually installed on this machine, in catalog order. */
|
|
63
|
+
export function detectProviders() {
|
|
64
|
+
return PROVIDERS.filter((provider) => onPath(provider.binary));
|
|
65
|
+
}
|
|
66
|
+
/** The provider we'd pick for someone who has never run `gmux setup`. */
|
|
67
|
+
export function firstDetected() {
|
|
68
|
+
return detectProviders()[0] ?? null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The summary argv for a configured choice.
|
|
72
|
+
*
|
|
73
|
+
* A catalog provider gets its current `summaryArgv` — NOT the `command` frozen
|
|
74
|
+
* into config at setup time. That stored command is only ever a snapshot of the
|
|
75
|
+
* catalog, and a snapshot goes stale: codex gained `--skip-git-repo-check` after
|
|
76
|
+
* it shipped, and a config written before that would otherwise call codex
|
|
77
|
+
* without the flag forever, failing every summary with "not inside a trusted
|
|
78
|
+
* directory". The catalog is the source of truth for a known id; the stored
|
|
79
|
+
* command matters only for a `custom` provider, whose flags we cannot invent.
|
|
80
|
+
*
|
|
81
|
+
* The exact mirror of `askArgvFor` — the two must move together, or ask
|
|
82
|
+
* self-heals across catalog fixes while summarize silently does not.
|
|
83
|
+
*/
|
|
84
|
+
export function summaryArgvFor(choice) {
|
|
85
|
+
const spec = providerById(choice.id);
|
|
86
|
+
return spec ? [...spec.summaryArgv] : [...choice.command];
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The ask argv for a configured choice.
|
|
90
|
+
*
|
|
91
|
+
* A catalog provider gets its `askArgv` — the variant with the grep grant. A
|
|
92
|
+
* custom command cannot: we don't know its flags, so we run it exactly as the
|
|
93
|
+
* user wrote it. It may still answer from the summaries alone, which is the
|
|
94
|
+
* degraded-but-working outcome rather than a broken one.
|
|
95
|
+
*/
|
|
96
|
+
export function askArgvFor(choice) {
|
|
97
|
+
const spec = providerById(choice.id);
|
|
98
|
+
return spec ? [...spec.askArgv] : [...choice.command];
|
|
99
|
+
}
|
|
100
|
+
/** Turn a catalog entry into the thing that gets written to config. */
|
|
101
|
+
export function toChoice(spec) {
|
|
102
|
+
return { id: spec.id, command: [...spec.summaryArgv] };
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=providers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../../src/services/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAwB/C,0DAA0D;AAC1D,MAAM,CAAC,MAAM,kBAAkB,GAAG,QAAQ,CAAC;AAE3C;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAA4B;IAChD;QACE,EAAE,EAAE,aAAa;QACjB,WAAW,EAAE,aAAa;QAC1B,MAAM,EAAE,QAAQ;QAChB,WAAW,EAAE,CAAC,QAAQ,EAAE,IAAI,CAAC;QAC7B,8EAA8E;QAC9E,2EAA2E;QAC3E,gCAAgC;QAChC,OAAO,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,gBAAgB,EAAE,mBAAmB,CAAC;QAChE,OAAO,EAAE,0CAA0C;KACpD;IACD;QACE,EAAE,EAAE,OAAO;QACX,WAAW,EAAE,OAAO;QACpB,MAAM,EAAE,OAAO;QACf,yEAAyE;QACzE,2EAA2E;QAC3E,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,uBAAuB,CAAC;QACvD,OAAO,EAAE;YACP,OAAO;YACP,MAAM;YACN,WAAW;YACX,WAAW;YACX,uBAAuB;SACxB;QACD,OAAO,EAAE,8BAA8B;KACxC;CACF,CAAC;AAEF,2CAA2C;AAC3C,MAAM,UAAU,MAAM,CAAC,MAAc;IACnC,IAAI,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IACvC,OAAO,SAAS,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAAU;IACrC,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,IAAI,IAAI,CAAC;AACpD,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,eAAe;IAC7B,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;AACjE,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,aAAa;IAC3B,OAAO,eAAe,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;AACtC,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAAC,MAAsB;IACnD,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAC,MAAsB;IAC/C,MAAM,IAAI,GAAG,YAAY,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;AACxD,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,QAAQ,CAAC,IAAkB;IACzC,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Resolve a user-typed id — usually a short prefix — to exactly one session. */
|
|
2
|
+
import { AmbiguousSessionError, SessionNotFoundError } from "../core/errors.js";
|
|
3
|
+
export function resolveSession(records, id) {
|
|
4
|
+
const needle = id.trim().toLowerCase();
|
|
5
|
+
if (needle === "")
|
|
6
|
+
throw new SessionNotFoundError(id);
|
|
7
|
+
const exact = records.find((r) => r.sessionId.toLowerCase() === needle);
|
|
8
|
+
if (exact)
|
|
9
|
+
return exact;
|
|
10
|
+
const matches = records.filter((r) => r.sessionId.toLowerCase().startsWith(needle));
|
|
11
|
+
if (matches.length === 0)
|
|
12
|
+
throw new SessionNotFoundError(id);
|
|
13
|
+
if (matches.length > 1) {
|
|
14
|
+
throw new AmbiguousSessionError(id, matches.map((m) => m.sessionId));
|
|
15
|
+
}
|
|
16
|
+
return matches[0];
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=resolve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolve.js","sourceRoot":"","sources":["../../src/services/resolve.ts"],"names":[],"mappings":"AAAA,iFAAiF;AAEjF,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAGhF,MAAM,UAAU,cAAc,CAAC,OAAiC,EAAE,EAAU;IAC1E,MAAM,MAAM,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACvC,IAAI,MAAM,KAAK,EAAE;QAAE,MAAM,IAAI,oBAAoB,CAAC,EAAE,CAAC,CAAC;IAEtD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,MAAM,CAAC,CAAC;IACxE,IAAI,KAAK;QAAE,OAAO,KAAK,CAAC;IAExB,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;IACpF,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,oBAAoB,CAAC,EAAE,CAAC,CAAC;IAC7D,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,qBAAqB,CAC7B,EAAE,EACF,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAChC,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC,CAAC,CAAE,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { HostPressure, PaneResources } from "../core/gmux-types.js";
|
|
2
|
+
export interface ResourceDeps {
|
|
3
|
+
psSnapshot: () => Promise<string>;
|
|
4
|
+
hostMemory: () => Promise<{
|
|
5
|
+
usedRatio: number;
|
|
6
|
+
usedBytes: number;
|
|
7
|
+
}>;
|
|
8
|
+
}
|
|
9
|
+
export declare function defaultResourceDeps(): ResourceDeps;
|
|
10
|
+
export declare class ResourceMonitor {
|
|
11
|
+
private deps;
|
|
12
|
+
constructor(deps?: Partial<ResourceDeps>);
|
|
13
|
+
sample(panePids: Map<string, number>): Promise<{
|
|
14
|
+
perPane: Map<string, PaneResources>;
|
|
15
|
+
host: HostPressure;
|
|
16
|
+
}>;
|
|
17
|
+
}
|