mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-30 09:22:24 +00:00
143 lines
4.6 KiB
JavaScript
143 lines
4.6 KiB
JavaScript
// The fleet session lives on one tmux server. A sender (Codex MCP, a sandbox,
|
|
// a shell) often has a different TMUX or TMUX_TMPDIR, so a bare `tmux` looks
|
|
// at the wrong server and reports the pane missing. fleet up records the
|
|
// socket. Every later command passes -S and ignores the sender's TMUX.
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import { spawnSync } from 'node:child_process';
|
|
|
|
export function fleetTmuxSession(c) {
|
|
return `fleet-${c?.project || 'fleet'}`.replace(/[.:]/g, '-');
|
|
}
|
|
|
|
export function tmuxSocketFile(c) {
|
|
return path.join(c.stateDir, 'tmux-socket');
|
|
}
|
|
|
|
function readSocketFile(c) {
|
|
if (!c?.stateDir) return '';
|
|
try {
|
|
const sock = fs.readFileSync(tmuxSocketFile(c), 'utf8').trim().split('\n')[0].trim();
|
|
return sock.startsWith('/') ? sock : '';
|
|
} catch {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
function writeSocketFile(c, sock) {
|
|
if (!c?.stateDir || !sock || !sock.startsWith('/')) return;
|
|
fs.mkdirSync(c.stateDir, { recursive: true });
|
|
fs.writeFileSync(tmuxSocketFile(c), `${sock}\n`);
|
|
}
|
|
|
|
function cleanEnv(base, { keepTmux = false } = {}) {
|
|
const env = { ...base };
|
|
if (!keepTmux) delete env.TMUX;
|
|
return env;
|
|
}
|
|
|
|
function hasSession(session, socket, env) {
|
|
const args = [...(socket ? ['-S', socket] : []), 'has-session', '-t', session];
|
|
const r = spawnSync('tmux', args, { encoding: 'utf8', env });
|
|
return r.status === 0;
|
|
}
|
|
|
|
function socketPathOf(session, socket, env) {
|
|
const args = [...(socket ? ['-S', socket] : []), 'display-message', '-p', '-t', session, '#{socket_path}'];
|
|
const r = spawnSync('tmux', args, { encoding: 'utf8', env });
|
|
const sock = String(r.stdout || '').trim();
|
|
return r.status === 0 && sock.startsWith('/') ? sock : '';
|
|
}
|
|
|
|
function socketDirs() {
|
|
const uid = typeof process.getuid === 'function' ? process.getuid() : '';
|
|
const roots = [process.env.TMUX_TMPDIR, process.env.XDG_RUNTIME_DIR, '/tmp'];
|
|
return [...new Set(roots.filter(Boolean).map((root) => path.join(root, `tmux-${uid}`)))];
|
|
}
|
|
|
|
function listSockets() {
|
|
const found = [];
|
|
for (const dir of socketDirs()) {
|
|
let names = [];
|
|
try { names = fs.readdirSync(dir); } catch { continue; }
|
|
for (const name of names) {
|
|
const full = path.join(dir, name);
|
|
try {
|
|
if (fs.statSync(full).isSocket()) found.push(full);
|
|
} catch { /* gone */ }
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/** Socket that actually has this fleet session, or '' if none is visible. */
|
|
function findSocket(session) {
|
|
const bare = cleanEnv(process.env);
|
|
delete bare.TMUX_TMPDIR;
|
|
if (hasSession(session, '', bare)) {
|
|
const pathOf = socketPathOf(session, '', bare);
|
|
if (pathOf) return pathOf;
|
|
}
|
|
const fromEnv = String(process.env.TMUX || '').split(',')[0];
|
|
if (fromEnv.startsWith('/') && hasSession(session, fromEnv, cleanEnv(process.env))) return fromEnv;
|
|
for (const sock of listSockets()) {
|
|
if (sock === fromEnv) continue;
|
|
if (hasSession(session, sock, cleanEnv(process.env))) return sock;
|
|
}
|
|
return '';
|
|
}
|
|
|
|
export function resolveTmuxSocket(c) {
|
|
if (!c) return '';
|
|
if (c._tmuxSocket) return c._tmuxSocket;
|
|
const session = fleetTmuxSession(c);
|
|
const saved = readSocketFile(c);
|
|
if (saved && hasSession(session, saved, cleanEnv(process.env))) {
|
|
c._tmuxSocket = saved;
|
|
return saved;
|
|
}
|
|
const found = findSocket(session);
|
|
if (found) {
|
|
writeSocketFile(c, found);
|
|
c._tmuxSocket = found;
|
|
return found;
|
|
}
|
|
// Keep a recorded path even when the session is not on it, so the miss
|
|
// log names the server we actually queried instead of the sender's TMUX.
|
|
if (saved) {
|
|
c._tmuxSocket = saved;
|
|
return saved;
|
|
}
|
|
return '';
|
|
}
|
|
|
|
/** After fleet up creates or reuses the session, store the server it used. */
|
|
export function rememberTmuxServer(c) {
|
|
const session = fleetTmuxSession(c);
|
|
const known = c?._tmuxSocket || readSocketFile(c);
|
|
const env = cleanEnv(process.env, { keepTmux: !known });
|
|
const sock = socketPathOf(session, known, env);
|
|
if (!sock) return '';
|
|
writeSocketFile(c, sock);
|
|
c._tmuxSocket = sock;
|
|
return sock;
|
|
}
|
|
|
|
export function tmuxSocketFacts(c) {
|
|
const saved = c?._tmuxSocket || readSocketFile(c);
|
|
return {
|
|
socket: saved || null,
|
|
socketExists: saved ? fs.existsSync(saved) : false,
|
|
tmuxEnv: process.env.TMUX || null,
|
|
tmuxTmpdir: process.env.TMUX_TMPDIR || null,
|
|
};
|
|
}
|
|
|
|
export function tmuxCommand(c, args, opts = {}) {
|
|
const socket = resolveTmuxSocket(c);
|
|
const argv = socket ? ['-S', socket, ...args] : args;
|
|
const env = cleanEnv({ ...process.env, ...(opts.env || {}) }, { keepTmux: !socket });
|
|
const { env: _env, ...rest } = opts;
|
|
return spawnSync('tmux', argv, { encoding: 'utf8', ...rest, env });
|
|
}
|