71 lines
2.1 KiB
TypeScript
71 lines
2.1 KiB
TypeScript
import { CString, dlopen, FFIType } from "bun:ffi";
|
|
import * as fs from "node:fs";
|
|
import * as os from "node:os";
|
|
|
|
/** Resolve the TTY device path for stdin (fd 0) via POSIX `ttyname(3)`. */
|
|
export function getTtyPath(): string | null {
|
|
if (os.platform() === "linux") {
|
|
// Linux: /proc/self/fd/0 is a symlink to /dev/pts/N
|
|
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 {
|
|
// TTY device path — most reliable, unique per terminal tab
|
|
if (process.stdin.isTTY) {
|
|
try {
|
|
const ttyPath = getTtyPath();
|
|
if (ttyPath?.startsWith("/dev/")) {
|
|
return ttyPath.slice(5).replace(/\//g, "-"); // /dev/pts/3 -> pts-3
|
|
}
|
|
} catch {}
|
|
}
|
|
|
|
// Fallback to terminal-specific env vars
|
|
// Prefer inner multiplexers over host terminal emulators when stdin has no TTY path.
|
|
const tmuxPane = process.env.TMUX_PANE;
|
|
if (tmuxPane) return `tmux-${tmuxPane}`;
|
|
|
|
const cmuxSurface = process.env.CMUX_SURFACE_ID;
|
|
if (cmuxSurface) return `cmux-${cmuxSurface}`;
|
|
|
|
const kittyId = process.env.KITTY_WINDOW_ID;
|
|
if (kittyId) return `kitty-${kittyId}`;
|
|
|
|
const terminalSessionId = process.env.TERM_SESSION_ID; // macOS Terminal.app
|
|
if (terminalSessionId) return `apple-${terminalSessionId}`;
|
|
|
|
const wtSession = process.env.WT_SESSION; // Windows Terminal
|
|
if (wtSession) return `wt-${wtSession}`;
|
|
|
|
return null;
|
|
}
|