[Init] Initial commit - NetMesh terminal manager
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
Some checks failed
build-packages / resolve bundled mosh-client (push) Has been cancelled
build-packages / resolve bundled et-client (push) Has been cancelled
build-packages / build-macos (push) Has been cancelled
build-packages / build-windows (push) Has been cancelled
build-packages / build-linux-x64 (push) Has been cancelled
build-packages / build-linux-arm64 (push) Has been cancelled
build-packages / release (push) Has been cancelled
build-packages / update Nix release metadata (push) Has been cancelled
build-packages / bump homebrew tap (push) Has been cancelled
test / lint-and-test (push) Has been cancelled
AI automation / Route event (push) Has been cancelled
AI automation / Hand reopened issue to maintainers (push) Has been cancelled
AI automation / Clean source issue state (push) Has been cancelled
AI automation / Reconcile handoffs (push) Has been cancelled
AI automation / Classify issue (push) Has been cancelled
AI automation / Claude Code smoke (push) Has been cancelled
AI automation / Review issue follow-up (push) Has been cancelled
AI automation / Publish issue follow-up (push) Has been cancelled
AI automation / Implement with Claude Code (push) Has been cancelled
AI automation / Publish implement PR (push) Has been cancelled
AI automation / Continue queued issue comments (push) Has been cancelled
AI automation / Codex review loop (push) Has been cancelled
AI automation / Publish Codex fix (push) Has been cancelled
AI automation / Clear Codex dispatch marker (push) Has been cancelled
AI automation / Own PR re-request Codex (push) Has been cancelled
AI automation / External PR re-request Codex (push) Has been cancelled
AI automation / Poll Codex reaction / retry (push) Has been cancelled
build-et-binaries / build-linux-x64 (push) Has been cancelled
build-et-binaries / build-linux-arm64 (push) Has been cancelled
build-et-binaries / build-macos-universal (push) Has been cancelled
build-et-binaries / build-windows-x64 (push) Has been cancelled
build-et-binaries / release (push) Has been cancelled
This commit is contained in:
194
electron/bridges/systemManager/tmuxEnv.cjs
Normal file
194
electron/bridges/systemManager/tmuxEnv.cjs
Normal file
@@ -0,0 +1,194 @@
|
||||
/* eslint-disable no-undef */
|
||||
|
||||
function shQuote(str) {
|
||||
return `'${String(str).replace(/'/g, `'\"'\"'`)}'`;
|
||||
}
|
||||
|
||||
function wrapLoginShell(command) {
|
||||
const oneLine = String(command || "")
|
||||
.replace(/\r\n/g, "\n")
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
.join("; ");
|
||||
return `bash -lc ${JSON.stringify(oneLine)}`;
|
||||
}
|
||||
|
||||
function wrapShExec(command) {
|
||||
const oneLine = String(command || "")
|
||||
.replace(/\r\n/g, "\n")
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
.join("; ");
|
||||
return `exec sh -c ${JSON.stringify(oneLine)}`;
|
||||
}
|
||||
|
||||
function stripAnsi(text) {
|
||||
return String(text || "").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "");
|
||||
}
|
||||
|
||||
function parseTmuxVersionString(text) {
|
||||
const match = stripAnsi(text).match(/tmux\s+(\d+)\.(\d+)([a-z0-9]*)/i);
|
||||
if (!match) {
|
||||
return { raw: stripAnsi(text).trim(), major: 0, minor: 0, patch: "" };
|
||||
}
|
||||
return {
|
||||
raw: match[0],
|
||||
major: Number(match[1]) || 0,
|
||||
minor: Number(match[2]) || 0,
|
||||
patch: match[3] || "",
|
||||
};
|
||||
}
|
||||
|
||||
function getListSessionsFormat(version) {
|
||||
const major = version?.major ?? 0;
|
||||
const minor = version?.minor ?? 0;
|
||||
|
||||
if (major < 2) return null;
|
||||
|
||||
const fields = ["#{session_name}", "#{session_windows}", "#{session_attached}"];
|
||||
|
||||
if (major >= 3 || (major === 2 && minor >= 1)) {
|
||||
fields.push("#{session_created}", "#{session_activity}");
|
||||
}
|
||||
|
||||
if (major > 3 || (major === 3 && minor >= 2)) {
|
||||
fields.push("#{session_group}");
|
||||
}
|
||||
|
||||
return fields.join("\\t");
|
||||
}
|
||||
|
||||
// Single-line script — multiline strings break when passed through bash -lc JSON quoting.
|
||||
const TMUX_DETECT_SCRIPT = [
|
||||
"uid=$(id -u 2>/dev/null || echo 0)",
|
||||
"echo \"__TMUX_VERSION__=$(tmux -V 2>/dev/null || true)\"",
|
||||
"echo \"__TMUX_BIN__=$(command -v tmux 2>/dev/null || which tmux 2>/dev/null || true)\"",
|
||||
"for d in \"${TMUX_TMPDIR:-/tmp}/tmux-$uid\" \"/tmp/tmux-$uid\"; do",
|
||||
"[ -d \"$d\" ] || continue",
|
||||
"for s in \"$d\"/*; do [ -S \"$s\" ] && echo \"__SOCKET__=$s\"; done",
|
||||
"done",
|
||||
].join("; ");
|
||||
|
||||
function parseDetectScriptOutput(stdout) {
|
||||
const info = {
|
||||
version: { raw: "", major: 0, minor: 0, patch: "" },
|
||||
binary: "",
|
||||
sockets: [],
|
||||
};
|
||||
|
||||
for (const line of stripAnsi(stdout).split("\n")) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) continue;
|
||||
if (trimmed.startsWith("__TMUX_VERSION__=")) {
|
||||
info.version = parseTmuxVersionString(trimmed.slice("__TMUX_VERSION__=".length));
|
||||
continue;
|
||||
}
|
||||
if (trimmed.startsWith("__TMUX_BIN__=")) {
|
||||
info.binary = trimmed.slice("__TMUX_BIN__=".length).trim();
|
||||
continue;
|
||||
}
|
||||
if (trimmed.startsWith("__SOCKET__=")) {
|
||||
info.sockets.push(trimmed.slice("__SOCKET__=".length).trim());
|
||||
}
|
||||
}
|
||||
|
||||
info.sockets = [...new Set(info.sockets.filter(Boolean))];
|
||||
return info;
|
||||
}
|
||||
|
||||
function normalizeExecResult(result) {
|
||||
if (!result) return { success: false, error: "No exec result" };
|
||||
const stdout = stripAnsi(result.stdout || "");
|
||||
const stderr = stripAnsi(result.stderr || "");
|
||||
const combined = [stderr, stdout].filter(Boolean).join("\n").trim();
|
||||
if (!result.success && combined) {
|
||||
return {
|
||||
...result,
|
||||
success: true,
|
||||
stdout: combined,
|
||||
stderr,
|
||||
code: result.code ?? 1,
|
||||
};
|
||||
}
|
||||
return { ...result, stdout: combined || stdout, stderr };
|
||||
}
|
||||
|
||||
function buildTmuxInvocation(binary, socketPath, args) {
|
||||
const bin = binary || "tmux";
|
||||
const socketFlag = socketPath ? `-S ${shQuote(socketPath)} ` : "";
|
||||
return `${bin} ${socketFlag}${args}`.replace(/\s+/g, " ").trim();
|
||||
}
|
||||
|
||||
// tmux diagnostics that must never be mistaken for session names — a stale
|
||||
// socket makes `tmux ls 2>&1` print "error connecting to /tmp/tmux-0/default
|
||||
// (No such file or directory)", which the bare-name fallback below would
|
||||
// otherwise turn into a phantom session row.
|
||||
const TMUX_DIAGNOSTIC_LINE = /^(error connecting to|no server running|no current client|can't find|lost server|server exited|failed to connect|protocol version mismatch|open terminal failed|invalid option|usage:|unknown command)/i;
|
||||
|
||||
function isTmuxDiagnosticLine(line) {
|
||||
return TMUX_DIAGNOSTIC_LINE.test(String(line || "").trim());
|
||||
}
|
||||
|
||||
function parseListOutput(stdout) {
|
||||
const text = stripAnsi(stdout);
|
||||
const plain = [];
|
||||
const lines = text.split("\n");
|
||||
for (const line of lines) {
|
||||
const trimmed = line.trim();
|
||||
if (!trimmed) continue;
|
||||
if (isTmuxDiagnosticLine(trimmed)) continue;
|
||||
const match = trimmed.match(/^([^:]+):\s*(\d+)\s+windows?\b/i);
|
||||
if (match) {
|
||||
plain.push({
|
||||
name: match[1].trim(),
|
||||
windows: Number(match[2]) || 0,
|
||||
attached: /\battached\b/i.test(trimmed),
|
||||
created: 0,
|
||||
activity: "",
|
||||
group: "",
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const parts = trimmed.split("\t");
|
||||
if (parts.length >= 4) {
|
||||
plain.push({
|
||||
name: parts[0].trim(),
|
||||
windows: Number(parts[1]) || 0,
|
||||
attached: parts[2] === "1",
|
||||
created: Number(parts[3]) || 0,
|
||||
activity: parts[4] || "",
|
||||
group: parts[5] || "",
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (parts.length === 1 && !trimmed.includes(":")) {
|
||||
plain.push({
|
||||
name: trimmed,
|
||||
windows: 0,
|
||||
attached: false,
|
||||
created: 0,
|
||||
activity: "",
|
||||
group: "",
|
||||
});
|
||||
}
|
||||
}
|
||||
return plain;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
shQuote,
|
||||
wrapLoginShell,
|
||||
wrapShExec,
|
||||
stripAnsi,
|
||||
parseTmuxVersionString,
|
||||
getListSessionsFormat,
|
||||
TMUX_DETECT_SCRIPT,
|
||||
parseDetectScriptOutput,
|
||||
normalizeExecResult,
|
||||
buildTmuxInvocation,
|
||||
parseListOutput,
|
||||
isTmuxDiagnosticLine,
|
||||
TMUX_DIAGNOSTIC_LINE,
|
||||
};
|
||||
Reference in New Issue
Block a user