fix(tmux,run,hooks): the four pre-existing bugs the #37 evaluation found
- tmux targets are exact (`=name`, `=name:` for panes/options): a bare `-t agenthub-lane-1` resolves by prefix to agenthub-lane-12 once lane 1 is gone, so killing a finished lane could kill another. - getRun/killRun refuse ids that are not `agenthub-lane-<n>`, so DELETE /api/run/main can no longer kill a user's own tmux session. - router/apply reports restart_required only for a running pane; getRun returns a `gone` handle, so it was always true. - needs_action is cleared only by the session that raised it (new nullable lanes.needs_action_session); a second session in the same cwd used to clear it on its second hook.
This commit is contained in:
@@ -69,6 +69,7 @@ const PATCHABLE = new Set([
|
||||
"gate_decision",
|
||||
"ci_status",
|
||||
"needs_action",
|
||||
"needs_action_session",
|
||||
"links",
|
||||
"notes",
|
||||
"session_id",
|
||||
|
||||
@@ -48,7 +48,7 @@ function attach(ws, runId, { cols, rows }) {
|
||||
} catch {
|
||||
/* session gone or tmux unavailable — attach below will surface that */
|
||||
}
|
||||
const pty = spawnImpl("tmux", ["attach-session", "-t", runId], {
|
||||
const pty = spawnImpl("tmux", ["attach-session", "-t", `=${runId}`], {
|
||||
name: "xterm-256color",
|
||||
cols: cols || 80,
|
||||
rows: rows || 24,
|
||||
|
||||
@@ -322,8 +322,10 @@ function spawnRun(args) {
|
||||
return getRun(id);
|
||||
}
|
||||
|
||||
// Only a dashboard lane pane is a run. Without this, `DELETE /api/run/main`
|
||||
// would kill the user's own tmux session named `main`.
|
||||
function killRun(id) {
|
||||
if (!id || !tmux.hasSession(id)) return false;
|
||||
if (laneIdFromRunId(id) === null || !tmux.hasSession(id)) return false;
|
||||
tmux.killSession(id);
|
||||
if (dashboardRuns) {
|
||||
dashboardRuns.patchRun({ id, status: "killed", endedAt: Date.now() });
|
||||
@@ -353,7 +355,7 @@ function publicRun(id) {
|
||||
}
|
||||
|
||||
function getRun(id) {
|
||||
if (!id) return null;
|
||||
if (laneIdFromRunId(id) === null) return null;
|
||||
return publicRun(id);
|
||||
}
|
||||
|
||||
|
||||
+15
-7
@@ -33,9 +33,16 @@ function __reset() {
|
||||
execImpl = (args) => execFileSync("tmux", args, EXEC_OPTS);
|
||||
}
|
||||
|
||||
// tmux resolves a bare `-t agenthub-lane-1` by PREFIX when no exact match
|
||||
// exists, so it would hit `agenthub-lane-12` once lane 1's session is gone —
|
||||
// and killSession would kill the wrong lane. `=` forces an exact match; pane
|
||||
// and option targets need the trailing `:` (tmux 3.4 rejects `=name` there).
|
||||
const session = (name) => `=${name}`;
|
||||
const pane = (name) => `=${name}:`;
|
||||
|
||||
function hasSession(name) {
|
||||
try {
|
||||
execImpl(["has-session", "-t", name]);
|
||||
execImpl(["has-session", "-t", session(name)]);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
@@ -71,7 +78,7 @@ function newSession({ name, cwd, argv, env }) {
|
||||
// this option existed — or before a server restart picked up the change —
|
||||
// still gets it; `set-option` is idempotent, so re-running it is harmless.
|
||||
function enableMouse(name) {
|
||||
execImpl(["set-option", "-t", name, "mouse", "on"]);
|
||||
execImpl(["set-option", "-t", pane(name), "mouse", "on"]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +89,8 @@ function enableMouse(name) {
|
||||
function paneCommand(name) {
|
||||
try {
|
||||
return (
|
||||
execImpl(["display-message", "-p", "-t", name, "#{pane_current_command}"]).trim() || null
|
||||
execImpl(["display-message", "-p", "-t", pane(name), "#{pane_current_command}"]).trim() ||
|
||||
null
|
||||
);
|
||||
} catch {
|
||||
return null;
|
||||
@@ -107,7 +115,7 @@ function sendCommand(name, argv, env, cwd) {
|
||||
"respawn-pane",
|
||||
"-k",
|
||||
"-t",
|
||||
name,
|
||||
pane(name),
|
||||
...(cwd ? ["-c", cwd] : []),
|
||||
...envFlags(env),
|
||||
"--",
|
||||
@@ -125,14 +133,14 @@ function sendCommand(name, argv, env, cwd) {
|
||||
* only the first line and leave the rest sitting unsent in the input box.
|
||||
*/
|
||||
function sendText(name, text) {
|
||||
execImpl(["send-keys", "-t", name, "-l", text]);
|
||||
execImpl(["send-keys", "-t", name, "Enter"]);
|
||||
execImpl(["send-keys", "-t", pane(name), "-l", text]);
|
||||
execImpl(["send-keys", "-t", pane(name), "Enter"]);
|
||||
}
|
||||
|
||||
/** Idempotent — a session that's already gone is not an error. */
|
||||
function killSession(name) {
|
||||
try {
|
||||
execImpl(["kill-session", "-t", name]);
|
||||
execImpl(["kill-session", "-t", session(name)]);
|
||||
} catch {
|
||||
/* already gone */
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user