import { CString, dlopen, FFIType } from "bun:ffi";
import * as fs from "node:fs";
import * as os from "node:os";
export function getTtyPath(): string | null {
if (os.platform() === "linux") {
try {
const ttyPath = fs.readlinkSync("/proc/self/fd/0");
if (ttyPath.startsWith("/dev/")) {
return ttyPath;
}
} catch {
return null;
}
} else if (os.platform() !== "win32") {
try {
const libName = os.platform() === "darwin" ? "libSystem.B.dylib" : "libc.so.6";
const lib = dlopen(libName, {
ttyname: { args: [FFIType.i32], returns: FFIType.ptr },
});
try {
const result = lib.symbols.ttyname(0);
return result ? new CString(result).toString() : null;
} finally {
lib.close();
}
} catch {
return null;
}
}
return null;
}
* Get a stable identifier for the current terminal.
* Uses the TTY device path (e.g., /dev/pts/3), falling back to environment
* variables for terminal multiplexers or terminal emulators.
* Returns null if no terminal can be identified (e.g., piped input).
*/
export function getTerminalId(): string | null {
if (process.stdin.isTTY) {
try {
const ttyPath = getTtyPath();
if (ttyPath?.startsWith("/dev/")) {
return ttyPath.slice(5).replace(/\//g, "-");
}
} catch {}
}
const kittyId = process.env.KITTY_WINDOW_ID;
if (kittyId) return `kitty-${kittyId}`;
const tmuxPane = process.env.TMUX_PANE;
if (tmuxPane) return `tmux-${tmuxPane}`;
const terminalSessionId = process.env.TERM_SESSION_ID;
if (terminalSessionId) return `apple-${terminalSessionId}`;
const wtSession = process.env.WT_SESSION;
if (wtSession) return `wt-${wtSession}`;
return null;
}