webtty 1.9.0 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +128 -8
- package/dist/cli/index.js.map +7 -7
- package/dist/server/index.js +172 -22
- package/dist/server/index.js.map +10 -10
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -17,6 +17,9 @@ var __export = (target, all) => {
|
|
|
17
17
|
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
18
18
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
19
|
|
|
20
|
+
// src/cli/index.ts
|
|
21
|
+
import path4 from "node:path";
|
|
22
|
+
|
|
20
23
|
// src/cli/commands.ts
|
|
21
24
|
import * as childProcess2 from "node:child_process";
|
|
22
25
|
import fs3 from "node:fs";
|
|
@@ -74,7 +77,8 @@ var DEFAULT_CONFIG = {
|
|
|
74
77
|
mouseScrollSpeed: 1,
|
|
75
78
|
logs: false,
|
|
76
79
|
theme: DEFAULT_THEME,
|
|
77
|
-
keyboardBindings: DEFAULT_KEYBOARD_BINDINGS
|
|
80
|
+
keyboardBindings: DEFAULT_KEYBOARD_BINDINGS,
|
|
81
|
+
env: {}
|
|
78
82
|
};
|
|
79
83
|
function bindingKey(b) {
|
|
80
84
|
const mods = [...b.mods ?? []].sort().join("+");
|
|
@@ -156,6 +160,9 @@ function loadConfig() {
|
|
|
156
160
|
...p.theme && typeof p.theme === "object" && { theme: { ...DEFAULT_THEME, ...p.theme } },
|
|
157
161
|
...Array.isArray(p.keyboardBindings) && {
|
|
158
162
|
keyboardBindings: mergeKeyboardBindings(DEFAULT_KEYBOARD_BINDINGS, p.keyboardBindings.filter(isValidBinding).map(normalizeBinding))
|
|
163
|
+
},
|
|
164
|
+
...p.env && typeof p.env === "object" && !Array.isArray(p.env) && {
|
|
165
|
+
env: Object.fromEntries(Object.entries(p.env).filter(([k, v]) => typeof k === "string" && typeof v === "string"))
|
|
159
166
|
}
|
|
160
167
|
};
|
|
161
168
|
}
|
|
@@ -279,7 +286,7 @@ function toBrowserHost(host) {
|
|
|
279
286
|
return `[${host}]`;
|
|
280
287
|
return host;
|
|
281
288
|
}
|
|
282
|
-
async function cmdGo(id = "main") {
|
|
289
|
+
async function cmdGo(id = "main", baseDir) {
|
|
283
290
|
if (!await isServerRunning()) {
|
|
284
291
|
await startServer();
|
|
285
292
|
}
|
|
@@ -291,7 +298,7 @@ async function cmdGo(id = "main") {
|
|
|
291
298
|
const res = await fetch(`${getBaseUrl()}/api/sessions`, {
|
|
292
299
|
method: "POST",
|
|
293
300
|
headers: { "Content-Type": "application/json" },
|
|
294
|
-
body: JSON.stringify({ id })
|
|
301
|
+
body: JSON.stringify({ id, ...baseDir ? { baseDir } : {} })
|
|
295
302
|
});
|
|
296
303
|
if (!res.ok) {
|
|
297
304
|
const body = await res.json();
|
|
@@ -453,6 +460,102 @@ function bytesToDisplay(buf) {
|
|
|
453
460
|
return `\\x${b.toString(16).padStart(2, "0")}`;
|
|
454
461
|
}).join(" ");
|
|
455
462
|
}
|
|
463
|
+
async function cmdRun(id, cmd, args = []) {
|
|
464
|
+
if (!cmd) {
|
|
465
|
+
if (!await isServerRunning()) {
|
|
466
|
+
await startServer();
|
|
467
|
+
}
|
|
468
|
+
const check = await fetch(`${getBaseUrl()}/api/sessions/${encodeURIComponent(id)}`);
|
|
469
|
+
if (check.status !== 200) {
|
|
470
|
+
const res2 = await fetch(`${getBaseUrl()}/api/sessions`, {
|
|
471
|
+
method: "POST",
|
|
472
|
+
headers: { "Content-Type": "application/json" },
|
|
473
|
+
body: JSON.stringify({ id })
|
|
474
|
+
});
|
|
475
|
+
if (!res2.ok) {
|
|
476
|
+
const body = await res2.json();
|
|
477
|
+
console.error(`webtty: ${body.error ?? `failed to create session (${res2.status})`}`);
|
|
478
|
+
process.exit(1);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
const wsUrl = `ws://127.0.0.1:${getPort()}/ws/${encodeURIComponent(id)}/pty?cols=80&rows=24`;
|
|
482
|
+
await new Promise((resolve, reject) => {
|
|
483
|
+
const ws = new WebSocket(wsUrl);
|
|
484
|
+
let opened = false;
|
|
485
|
+
ws.onopen = () => {
|
|
486
|
+
opened = true;
|
|
487
|
+
ws.close(1000);
|
|
488
|
+
resolve();
|
|
489
|
+
};
|
|
490
|
+
ws.onerror = () => reject(new Error("failed to connect to PTY"));
|
|
491
|
+
ws.onclose = (evt) => {
|
|
492
|
+
if (!opened)
|
|
493
|
+
reject(new Error(`failed to start PTY (code ${evt.code})`));
|
|
494
|
+
};
|
|
495
|
+
}).catch((err) => {
|
|
496
|
+
console.error(`webtty: failed to start PTY: ${err.message}`);
|
|
497
|
+
process.exit(1);
|
|
498
|
+
});
|
|
499
|
+
const url = `http://${toBrowserHost(loadConfig().host)}:${getPort()}/s/${id}`;
|
|
500
|
+
console.log(url);
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
if (!await isServerRunning()) {
|
|
504
|
+
console.error("webtty: server is not running");
|
|
505
|
+
process.exit(1);
|
|
506
|
+
}
|
|
507
|
+
let res;
|
|
508
|
+
try {
|
|
509
|
+
res = await fetch(`${getBaseUrl()}/s/${encodeURIComponent(id)}/execute`, {
|
|
510
|
+
method: "POST",
|
|
511
|
+
headers: { "Content-Type": "application/json" },
|
|
512
|
+
body: JSON.stringify({ cmd, args })
|
|
513
|
+
});
|
|
514
|
+
} catch {
|
|
515
|
+
console.error("webtty: server is not running");
|
|
516
|
+
process.exit(1);
|
|
517
|
+
}
|
|
518
|
+
if (res.status === 404) {
|
|
519
|
+
console.error(`webtty: session not found: ${id}`);
|
|
520
|
+
process.exit(1);
|
|
521
|
+
}
|
|
522
|
+
if (res.status === 409) {
|
|
523
|
+
console.error(`webtty: PTY not running for session: ${id}`);
|
|
524
|
+
process.exit(1);
|
|
525
|
+
}
|
|
526
|
+
if (!res.ok || !res.body) {
|
|
527
|
+
console.error(`webtty: run failed (${res.status})`);
|
|
528
|
+
process.exit(1);
|
|
529
|
+
}
|
|
530
|
+
const reader = res.body.getReader();
|
|
531
|
+
const decoder = new TextDecoder;
|
|
532
|
+
let buf = "";
|
|
533
|
+
while (true) {
|
|
534
|
+
const { done, value } = await reader.read();
|
|
535
|
+
if (done)
|
|
536
|
+
break;
|
|
537
|
+
buf += decoder.decode(value, { stream: true });
|
|
538
|
+
const lines = buf.split(`
|
|
539
|
+
`);
|
|
540
|
+
buf = lines.pop() ?? "";
|
|
541
|
+
for (const line of lines) {
|
|
542
|
+
if (!line)
|
|
543
|
+
continue;
|
|
544
|
+
const msg = JSON.parse(line);
|
|
545
|
+
if (msg.stream === "stdout")
|
|
546
|
+
process.stdout.write(String(msg.data));
|
|
547
|
+
else if (msg.stream === "stderr")
|
|
548
|
+
process.stderr.write(String(msg.data));
|
|
549
|
+
else if ("exit" in msg) {
|
|
550
|
+
if (msg.error)
|
|
551
|
+
console.error(`webtty: ${msg.error}`);
|
|
552
|
+
process.exit(msg.exit ?? 1);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
console.error("webtty: connection closed unexpectedly");
|
|
557
|
+
process.exit(1);
|
|
558
|
+
}
|
|
456
559
|
function cmdKey() {
|
|
457
560
|
if (!process.stdin.isTTY) {
|
|
458
561
|
console.error("webtty key: requires an interactive terminal");
|
|
@@ -503,7 +606,7 @@ function cmdKey() {
|
|
|
503
606
|
}
|
|
504
607
|
|
|
505
608
|
// src/cli/index.ts
|
|
506
|
-
var GO_ALIASES = new Set(["go", "a", "
|
|
609
|
+
var GO_ALIASES = new Set(["go", "a", "attach", "open"]);
|
|
507
610
|
function printHelp() {
|
|
508
611
|
const indent = " ";
|
|
509
612
|
const col = 18;
|
|
@@ -520,6 +623,7 @@ function printHelp() {
|
|
|
520
623
|
row("ls [id]", "List all sessions, or filter by id substring"),
|
|
521
624
|
row("rm <id>", "Destroy a session"),
|
|
522
625
|
row("mv <id> <new-id>", "Rename a session"),
|
|
626
|
+
row("run <id> [cmd]", "Start PTY (no browser) or run a command in a session"),
|
|
523
627
|
row("stop", "Stop the webtty server"),
|
|
524
628
|
row("start", "Start the webtty server"),
|
|
525
629
|
row("config", "Open the config file in $VISUAL, $EDITOR, or a default editor"),
|
|
@@ -528,11 +632,18 @@ function printHelp() {
|
|
|
528
632
|
].join(`
|
|
529
633
|
`));
|
|
530
634
|
}
|
|
531
|
-
var
|
|
635
|
+
var allArgs = process.argv.slice(2);
|
|
636
|
+
var dirFlagIdx = allArgs.indexOf("--dir");
|
|
637
|
+
var cliBaseDir;
|
|
638
|
+
if (dirFlagIdx !== -1) {
|
|
639
|
+
cliBaseDir = path4.resolve(allArgs[dirFlagIdx + 1] ?? ".");
|
|
640
|
+
allArgs.splice(dirFlagIdx, 2);
|
|
641
|
+
}
|
|
642
|
+
var [cmd, ...rest] = allArgs;
|
|
532
643
|
if (!cmd) {
|
|
533
|
-
await cmdGo();
|
|
644
|
+
await cmdGo("main", cliBaseDir);
|
|
534
645
|
} else if (GO_ALIASES.has(cmd)) {
|
|
535
|
-
await cmdGo(rest[0]);
|
|
646
|
+
await cmdGo(rest[0] ?? "main", cliBaseDir);
|
|
536
647
|
} else {
|
|
537
648
|
switch (cmd) {
|
|
538
649
|
case "ls":
|
|
@@ -557,6 +668,15 @@ if (!cmd) {
|
|
|
557
668
|
case "config":
|
|
558
669
|
cmdConfig();
|
|
559
670
|
break;
|
|
671
|
+
case "run": {
|
|
672
|
+
const [id, runCmd, ...runArgs] = rest;
|
|
673
|
+
if (!id) {
|
|
674
|
+
console.error("usage: webtty run <id> [cmd [args...]]");
|
|
675
|
+
process.exit(1);
|
|
676
|
+
}
|
|
677
|
+
await cmdRun(id, runCmd, runArgs);
|
|
678
|
+
break;
|
|
679
|
+
}
|
|
560
680
|
case "key":
|
|
561
681
|
cmdKey();
|
|
562
682
|
break;
|
|
@@ -572,4 +692,4 @@ Run \`webtty help\` for usage.`);
|
|
|
572
692
|
}
|
|
573
693
|
}
|
|
574
694
|
|
|
575
|
-
//# debugId=
|
|
695
|
+
//# debugId=5CD11DFCEDA2DDF864756E2164756E21
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/cli/
|
|
3
|
+
"sources": ["../src/cli/index.ts", "../src/cli/commands.ts", "../src/config.ts", "../src/cli/http.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import
|
|
6
|
-
"import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\n/** A single keyboard binding: intercepts `key`+`mods` and sends `chars` to the PTY. */\nexport interface KeyboardBinding {\n /** Key name matched case-insensitively against `KeyboardEvent.key` (e.g. `\"enter\"`, `\"arrowup\"`, `\"a\"`). */\n key: string;\n /** Modifier keys that must be active — and no others — for this binding to match. Accepted values: `\"shift\"`, `\"ctrl\"`, `\"alt\"`, `\"meta\"`. Order irrelevant. Unknown values are filtered out at config load time. */\n mods?: string[];\n /** Byte sequence sent verbatim to the PTY. Standard JSON escapes apply (`\\r`, `\\uXXXX`, etc.). */\n chars: string;\n}\n\n/** Terminal color palette. All keys are optional; omitted keys fall back to the Campbell defaults. */\nexport interface Theme {\n /** Terminal background. */\n background?: string;\n /** Default text color. */\n foreground?: string;\n /** Cursor color. */\n cursor?: string;\n /** Selection highlight. */\n selection?: string;\n /** ANSI 0. */\n black?: string;\n /** ANSI 1. */\n red?: string;\n /** ANSI 2. */\n green?: string;\n /** ANSI 3. */\n yellow?: string;\n /** ANSI 4. */\n blue?: string;\n /** ANSI 5. */\n purple?: string;\n /** ANSI 6. */\n cyan?: string;\n /** ANSI 7. */\n white?: string;\n /** ANSI 8. */\n brightBlack?: string;\n /** ANSI 9. */\n brightRed?: string;\n /** ANSI 10. */\n brightGreen?: string;\n /** ANSI 11. */\n brightYellow?: string;\n /** ANSI 12. */\n brightBlue?: string;\n /** ANSI 13. */\n brightPurple?: string;\n /** ANSI 14. */\n brightCyan?: string;\n /** ANSI 15. */\n brightWhite?: string;\n}\n\n/** Right-click behavior: `\"copyPaste\"` copies selection + clears it if selection exists, otherwise native menu; `\"default\"` always shows native context menu. */\nexport type RightClickBehavior = 'default' | 'copyPaste';\n\n/** Full webtty configuration. All keys are optional in the config file; missing keys fall back to {@link DEFAULT_CONFIG}. */\nexport interface Config {\n /** HTTP listen port; env `PORT` takes precedence. */\n port: number;\n /** Bind address; use `\"0.0.0.0\"` for remote access. */\n host: string;\n /** Shell for new sessions. */\n shell: string;\n /** `$TERM` env var passed to the shell. Fixed to `xterm-256color` — the PTY child talks to webtty, not to the parent terminal. */\n term: string;\n /** `$COLORTERM` env var passed to the shell. */\n colorTerm: string;\n /** PTY history buffer in bytes; used for server-side replay on reload/reconnect. */\n scrollback: number;\n /** Initial terminal width in columns. */\n cols: number;\n /** Initial terminal height in rows. */\n rows: number;\n /** Font size in px. */\n fontSize: number;\n /** CSS font-family stack. */\n fontFamily: string;\n /** Default cursor shape. Apps override at runtime via DECSCUSR — this is the startup default only. */\n cursorStyle: 'block' | 'bar' | 'underline';\n /** Default blink state. Apps override at runtime via DECSCUSR — this is the startup default only. */\n cursorStyleBlink: boolean;\n /** Auto-copy selection to clipboard on mouseup (kitty / Windows Terminal style). */\n copyOnSelect: boolean;\n /** `\"copyPaste\"` copies selection + clears it on right-click if selection exists; `\"default\"` always shows native context menu. */\n rightClickBehavior: RightClickBehavior;\n /** Mouse wheel scroll speed multiplier for apps with mouse tracking. `1` = one SGR per tick. Values `< 1` reduce rate; values `> 1` send multiple SGRs per tick. Must be `> 0`. */\n mouseScrollSpeed: number;\n /** Write server stdout/stderr to `~/.config/webtty/server.log`. Appends on each start. */\n logs: boolean;\n /** Terminal color palette. */\n theme: Theme;\n /** Custom key-to-sequence bindings. Merged with built-in defaults by `(key, mods)` identity. */\n keyboardBindings: KeyboardBinding[];\n}\n\n/** Returns the webtty config directory: `~/.config/webtty`. */\nexport function configDir(): string {\n // os.homedir() is the authoritative home directory. process.env.HOME is used\n // as an override in tests (e.g. to isolate config state), but only when it is\n // an absolute path — guarding against accidental env pollution such as the\n // string \"undefined\" being assigned when HOME was originally unset on Windows.\n const home =\n process.env.HOME && path.isAbsolute(process.env.HOME) ? process.env.HOME : os.homedir();\n return path.join(home, '.config', 'webtty');\n}\n\nfunction getConfigPath(): string {\n return path.join(configDir(), 'config.json');\n}\n\n// NOTE: export for testing only; users should use loadConfig() and initConfig() instead\nexport const DEFAULT_THEME: Theme = {\n background: '#000000',\n foreground: '#CCCCCC',\n cursor: '#FFFFFF',\n selection: '#FFFFFF',\n black: '#0C0C0C',\n red: '#C50F1F',\n green: '#13A10E',\n yellow: '#C19C00',\n blue: '#0037DA',\n purple: '#881798',\n cyan: '#3A96DD',\n white: '#CCCCCC',\n brightBlack: '#767676',\n brightRed: '#E74856',\n brightGreen: '#16C60C',\n brightYellow: '#F9F1A5',\n brightBlue: '#3B78FF',\n brightPurple: '#B4009E',\n brightCyan: '#61D6D6',\n brightWhite: '#F2F2F2',\n};\n\n// NOTE: export for testing only; users should use loadConfig() and initConfig() instead\nexport const DEFAULT_KEYBOARD_BINDINGS: KeyboardBinding[] = [];\n\n// NOTE: export for testing only; users should use loadConfig() and initConfig() instead\nexport const DEFAULT_CONFIG: Config = {\n port: 2346,\n host: '127.0.0.1',\n shell:\n process.platform === 'win32'\n ? (process.env.COMSPEC ?? 'cmd.exe')\n : (process.env.SHELL ?? '/bin/bash'),\n term: 'xterm-256color',\n colorTerm: 'truecolor',\n scrollback: 256 * 1024,\n cols: 80,\n rows: 24,\n fontSize: 13,\n fontFamily: \"Menlo, Consolas, 'DejaVu Sans Mono', monospace\",\n cursorStyle: 'bar',\n cursorStyleBlink: true,\n copyOnSelect: true,\n rightClickBehavior: 'default' as RightClickBehavior,\n mouseScrollSpeed: 1,\n logs: false,\n theme: DEFAULT_THEME,\n keyboardBindings: DEFAULT_KEYBOARD_BINDINGS,\n};\n\nfunction bindingKey(b: KeyboardBinding): string {\n const mods = [...(b.mods ?? [])].sort().join('+');\n return `${b.key.toLowerCase()}|${mods}`;\n}\n\nconst VALID_MODS = new Set(['shift', 'ctrl', 'alt', 'meta']);\n\nfunction isValidBinding(b: unknown): b is KeyboardBinding {\n if (!b || typeof b !== 'object') return false;\n const o = b as Record<string, unknown>;\n if (typeof o.key !== 'string' || typeof o.chars !== 'string') return false;\n if (o.mods !== undefined && !Array.isArray(o.mods)) return false;\n return true;\n}\n\nfunction normalizeBinding(b: KeyboardBinding): KeyboardBinding {\n return {\n ...b,\n key: b.key.toLowerCase(),\n mods: (b.mods ?? []).map((m) => m.toLowerCase()).filter((m) => VALID_MODS.has(m)),\n };\n}\n\n/**\n * Merges user-supplied bindings over a set of defaults by `(key, mods)` identity.\n * User entries replace matching defaults; unmatched user entries are appended.\n *\n * @param defaults - Default bindings to merge over.\n * @param user - User-supplied bindings that override defaults.\n * @returns Merged bindings with user entries replacing matching defaults and new user entries appended.\n */\n// NOTE: export for testing only\nexport function mergeKeyboardBindings(\n defaults: KeyboardBinding[],\n user: KeyboardBinding[],\n): KeyboardBinding[] {\n const deduped = [...new Map(user.map((b) => [bindingKey(b), b])).values()];\n const overrides = new Map(deduped.map((b) => [bindingKey(b), b]));\n const merged = defaults.map((d) => overrides.get(bindingKey(d)) ?? d);\n const defaultKeys = new Set(defaults.map(bindingKey));\n for (const b of deduped) {\n if (!defaultKeys.has(bindingKey(b))) merged.push(b);\n }\n return merged;\n}\n\n/**\n * Load config from `~/.config/webtty/config.json`, merged over `DEFAULT_CONFIG`.\n *\n * On first run (file absent), writes the default port/host stub and returns defaults.\n * Unknown keys and keys with wrong types are silently ignored.\n * Throws if the file exists but cannot be read or contains invalid JSON.\n *\n * @returns The resolved {@link Config}, with all missing keys filled from {@link DEFAULT_CONFIG}.\n */\nexport function loadConfig(): Config {\n if (!fs.existsSync(getConfigPath())) {\n try {\n initConfig();\n } catch (err) {\n console.warn(\n `webtty: failed to write default config to ${getConfigPath()}: ${(err as Error).message}`,\n );\n return { ...DEFAULT_CONFIG };\n }\n }\n\n let raw: string;\n try {\n raw = fs.readFileSync(getConfigPath(), 'utf8');\n } catch (err) {\n throw new Error(\n `webtty: failed to read config at ${getConfigPath()}: ${(err as Error).message}`,\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(`webtty: invalid JSON in config file ${getConfigPath()}`);\n }\n\n const p = parsed as Partial<Config>;\n return {\n ...DEFAULT_CONFIG,\n ...(typeof p.port === 'number' && { port: p.port }),\n ...(typeof p.host === 'string' && { host: p.host }),\n ...(typeof p.shell === 'string' && { shell: p.shell }),\n ...(typeof p.term === 'string' && { term: p.term }),\n ...(typeof p.colorTerm === 'string' && { colorTerm: p.colorTerm }),\n ...(typeof p.scrollback === 'number' && { scrollback: p.scrollback }),\n ...(typeof p.cols === 'number' && { cols: p.cols }),\n ...(typeof p.rows === 'number' && { rows: p.rows }),\n ...(typeof p.fontSize === 'number' && { fontSize: p.fontSize }),\n ...(typeof p.fontFamily === 'string' && { fontFamily: p.fontFamily }),\n ...(typeof p.cursorStyle === 'string' &&\n (p.cursorStyle === 'block' || p.cursorStyle === 'bar' || p.cursorStyle === 'underline') && {\n cursorStyle: p.cursorStyle,\n }),\n ...(typeof p.cursorStyleBlink === 'boolean' && { cursorStyleBlink: p.cursorStyleBlink }),\n ...(typeof p.copyOnSelect === 'boolean' && { copyOnSelect: p.copyOnSelect }),\n ...(typeof p.rightClickBehavior === 'string' && {\n rightClickBehavior: (p.rightClickBehavior === 'copyPaste'\n ? 'copyPaste'\n : 'default') as RightClickBehavior,\n }),\n ...(typeof p.mouseScrollSpeed === 'number' &&\n p.mouseScrollSpeed > 0 && { mouseScrollSpeed: p.mouseScrollSpeed }),\n ...(typeof p.logs === 'boolean' && { logs: p.logs }),\n ...(p.theme && typeof p.theme === 'object' && { theme: { ...DEFAULT_THEME, ...p.theme } }),\n ...(Array.isArray(p.keyboardBindings) && {\n keyboardBindings: mergeKeyboardBindings(\n DEFAULT_KEYBOARD_BINDINGS,\n p.keyboardBindings.filter(isValidBinding).map(normalizeBinding),\n ),\n }),\n };\n}\n\n/**\n * Write the initial config stub (`port` + `host` only) to `~/.config/webtty/config.json`.\n *\n * Only writes the two keys that are safe to persist as defaults; all other\n * keys are intentionally omitted so future webtty versions can add new fields\n * without the stub going stale.\n */\nexport function initConfig(): void {\n fs.mkdirSync(path.dirname(getConfigPath()), { recursive: true });\n const content = JSON.stringify(\n {\n port: DEFAULT_CONFIG.port,\n host: DEFAULT_CONFIG.host,\n },\n null,\n 2,\n );\n fs.writeFileSync(getConfigPath(), content, 'utf8');\n}\n",
|
|
7
|
-
"import * as childProcess from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { configDir, loadConfig } from '../config';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n/** Active server port, read from config on first call (env PORT is intentionally ignored). */\nexport function getPort(): number {\n return loadConfig().port;\n}\n\n/** Base URL for internal CLI↔server API calls (always 127.0.0.1, avoids IPv6 lookup). */\nexport function getBaseUrl(): string {\n return `http://127.0.0.1:${getPort()}`;\n}\n\n/** Returns the path to the server log file: `~/.config/webtty/server.log`. */\nexport function logPath(): string {\n return path.join(configDir(), 'server.log');\n}\n\n/** Returns `true` if the webtty server is reachable and responding to API requests. */\nexport async function isServerRunning(): Promise<boolean> {\n try {\n const res = await fetch(`${getBaseUrl()}/api/sessions`);\n if (!res.ok) return false;\n const body = await res.json();\n return Array.isArray(body);\n } catch {\n return false;\n }\n}\n\n/**\n * Spawns the server process detached, then polls until it is ready or `timeoutMs` expires.\n *\n * @param timeoutMs - Maximum time to wait for the server to start (default: 10000 ms).\n * @param _spawn - Spawn function override for testing (default: childProcess.spawn).\n * @throws Exits the process with code 1 if the server entry is not found or fails to start in time.\n */\nexport async function startServer(timeoutMs = 10000, _spawn = childProcess.spawn): Promise<void> {\n const isBun = typeof (globalThis as Record<string, unknown>).Bun !== 'undefined';\n\n // On Windows, Bun's net.Socket doesn't support the fd-based named-pipe\n // wrapping that node-pty's ConPTY backend requires, so node-pty fails under\n // Bun on Windows. bunx works because its #!/usr/bin/env node shim runs the\n // server with Node.js instead. Mirror that here explicitly.\n const useNode = process.platform === 'win32' && isBun;\n const serverExec = useNode ? 'node' : process.execPath;\n\n // Resolve the server entry. When useNode is true the executor is Node.js,\n // which cannot run .ts files, so we always need the compiled .js output.\n // The CLI may itself run from source (src/cli/) or from dist (dist/cli/);\n // adjust the relative path accordingly so we always land in the same dist/.\n const runningFromSrc = __filename.endsWith('.ts');\n let serverEntry: string;\n if (useNode) {\n serverEntry = runningFromSrc\n ? path.resolve(__dirname, '../../dist/server/index.js') // src/cli → dist/server\n : path.resolve(__dirname, '../server/index.js'); // dist/cli → dist/server\n } else {\n serverEntry = path.resolve(\n __dirname,\n runningFromSrc ? '../server/index.ts' : '../server/index.js',\n );\n }\n if (!fs.existsSync(serverEntry)) {\n console.error(`webtty: server entry not found at ${serverEntry}`);\n (process.exit as (code?: number) => void)(1);\n return;\n }\n\n const config = loadConfig();\n let stdio: childProcess.SpawnOptions['stdio'] = 'ignore';\n let logFd: number | undefined;\n if (config.logs) {\n const log = logPath();\n fs.mkdirSync(path.dirname(log), { recursive: true });\n logFd = fs.openSync(log, 'a');\n stdio = ['ignore', logFd, logFd];\n }\n\n const child = _spawn(serverExec, [serverEntry], {\n detached: true,\n stdio,\n env: { ...process.env, PORT: String(config.port) },\n });\n child.on('error', (err) => {\n const hint = useNode\n ? ' (Node.js must be on PATH when running webtty under Bun on Windows)'\n : '';\n console.error(`webtty: failed to start server: ${err.message}${hint}`);\n process.exit(1);\n });\n child.unref();\n if (logFd !== undefined) fs.closeSync(logFd);\n\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (await isServerRunning()) return;\n await new Promise((r) => setTimeout(r, 100));\n }\n console.error('webtty: server did not start in time');\n process.exit(1);\n}\n\n/**\n * Sends `POST /api/server/stop`, then polls until the server is no longer reachable.\n *\n * @param baseUrl - The server base URL (default: getBaseUrl()).\n * @param timeoutMs - Maximum time to wait for the server to stop (default: 5000 ms).\n * @returns `true` if the server stopped successfully, `false` otherwise.\n */\nexport async function stopServer(\n baseUrl: string = getBaseUrl(),\n timeoutMs = 5000,\n): Promise<boolean> {\n try {\n const res = await fetch(`${baseUrl}/api/server/stop`, { method: 'POST' });\n if (!res.ok) return false;\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (!(await isServerRunning())) return true;\n await new Promise((r) => setTimeout(r, 100));\n }\n return false;\n } catch {\n return false;\n }\n}\n\n/**\n * Opens `url` in the default system browser.\n * No-op in test environments or when `WEBTTY_NO_OPEN=1` is set.\n *\n * @param url - The URL to open.\n * @param _spawn - Spawn function override for testing (default: childProcess.spawn).\n */\nexport function openBrowser(url: string, _spawn = childProcess.spawn): void {\n if (process.env.WEBTTY_NO_OPEN === '1') return;\n if (process.env.NODE_ENV === 'test') return;\n if (process.platform === 'win32') {\n _spawn('cmd.exe', ['/c', 'start', '', url], { detached: true, stdio: 'ignore' }).unref();\n } else {\n const cmd = process.platform === 'darwin' ? 'open' : 'xdg-open';\n _spawn(cmd, [url], { detached: true, stdio: 'ignore' }).unref();\n }\n}\n",
|
|
8
|
-
"import
|
|
5
|
+
"import path from 'node:path';\nimport {\n cmdConfig,\n cmdGo,\n cmdKey,\n cmdList,\n cmdRemove,\n cmdRename,\n cmdRun,\n cmdStart,\n cmdStop,\n} from './commands';\n\nconst GO_ALIASES = new Set(['go', 'a', 'attach', 'open']);\n\nfunction printHelp(): void {\n const indent = ' ';\n const col = 18; // width of the widest term: \"mv <id> <new-id>\"\n const row = (term: string, desc: string) => `${indent}${term.padEnd(col)} ${desc}`;\n\n console.log(\n [\n 'Launch Terminal UI in the browser.',\n '',\n 'USAGE',\n row('webtty', 'Open main session in the browser'),\n row('webtty [command]', 'Execute a specific command'),\n '',\n 'COMMANDS',\n row('go [id]', 'Open a new or existing session in the browser'),\n row('ls [id]', 'List all sessions, or filter by id substring'),\n row('rm <id>', 'Destroy a session'),\n row('mv <id> <new-id>', 'Rename a session'),\n row('run <id> [cmd]', 'Start PTY (no browser) or run a command in a session'),\n row('stop', 'Stop the webtty server'),\n row('start', 'Start the webtty server'),\n row('config', 'Open the config file in $VISUAL, $EDITOR, or a default editor'),\n row('key', 'Capture a key combo and print its chars value for keyboardBindings'),\n row('help', 'Show this help message'),\n ].join('\\n'),\n );\n}\n\nconst allArgs = process.argv.slice(2);\nconst dirFlagIdx = allArgs.indexOf('--dir');\nlet cliBaseDir: string | undefined;\nif (dirFlagIdx !== -1) {\n cliBaseDir = path.resolve(allArgs[dirFlagIdx + 1] ?? '.');\n allArgs.splice(dirFlagIdx, 2);\n}\nconst [cmd, ...rest] = allArgs;\n\nif (!cmd) {\n await cmdGo('main', cliBaseDir);\n} else if (GO_ALIASES.has(cmd)) {\n await cmdGo(rest[0] ?? 'main', cliBaseDir);\n} else {\n switch (cmd) {\n case 'ls':\n case 'list':\n await cmdList(rest[0]);\n break;\n case 'rm':\n case 'remove':\n await cmdRemove(rest[0]);\n break;\n case 'mv':\n case 'move':\n case 'rename':\n await cmdRename(rest[0], rest[1]);\n break;\n case 'stop':\n await cmdStop();\n break;\n case 'start':\n await cmdStart();\n break;\n case 'config':\n cmdConfig();\n break;\n case 'run': {\n const [id, runCmd, ...runArgs] = rest;\n if (!id) {\n console.error('usage: webtty run <id> [cmd [args...]]');\n process.exit(1);\n }\n await cmdRun(id, runCmd, runArgs);\n break;\n }\n case 'key':\n cmdKey();\n break;\n case 'help':\n case '--help':\n case '-h':\n printHelp();\n break;\n default:\n console.error(`webtty: unknown command '${cmd}'\\nRun \\`webtty help\\` for usage.`);\n process.exit(1);\n }\n}\n",
|
|
6
|
+
"import * as childProcess from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { configDir, loadConfig } from '../config';\nimport { getBaseUrl, getPort, isServerRunning, openBrowser, startServer, stopServer } from './http';\n\n/**\n * Converts a bind host to a browser-navigable host.\n * - Bind-all addresses (`0.0.0.0`, `::`) become `localhost`.\n * - Bare IPv6 addresses are bracketed (`::1` → `[::1]`).\n * - All other values pass through unchanged.\n */\nexport function toBrowserHost(host: string): string {\n if (host === '0.0.0.0' || host === '::') return 'localhost';\n if (host.includes(':') && !host.startsWith('[')) return `[${host}]`;\n return host;\n}\n\n/**\n * Opens (or creates) session `id`, starts the server if needed, and opens the URL in the browser.\n *\n * @param id - The session ID to open (default: `'main'`).\n * @param baseDir - Working directory for the PTY shell (optional).\n */\nexport async function cmdGo(id = 'main', baseDir?: string): Promise<void> {\n if (!(await isServerRunning())) {\n await startServer();\n }\n\n let sessionId: string;\n const check = await fetch(`${getBaseUrl()}/api/sessions/${encodeURIComponent(id)}`);\n if (check.status === 200) {\n sessionId = id;\n } else {\n const res = await fetch(`${getBaseUrl()}/api/sessions`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id, ...(baseDir ? { baseDir } : {}) }),\n });\n if (!res.ok) {\n const body = (await res.json()) as { error?: string };\n console.error(`webtty: ${body.error ?? `failed to create session (${res.status})`}`);\n process.exit(1);\n }\n const session = (await res.json()) as { id: string };\n sessionId = session.id;\n }\n\n const url = `http://${toBrowserHost(loadConfig().host)}:${getPort()}/s/${sessionId}`;\n console.log(url);\n openBrowser(url);\n}\n\n/**\n * Lists all active sessions, optionally filtered by a substring of the session ID.\n *\n * @param filter - Optional substring to filter session IDs.\n */\nexport async function cmdList(filter?: string): Promise<void> {\n let res: Response;\n try {\n res = await fetch(`${getBaseUrl()}/api/sessions`);\n } catch {\n console.log('webtty is not running');\n process.exit(1);\n }\n const all = (await res.json()) as Array<{\n id: string;\n connected: boolean;\n createdAt: number;\n }>;\n const sessions = filter ? all.filter((s) => s.id.includes(filter)) : all;\n if (sessions.length === 0) {\n console.log('no sessions');\n return;\n }\n console.log('id\\t\\t\\tconnected\\tcreated');\n for (const s of sessions) {\n const created = new Date(s.createdAt).toLocaleString();\n console.log(`${s.id}\\t\\t\\t${s.connected}\\t\\t${created}`);\n }\n}\n\n/**\n * Removes session `id` and stops the server if no sessions remain.\n *\n * @param id - The session ID to remove.\n */\nexport async function cmdRemove(id?: string): Promise<void> {\n if (!id) {\n console.error('webtty: rm requires a session id');\n process.exit(1);\n }\n let res: Response;\n try {\n res = await fetch(`${getBaseUrl()}/api/sessions/${encodeURIComponent(id)}`, {\n method: 'DELETE',\n });\n } catch {\n console.log('webtty is not running');\n process.exit(1);\n }\n if (res.status === 204) {\n console.log(`removed ${id}`);\n if (res.headers.get('x-sessions-remaining') === '0') {\n await stopServer();\n console.log('no sessions remaining — webtty stopped');\n }\n } else if (res.status === 404) {\n console.error(`session ${id} not found`);\n process.exit(1);\n } else {\n console.error(`webtty rm failed (status: ${res.status})`);\n process.exit(1);\n }\n}\n\n/**\n * Renames session `id` to `newId`.\n *\n * @param id - The current session ID.\n * @param newId - The new session ID.\n */\nexport async function cmdRename(id?: string, newId?: string): Promise<void> {\n if (!id || !newId) {\n console.error('webtty: rename requires two arguments: [id] [new-id]');\n process.exit(1);\n }\n let res: Response;\n try {\n res = await fetch(`${getBaseUrl()}/api/sessions/${encodeURIComponent(id)}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id: newId }),\n });\n } catch {\n console.log('webtty is not running');\n process.exit(1);\n }\n if (res.ok) {\n console.log(`renamed ${id} → ${newId}`);\n } else if (res.status === 404) {\n console.error(`session ${id} not found`);\n process.exit(1);\n } else {\n const body = (await res.json()) as { error?: string };\n console.error(`webtty: ${body.error ?? `rename failed (${res.status})`}`);\n process.exit(1);\n }\n}\n\n/** Stops the server if it is running. */\nexport async function cmdStop(): Promise<void> {\n if (!(await isServerRunning())) {\n console.log('webtty is not running');\n return;\n }\n const ok = await stopServer();\n if (ok) {\n console.log('webtty stopped');\n } else {\n console.error('webtty stop failed');\n process.exit(1);\n }\n}\n\n/** Starts the server if it is not already running. */\nexport async function cmdStart(): Promise<void> {\n if (await isServerRunning()) {\n console.log('webtty is already running');\n return;\n }\n await startServer();\n console.log('webtty started');\n}\n\n/** Opens `~/.config/webtty/config.json` in `$VISUAL` / `$EDITOR`, creating it if absent. */\nexport function cmdConfig(): void {\n const dir = configDir();\n const configPath = path.join(dir, 'config.json');\n fs.mkdirSync(dir, { recursive: true });\n if (!fs.existsSync(configPath)) {\n fs.writeFileSync(configPath, '{}\\n', 'utf8');\n }\n const editor =\n process.env.VISUAL ?? process.env.EDITOR ?? (process.platform === 'win32' ? 'notepad' : 'vi');\n childProcess.spawnSync(editor, [configPath], { stdio: 'inherit' });\n}\n\nexport function bytesToChars(buf: Buffer): string {\n let out = '';\n for (const b of buf) {\n if (b === 0x1b) out += '\\\\u001b';\n else if (b === 0x0d) out += '\\\\r';\n else if (b === 0x09) out += '\\\\t';\n else if (b === 0x0a) out += '\\\\n';\n else if (b === 0x22) out += '\\\\\"';\n else if (b === 0x5c) out += '\\\\\\\\';\n else if (b >= 0x20 && b < 0x7f) out += String.fromCharCode(b);\n else out += `\\\\u${b.toString(16).padStart(4, '0')}`;\n }\n return `\"${out}\"`;\n}\n\nexport function bytesToDisplay(buf: Buffer): string {\n return Array.from(buf)\n .map((b) => {\n if (b === 0x1b) return 'ESC';\n if (b === 0x0d) return 'CR';\n if (b === 0x09) return 'TAB';\n if (b === 0x0a) return 'LF';\n if (b === 0x20) return 'SPC';\n if (b === 0x7f) return 'DEL';\n if (b > 0x20 && b < 0x7f) return String.fromCharCode(b);\n return `\\\\x${b.toString(16).padStart(2, '0')}`;\n })\n .join(' ');\n}\n\nexport async function cmdRun(id: string, cmd?: string, args: string[] = []): Promise<void> {\n if (!cmd) {\n // Warm-up mode: start server + session + PTY, no browser\n if (!(await isServerRunning())) {\n await startServer();\n }\n\n const check = await fetch(`${getBaseUrl()}/api/sessions/${encodeURIComponent(id)}`);\n if (check.status !== 200) {\n const res = await fetch(`${getBaseUrl()}/api/sessions`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ id }),\n });\n if (!res.ok) {\n const body = (await res.json()) as { error?: string };\n console.error(`webtty: ${body.error ?? `failed to create session (${res.status})`}`);\n process.exit(1);\n }\n }\n\n const wsUrl = `ws://127.0.0.1:${getPort()}/ws/${encodeURIComponent(id)}/pty?cols=80&rows=24`;\n await new Promise<void>((resolve, reject) => {\n const ws = new WebSocket(wsUrl);\n let opened = false;\n ws.onopen = () => {\n opened = true;\n ws.close(1000);\n resolve();\n };\n ws.onerror = () => reject(new Error('failed to connect to PTY'));\n ws.onclose = (evt) => {\n if (!opened) reject(new Error(`failed to start PTY (code ${(evt as CloseEvent).code})`));\n };\n }).catch((err: Error) => {\n console.error(`webtty: failed to start PTY: ${err.message}`);\n process.exit(1);\n });\n\n const url = `http://${toBrowserHost(loadConfig().host)}:${getPort()}/s/${id}`;\n console.log(url);\n return;\n }\n\n // Exec mode: headless command execution\n if (!(await isServerRunning())) {\n console.error('webtty: server is not running');\n process.exit(1);\n }\n\n let res: Response;\n try {\n res = await fetch(`${getBaseUrl()}/s/${encodeURIComponent(id)}/execute`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ cmd, args }),\n });\n } catch {\n console.error('webtty: server is not running');\n process.exit(1);\n }\n\n if (res.status === 404) {\n console.error(`webtty: session not found: ${id}`);\n process.exit(1);\n }\n if (res.status === 409) {\n console.error(`webtty: PTY not running for session: ${id}`);\n process.exit(1);\n }\n if (!res.ok || !res.body) {\n console.error(`webtty: run failed (${res.status})`);\n process.exit(1);\n }\n\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buf = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n const lines = buf.split('\\n');\n buf = lines.pop() ?? '';\n for (const line of lines) {\n if (!line) continue;\n const msg = JSON.parse(line) as Record<string, unknown>;\n if (msg.stream === 'stdout') process.stdout.write(String(msg.data));\n else if (msg.stream === 'stderr') process.stderr.write(String(msg.data));\n else if ('exit' in msg) {\n if (msg.error) console.error(`webtty: ${msg.error}`);\n process.exit((msg.exit as number) ?? 1);\n }\n }\n }\n console.error('webtty: connection closed unexpectedly');\n process.exit(1);\n}\n\nexport function cmdKey(): void {\n if (!process.stdin.isTTY) {\n console.error('webtty key: requires an interactive terminal');\n process.exit(1);\n return;\n }\n\n const dim = '\\x1b[2m';\n const bold = '\\x1b[1m';\n const reset = '\\x1b[0m';\n\n const restoreTerminal = () => {\n try {\n process.stdin.setRawMode(false);\n } catch {}\n };\n process.once('exit', restoreTerminal);\n process.once('SIGINT', restoreTerminal);\n process.once('SIGTERM', restoreTerminal);\n\n process.stdin.setRawMode(true);\n process.stdin.resume();\n console.log('Press any key combo to see its chars value. q to quit.\\n');\n console.log(` ${dim}received${reset} → ${bold}chars${reset}`);\n console.log(` ${'─'.repeat(17)}`);\n\n let buf = Buffer.alloc(0);\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const flush = () => {\n if (buf.length === 0) return;\n const display = bytesToDisplay(buf).padEnd(8);\n console.log(` ${dim}${display}${reset} → ${bold}${bytesToChars(buf)}${reset}`);\n buf = Buffer.alloc(0);\n };\n\n process.stdin.on('data', (chunk: Buffer) => {\n if (chunk.length === 1 && chunk[0] === 0x71) {\n process.stdin.setRawMode(false);\n process.removeListener('exit', restoreTerminal);\n process.removeListener('SIGINT', restoreTerminal);\n process.removeListener('SIGTERM', restoreTerminal);\n console.log(` ${'─'.repeat(17)}\\n`);\n process.exit(0);\n }\n buf = Buffer.concat([buf, chunk]);\n if (timer) clearTimeout(timer);\n timer = setTimeout(flush, 50);\n });\n}\n",
|
|
7
|
+
"import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\n/** A single keyboard binding: intercepts `key`+`mods` and sends `chars` to the PTY. */\nexport interface KeyboardBinding {\n /** Key name matched case-insensitively against `KeyboardEvent.key` (e.g. `\"enter\"`, `\"arrowup\"`, `\"a\"`). */\n key: string;\n /** Modifier keys that must be active — and no others — for this binding to match. Accepted values: `\"shift\"`, `\"ctrl\"`, `\"alt\"`, `\"meta\"`. Order irrelevant. Unknown values are filtered out at config load time. */\n mods?: string[];\n /** Byte sequence sent verbatim to the PTY. Standard JSON escapes apply (`\\r`, `\\uXXXX`, etc.). */\n chars: string;\n}\n\n/** Terminal color palette. All keys are optional; omitted keys fall back to the Campbell defaults. */\nexport interface Theme {\n /** Terminal background. */\n background?: string;\n /** Default text color. */\n foreground?: string;\n /** Cursor color. */\n cursor?: string;\n /** Selection highlight. */\n selection?: string;\n /** ANSI 0. */\n black?: string;\n /** ANSI 1. */\n red?: string;\n /** ANSI 2. */\n green?: string;\n /** ANSI 3. */\n yellow?: string;\n /** ANSI 4. */\n blue?: string;\n /** ANSI 5. */\n purple?: string;\n /** ANSI 6. */\n cyan?: string;\n /** ANSI 7. */\n white?: string;\n /** ANSI 8. */\n brightBlack?: string;\n /** ANSI 9. */\n brightRed?: string;\n /** ANSI 10. */\n brightGreen?: string;\n /** ANSI 11. */\n brightYellow?: string;\n /** ANSI 12. */\n brightBlue?: string;\n /** ANSI 13. */\n brightPurple?: string;\n /** ANSI 14. */\n brightCyan?: string;\n /** ANSI 15. */\n brightWhite?: string;\n}\n\n/** Right-click behavior: `\"copyPaste\"` copies selection + clears it if selection exists, otherwise native menu; `\"default\"` always shows native context menu. */\nexport type RightClickBehavior = 'default' | 'copyPaste';\n\n/** Full webtty configuration. All keys are optional in the config file; missing keys fall back to {@link DEFAULT_CONFIG}. */\nexport interface Config {\n /** HTTP listen port; env `PORT` takes precedence. */\n port: number;\n /** Bind address; use `\"0.0.0.0\"` for remote access. */\n host: string;\n /** Shell for new sessions. */\n shell: string;\n /** `$TERM` env var passed to the shell. Fixed to `xterm-256color` — the PTY child talks to webtty, not to the parent terminal. */\n term: string;\n /** `$COLORTERM` env var passed to the shell. */\n colorTerm: string;\n /** PTY history buffer in bytes; used for server-side replay on reload/reconnect. */\n scrollback: number;\n /** Initial terminal width in columns. */\n cols: number;\n /** Initial terminal height in rows. */\n rows: number;\n /** Font size in px. */\n fontSize: number;\n /** CSS font-family stack. */\n fontFamily: string;\n /** Default cursor shape. Apps override at runtime via DECSCUSR — this is the startup default only. */\n cursorStyle: 'block' | 'bar' | 'underline';\n /** Default blink state. Apps override at runtime via DECSCUSR — this is the startup default only. */\n cursorStyleBlink: boolean;\n /** Auto-copy selection to clipboard on mouseup (kitty / Windows Terminal style). */\n copyOnSelect: boolean;\n /** `\"copyPaste\"` copies selection + clears it on right-click if selection exists; `\"default\"` always shows native context menu. */\n rightClickBehavior: RightClickBehavior;\n /** Mouse wheel scroll speed multiplier for apps with mouse tracking. `1` = one SGR per tick. Values `< 1` reduce rate; values `> 1` send multiple SGRs per tick. Must be `> 0`. */\n mouseScrollSpeed: number;\n /** Write server stdout/stderr to `~/.config/webtty/server.log`. Appends on each start. */\n logs: boolean;\n /** Terminal color palette. */\n theme: Theme;\n /** Custom key-to-sequence bindings. Merged with built-in defaults by `(key, mods)` identity. */\n keyboardBindings: KeyboardBinding[];\n /** Additional environment variables injected into PTY shells and execute commands. Merges over `process.env`; `TERM` and `COLORTERM` always take precedence for PTY spawns. */\n env: Record<string, string>;\n}\n\n/** Returns the webtty config directory: `~/.config/webtty`. */\nexport function configDir(): string {\n // os.homedir() is the authoritative home directory. process.env.HOME is used\n // as an override in tests (e.g. to isolate config state), but only when it is\n // an absolute path — guarding against accidental env pollution such as the\n // string \"undefined\" being assigned when HOME was originally unset on Windows.\n const home =\n process.env.HOME && path.isAbsolute(process.env.HOME) ? process.env.HOME : os.homedir();\n return path.join(home, '.config', 'webtty');\n}\n\nfunction getConfigPath(): string {\n return path.join(configDir(), 'config.json');\n}\n\n// NOTE: export for testing only; users should use loadConfig() and initConfig() instead\nexport const DEFAULT_THEME: Theme = {\n background: '#000000',\n foreground: '#CCCCCC',\n cursor: '#FFFFFF',\n selection: '#FFFFFF',\n black: '#0C0C0C',\n red: '#C50F1F',\n green: '#13A10E',\n yellow: '#C19C00',\n blue: '#0037DA',\n purple: '#881798',\n cyan: '#3A96DD',\n white: '#CCCCCC',\n brightBlack: '#767676',\n brightRed: '#E74856',\n brightGreen: '#16C60C',\n brightYellow: '#F9F1A5',\n brightBlue: '#3B78FF',\n brightPurple: '#B4009E',\n brightCyan: '#61D6D6',\n brightWhite: '#F2F2F2',\n};\n\n// NOTE: export for testing only; users should use loadConfig() and initConfig() instead\nexport const DEFAULT_KEYBOARD_BINDINGS: KeyboardBinding[] = [];\n\n// NOTE: export for testing only; users should use loadConfig() and initConfig() instead\nexport const DEFAULT_CONFIG: Config = {\n port: 2346,\n host: '127.0.0.1',\n shell:\n process.platform === 'win32'\n ? (process.env.COMSPEC ?? 'cmd.exe')\n : (process.env.SHELL ?? '/bin/bash'),\n term: 'xterm-256color',\n colorTerm: 'truecolor',\n scrollback: 256 * 1024,\n cols: 80,\n rows: 24,\n fontSize: 13,\n fontFamily: \"Menlo, Consolas, 'DejaVu Sans Mono', monospace\",\n cursorStyle: 'bar',\n cursorStyleBlink: true,\n copyOnSelect: true,\n rightClickBehavior: 'default' as RightClickBehavior,\n mouseScrollSpeed: 1,\n logs: false,\n theme: DEFAULT_THEME,\n keyboardBindings: DEFAULT_KEYBOARD_BINDINGS,\n env: {},\n};\n\nfunction bindingKey(b: KeyboardBinding): string {\n const mods = [...(b.mods ?? [])].sort().join('+');\n return `${b.key.toLowerCase()}|${mods}`;\n}\n\nconst VALID_MODS = new Set(['shift', 'ctrl', 'alt', 'meta']);\n\nfunction isValidBinding(b: unknown): b is KeyboardBinding {\n if (!b || typeof b !== 'object') return false;\n const o = b as Record<string, unknown>;\n if (typeof o.key !== 'string' || typeof o.chars !== 'string') return false;\n if (o.mods !== undefined && !Array.isArray(o.mods)) return false;\n return true;\n}\n\nfunction normalizeBinding(b: KeyboardBinding): KeyboardBinding {\n return {\n ...b,\n key: b.key.toLowerCase(),\n mods: (b.mods ?? []).map((m) => m.toLowerCase()).filter((m) => VALID_MODS.has(m)),\n };\n}\n\n/**\n * Merges user-supplied bindings over a set of defaults by `(key, mods)` identity.\n * User entries replace matching defaults; unmatched user entries are appended.\n *\n * @param defaults - Default bindings to merge over.\n * @param user - User-supplied bindings that override defaults.\n * @returns Merged bindings with user entries replacing matching defaults and new user entries appended.\n */\n// NOTE: export for testing only\nexport function mergeKeyboardBindings(\n defaults: KeyboardBinding[],\n user: KeyboardBinding[],\n): KeyboardBinding[] {\n const deduped = [...new Map(user.map((b) => [bindingKey(b), b])).values()];\n const overrides = new Map(deduped.map((b) => [bindingKey(b), b]));\n const merged = defaults.map((d) => overrides.get(bindingKey(d)) ?? d);\n const defaultKeys = new Set(defaults.map(bindingKey));\n for (const b of deduped) {\n if (!defaultKeys.has(bindingKey(b))) merged.push(b);\n }\n return merged;\n}\n\n/**\n * Load config from `~/.config/webtty/config.json`, merged over `DEFAULT_CONFIG`.\n *\n * On first run (file absent), writes the default port/host stub and returns defaults.\n * Unknown keys and keys with wrong types are silently ignored.\n * Throws if the file exists but cannot be read or contains invalid JSON.\n *\n * @returns The resolved {@link Config}, with all missing keys filled from {@link DEFAULT_CONFIG}.\n */\nexport function loadConfig(): Config {\n if (!fs.existsSync(getConfigPath())) {\n try {\n initConfig();\n } catch (err) {\n console.warn(\n `webtty: failed to write default config to ${getConfigPath()}: ${(err as Error).message}`,\n );\n return { ...DEFAULT_CONFIG };\n }\n }\n\n let raw: string;\n try {\n raw = fs.readFileSync(getConfigPath(), 'utf8');\n } catch (err) {\n throw new Error(\n `webtty: failed to read config at ${getConfigPath()}: ${(err as Error).message}`,\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(`webtty: invalid JSON in config file ${getConfigPath()}`);\n }\n\n const p = parsed as Partial<Config>;\n return {\n ...DEFAULT_CONFIG,\n ...(typeof p.port === 'number' && { port: p.port }),\n ...(typeof p.host === 'string' && { host: p.host }),\n ...(typeof p.shell === 'string' && { shell: p.shell }),\n ...(typeof p.term === 'string' && { term: p.term }),\n ...(typeof p.colorTerm === 'string' && { colorTerm: p.colorTerm }),\n ...(typeof p.scrollback === 'number' && { scrollback: p.scrollback }),\n ...(typeof p.cols === 'number' && { cols: p.cols }),\n ...(typeof p.rows === 'number' && { rows: p.rows }),\n ...(typeof p.fontSize === 'number' && { fontSize: p.fontSize }),\n ...(typeof p.fontFamily === 'string' && { fontFamily: p.fontFamily }),\n ...(typeof p.cursorStyle === 'string' &&\n (p.cursorStyle === 'block' || p.cursorStyle === 'bar' || p.cursorStyle === 'underline') && {\n cursorStyle: p.cursorStyle,\n }),\n ...(typeof p.cursorStyleBlink === 'boolean' && { cursorStyleBlink: p.cursorStyleBlink }),\n ...(typeof p.copyOnSelect === 'boolean' && { copyOnSelect: p.copyOnSelect }),\n ...(typeof p.rightClickBehavior === 'string' && {\n rightClickBehavior: (p.rightClickBehavior === 'copyPaste'\n ? 'copyPaste'\n : 'default') as RightClickBehavior,\n }),\n ...(typeof p.mouseScrollSpeed === 'number' &&\n p.mouseScrollSpeed > 0 && { mouseScrollSpeed: p.mouseScrollSpeed }),\n ...(typeof p.logs === 'boolean' && { logs: p.logs }),\n ...(p.theme && typeof p.theme === 'object' && { theme: { ...DEFAULT_THEME, ...p.theme } }),\n ...(Array.isArray(p.keyboardBindings) && {\n keyboardBindings: mergeKeyboardBindings(\n DEFAULT_KEYBOARD_BINDINGS,\n p.keyboardBindings.filter(isValidBinding).map(normalizeBinding),\n ),\n }),\n ...(p.env &&\n typeof p.env === 'object' &&\n !Array.isArray(p.env) && {\n env: Object.fromEntries(\n Object.entries(p.env as Record<string, unknown>).filter(\n ([k, v]) => typeof k === 'string' && typeof v === 'string',\n ) as [string, string][],\n ),\n }),\n };\n}\n\n/**\n * Write the initial config stub (`port` + `host` only) to `~/.config/webtty/config.json`.\n *\n * Only writes the two keys that are safe to persist as defaults; all other\n * keys are intentionally omitted so future webtty versions can add new fields\n * without the stub going stale.\n */\nexport function initConfig(): void {\n fs.mkdirSync(path.dirname(getConfigPath()), { recursive: true });\n const content = JSON.stringify(\n {\n port: DEFAULT_CONFIG.port,\n host: DEFAULT_CONFIG.host,\n },\n null,\n 2,\n );\n fs.writeFileSync(getConfigPath(), content, 'utf8');\n}\n",
|
|
8
|
+
"import * as childProcess from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { configDir, loadConfig } from '../config';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n/** Active server port, read from config on first call (env PORT is intentionally ignored). */\nexport function getPort(): number {\n return loadConfig().port;\n}\n\n/** Base URL for internal CLI↔server API calls (always 127.0.0.1, avoids IPv6 lookup). */\nexport function getBaseUrl(): string {\n return `http://127.0.0.1:${getPort()}`;\n}\n\n/** Returns the path to the server log file: `~/.config/webtty/server.log`. */\nexport function logPath(): string {\n return path.join(configDir(), 'server.log');\n}\n\n/** Returns `true` if the webtty server is reachable and responding to API requests. */\nexport async function isServerRunning(): Promise<boolean> {\n try {\n const res = await fetch(`${getBaseUrl()}/api/sessions`);\n if (!res.ok) return false;\n const body = await res.json();\n return Array.isArray(body);\n } catch {\n return false;\n }\n}\n\n/**\n * Spawns the server process detached, then polls until it is ready or `timeoutMs` expires.\n *\n * @param timeoutMs - Maximum time to wait for the server to start (default: 10000 ms).\n * @param _spawn - Spawn function override for testing (default: childProcess.spawn).\n * @throws Exits the process with code 1 if the server entry is not found or fails to start in time.\n */\nexport async function startServer(timeoutMs = 10000, _spawn = childProcess.spawn): Promise<void> {\n const isBun = typeof (globalThis as Record<string, unknown>).Bun !== 'undefined';\n\n // On Windows, Bun's net.Socket doesn't support the fd-based named-pipe\n // wrapping that node-pty's ConPTY backend requires, so node-pty fails under\n // Bun on Windows. bunx works because its #!/usr/bin/env node shim runs the\n // server with Node.js instead. Mirror that here explicitly.\n const useNode = process.platform === 'win32' && isBun;\n const serverExec = useNode ? 'node' : process.execPath;\n\n // Resolve the server entry. When useNode is true the executor is Node.js,\n // which cannot run .ts files, so we always need the compiled .js output.\n // The CLI may itself run from source (src/cli/) or from dist (dist/cli/);\n // adjust the relative path accordingly so we always land in the same dist/.\n const runningFromSrc = __filename.endsWith('.ts');\n let serverEntry: string;\n if (useNode) {\n serverEntry = runningFromSrc\n ? path.resolve(__dirname, '../../dist/server/index.js') // src/cli → dist/server\n : path.resolve(__dirname, '../server/index.js'); // dist/cli → dist/server\n } else {\n serverEntry = path.resolve(\n __dirname,\n runningFromSrc ? '../server/index.ts' : '../server/index.js',\n );\n }\n if (!fs.existsSync(serverEntry)) {\n console.error(`webtty: server entry not found at ${serverEntry}`);\n (process.exit as (code?: number) => void)(1);\n return;\n }\n\n const config = loadConfig();\n let stdio: childProcess.SpawnOptions['stdio'] = 'ignore';\n let logFd: number | undefined;\n if (config.logs) {\n const log = logPath();\n fs.mkdirSync(path.dirname(log), { recursive: true });\n logFd = fs.openSync(log, 'a');\n stdio = ['ignore', logFd, logFd];\n }\n\n const child = _spawn(serverExec, [serverEntry], {\n detached: true,\n stdio,\n env: { ...process.env, PORT: String(config.port) },\n });\n child.on('error', (err) => {\n const hint = useNode\n ? ' (Node.js must be on PATH when running webtty under Bun on Windows)'\n : '';\n console.error(`webtty: failed to start server: ${err.message}${hint}`);\n process.exit(1);\n });\n child.unref();\n if (logFd !== undefined) fs.closeSync(logFd);\n\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (await isServerRunning()) return;\n await new Promise((r) => setTimeout(r, 100));\n }\n console.error('webtty: server did not start in time');\n process.exit(1);\n}\n\n/**\n * Sends `POST /api/server/stop`, then polls until the server is no longer reachable.\n *\n * @param baseUrl - The server base URL (default: getBaseUrl()).\n * @param timeoutMs - Maximum time to wait for the server to stop (default: 5000 ms).\n * @returns `true` if the server stopped successfully, `false` otherwise.\n */\nexport async function stopServer(\n baseUrl: string = getBaseUrl(),\n timeoutMs = 5000,\n): Promise<boolean> {\n try {\n const res = await fetch(`${baseUrl}/api/server/stop`, { method: 'POST' });\n if (!res.ok) return false;\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (!(await isServerRunning())) return true;\n await new Promise((r) => setTimeout(r, 100));\n }\n return false;\n } catch {\n return false;\n }\n}\n\n/**\n * Opens `url` in the default system browser.\n * No-op in test environments or when `WEBTTY_NO_OPEN=1` is set.\n *\n * @param url - The URL to open.\n * @param _spawn - Spawn function override for testing (default: childProcess.spawn).\n */\nexport function openBrowser(url: string, _spawn = childProcess.spawn): void {\n if (process.env.WEBTTY_NO_OPEN === '1') return;\n if (process.env.NODE_ENV === 'test') return;\n if (process.platform === 'win32') {\n _spawn('cmd.exe', ['/c', 'start', '', url], { detached: true, stdio: 'ignore' }).unref();\n } else {\n const cmd = process.platform === 'darwin' ? 'open' : 'xdg-open';\n _spawn(cmd, [url], { detached: true, stdio: 'ignore' }).unref();\n }\n}\n"
|
|
9
9
|
],
|
|
10
|
-
"mappings": "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",
|
|
11
|
-
"debugId": "
|
|
10
|
+
"mappings": "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",
|
|
11
|
+
"debugId": "5CD11DFCEDA2DDF864756E2164756E21",
|
|
12
12
|
"names": []
|
|
13
13
|
}
|
package/dist/server/index.js
CHANGED
|
@@ -22,8 +22,7 @@ var exports_bun = {};
|
|
|
22
22
|
__export(exports_bun, {
|
|
23
23
|
spawn: () => spawn
|
|
24
24
|
});
|
|
25
|
-
|
|
26
|
-
function spawn(shell, cols, rows, term, colorTerm) {
|
|
25
|
+
function spawn(shell, cols, rows, term, colorTerm, cwd, env) {
|
|
27
26
|
let onDataCb;
|
|
28
27
|
let onExitCb;
|
|
29
28
|
const proc = Bun.spawn([shell], {
|
|
@@ -34,8 +33,8 @@ function spawn(shell, cols, rows, term, colorTerm) {
|
|
|
34
33
|
onDataCb?.(Buffer.from(data).toString("utf8"));
|
|
35
34
|
}
|
|
36
35
|
},
|
|
37
|
-
cwd
|
|
38
|
-
env: { ...process.env, TERM: term, COLORTERM: colorTerm }
|
|
36
|
+
cwd,
|
|
37
|
+
env: { ...process.env, ...env, TERM: term, COLORTERM: colorTerm }
|
|
39
38
|
});
|
|
40
39
|
proc.exited.then((exitCode) => {
|
|
41
40
|
onExitCb?.({ exitCode: exitCode ?? 0 });
|
|
@@ -59,23 +58,21 @@ function spawn(shell, cols, rows, term, colorTerm) {
|
|
|
59
58
|
}
|
|
60
59
|
};
|
|
61
60
|
}
|
|
62
|
-
var init_bun = () => {};
|
|
63
61
|
|
|
64
62
|
// src/pty/node.ts
|
|
65
63
|
var exports_node = {};
|
|
66
64
|
__export(exports_node, {
|
|
67
65
|
spawn: () => spawn2
|
|
68
66
|
});
|
|
69
|
-
import { homedir as homedir2 } from "node:os";
|
|
70
67
|
import nodePty from "@lydell/node-pty";
|
|
71
|
-
function spawn2(shell, cols, rows, term, colorTerm) {
|
|
68
|
+
function spawn2(shell, cols, rows, term, colorTerm, cwd, env) {
|
|
72
69
|
const shellArgs = process.platform === "win32" && /cmd\.exe$/i.test(shell) ? ["/d"] : [];
|
|
73
70
|
const ptyProc = nodePty.spawn(shell, shellArgs, {
|
|
74
71
|
name: term,
|
|
75
72
|
cols,
|
|
76
73
|
rows,
|
|
77
|
-
cwd
|
|
78
|
-
env: { ...process.env, TERM: term, COLORTERM: colorTerm }
|
|
74
|
+
cwd,
|
|
75
|
+
env: { ...process.env, ...env, TERM: term, COLORTERM: colorTerm }
|
|
79
76
|
});
|
|
80
77
|
return {
|
|
81
78
|
pid: ptyProc.pid,
|
|
@@ -99,7 +96,7 @@ function spawn2(shell, cols, rows, term, colorTerm) {
|
|
|
99
96
|
var init_node = () => {};
|
|
100
97
|
|
|
101
98
|
// src/server/index.ts
|
|
102
|
-
import
|
|
99
|
+
import fs4 from "node:fs";
|
|
103
100
|
import http from "node:http";
|
|
104
101
|
import path4 from "node:path";
|
|
105
102
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
@@ -156,7 +153,8 @@ var DEFAULT_CONFIG = {
|
|
|
156
153
|
mouseScrollSpeed: 1,
|
|
157
154
|
logs: false,
|
|
158
155
|
theme: DEFAULT_THEME,
|
|
159
|
-
keyboardBindings: DEFAULT_KEYBOARD_BINDINGS
|
|
156
|
+
keyboardBindings: DEFAULT_KEYBOARD_BINDINGS,
|
|
157
|
+
env: {}
|
|
160
158
|
};
|
|
161
159
|
function bindingKey(b) {
|
|
162
160
|
const mods = [...b.mods ?? []].sort().join("+");
|
|
@@ -238,6 +236,9 @@ function loadConfig() {
|
|
|
238
236
|
...p.theme && typeof p.theme === "object" && { theme: { ...DEFAULT_THEME, ...p.theme } },
|
|
239
237
|
...Array.isArray(p.keyboardBindings) && {
|
|
240
238
|
keyboardBindings: mergeKeyboardBindings(DEFAULT_KEYBOARD_BINDINGS, p.keyboardBindings.filter(isValidBinding).map(normalizeBinding))
|
|
239
|
+
},
|
|
240
|
+
...p.env && typeof p.env === "object" && !Array.isArray(p.env) && {
|
|
241
|
+
env: Object.fromEntries(Object.entries(p.env).filter(([k, v]) => typeof k === "string" && typeof v === "string"))
|
|
241
242
|
}
|
|
242
243
|
};
|
|
243
244
|
}
|
|
@@ -251,9 +252,13 @@ function initConfig() {
|
|
|
251
252
|
}
|
|
252
253
|
|
|
253
254
|
// src/server/routes.ts
|
|
255
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
256
|
+
import fs3 from "node:fs";
|
|
257
|
+
import { homedir as homedir2 } from "node:os";
|
|
254
258
|
import path3 from "node:path";
|
|
255
259
|
|
|
256
260
|
// src/server/session.ts
|
|
261
|
+
import { homedir } from "node:os";
|
|
257
262
|
var sessionRegistry = new Map;
|
|
258
263
|
var lastUsedId = null;
|
|
259
264
|
function setLastUsedId(id) {
|
|
@@ -266,10 +271,11 @@ function isValidId(id) {
|
|
|
266
271
|
function generateId() {
|
|
267
272
|
return Math.floor(Math.random() * 4294967295).toString(16).padStart(8, "0");
|
|
268
273
|
}
|
|
269
|
-
function createSession(id) {
|
|
274
|
+
function createSession(id, baseDir = homedir()) {
|
|
270
275
|
const session = {
|
|
271
276
|
id,
|
|
272
277
|
createdAt: Date.now(),
|
|
278
|
+
baseDir,
|
|
273
279
|
pty: null,
|
|
274
280
|
clients: new Set,
|
|
275
281
|
subscribers: new Set,
|
|
@@ -282,6 +288,7 @@ function sessionToJson(s) {
|
|
|
282
288
|
return {
|
|
283
289
|
id: s.id,
|
|
284
290
|
createdAt: s.createdAt,
|
|
291
|
+
baseDir: s.baseDir,
|
|
285
292
|
connected: s.clients.size > 0,
|
|
286
293
|
pid: s.pty?.pid ?? null
|
|
287
294
|
};
|
|
@@ -350,9 +357,9 @@ import { WebSocketServer } from "ws";
|
|
|
350
357
|
// src/pty/index.ts
|
|
351
358
|
var isBun = !!process.versions.bun && process.platform !== "win32";
|
|
352
359
|
console.log(`pty: ${isBun ? "Bun.Terminal" : "node-pty"}`);
|
|
353
|
-
var { spawn: _spawn } = await (isBun ? Promise.resolve().then(() =>
|
|
354
|
-
function spawnForSession(cols, rows, shell, term, colorTerm) {
|
|
355
|
-
return _spawn(shell, cols, rows, term, colorTerm);
|
|
360
|
+
var { spawn: _spawn } = await (isBun ? Promise.resolve().then(() => exports_bun) : Promise.resolve().then(() => (init_node(), exports_node)));
|
|
361
|
+
function spawnForSession(cols, rows, shell, term, colorTerm, cwd, env) {
|
|
362
|
+
return _spawn(shell, cols, rows, term, colorTerm, cwd, env);
|
|
356
363
|
}
|
|
357
364
|
|
|
358
365
|
// src/server/websocket.ts
|
|
@@ -494,7 +501,7 @@ function createWebSocketServer(httpServer) {
|
|
|
494
501
|
setLastUsedId(id);
|
|
495
502
|
if (!session.pty) {
|
|
496
503
|
const config = loadConfig();
|
|
497
|
-
session.pty = spawnForSession(cols, rows, config.shell, config.term, config.colorTerm);
|
|
504
|
+
session.pty = spawnForSession(cols, rows, config.shell, config.term, config.colorTerm, session.baseDir, config.env);
|
|
498
505
|
session.pty.onData((data) => {
|
|
499
506
|
session.scrollback = (session.scrollback + data).slice(-config.scrollback);
|
|
500
507
|
for (const client of session.clients) {
|
|
@@ -556,6 +563,15 @@ function createWebSocketServer(httpServer) {
|
|
|
556
563
|
|
|
557
564
|
// src/server/routes.ts
|
|
558
565
|
var MAX_BODY = 64 * 1024;
|
|
566
|
+
var CORS_HEADERS = {
|
|
567
|
+
"Access-Control-Allow-Origin": "*",
|
|
568
|
+
"Access-Control-Allow-Methods": "GET, POST, PATCH, DELETE, OPTIONS",
|
|
569
|
+
"Access-Control-Allow-Headers": "Content-Type"
|
|
570
|
+
};
|
|
571
|
+
function setCors(res) {
|
|
572
|
+
for (const [k, v] of Object.entries(CORS_HEADERS))
|
|
573
|
+
res.setHeader(k, v);
|
|
574
|
+
}
|
|
559
575
|
function decodeId(raw) {
|
|
560
576
|
try {
|
|
561
577
|
return decodeURIComponent(raw);
|
|
@@ -586,6 +602,12 @@ function readJson(req) {
|
|
|
586
602
|
async function handleRequest(req, res, distPath, wasmPath, clientDistPath, onStop) {
|
|
587
603
|
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "127.0.0.1"}`);
|
|
588
604
|
const pathname = url.pathname;
|
|
605
|
+
setCors(res);
|
|
606
|
+
if (req.method === "OPTIONS") {
|
|
607
|
+
res.writeHead(204);
|
|
608
|
+
res.end();
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
589
611
|
if (req.method === "POST" && pathname === "/api/server/stop") {
|
|
590
612
|
res.writeHead(200, { "Content-Type": "text/plain" });
|
|
591
613
|
res.end("stopping");
|
|
@@ -640,7 +662,23 @@ async function handleRequest(req, res, distPath, wasmPath, clientDistPath, onSto
|
|
|
640
662
|
res.end(JSON.stringify({ error: `session already exists: ${id}` }));
|
|
641
663
|
return;
|
|
642
664
|
}
|
|
643
|
-
const
|
|
665
|
+
const baseDir = body.baseDir ?? homedir2();
|
|
666
|
+
if (!path3.isAbsolute(baseDir)) {
|
|
667
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
668
|
+
res.end(JSON.stringify({ error: "baseDir must be an absolute path" }));
|
|
669
|
+
return;
|
|
670
|
+
}
|
|
671
|
+
if (!fs3.existsSync(baseDir)) {
|
|
672
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
673
|
+
res.end(JSON.stringify({ error: `baseDir does not exist: ${baseDir}` }));
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (!fs3.statSync(baseDir).isDirectory()) {
|
|
677
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
678
|
+
res.end(JSON.stringify({ error: `baseDir is not a directory: ${baseDir}` }));
|
|
679
|
+
return;
|
|
680
|
+
}
|
|
681
|
+
const session = createSession(id, baseDir);
|
|
644
682
|
res.writeHead(201, { "Content-Type": "application/json" });
|
|
645
683
|
res.end(JSON.stringify(sessionToJson(session)));
|
|
646
684
|
return;
|
|
@@ -804,6 +842,106 @@ async function handleRequest(req, res, distPath, wasmPath, clientDistPath, onSto
|
|
|
804
842
|
});
|
|
805
843
|
return;
|
|
806
844
|
}
|
|
845
|
+
const executeMatch = pathname.match(/^\/s\/([^/]+)\/execute$/);
|
|
846
|
+
if (req.method === "POST" && executeMatch) {
|
|
847
|
+
const id = decodeId(executeMatch[1]);
|
|
848
|
+
if (!id) {
|
|
849
|
+
res.writeHead(400);
|
|
850
|
+
res.end("Bad Request");
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
if (!(req.headers["content-type"] ?? "").startsWith("application/json")) {
|
|
854
|
+
res.writeHead(400);
|
|
855
|
+
res.end("Bad Request");
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
const session = sessionRegistry.get(id);
|
|
859
|
+
if (!session) {
|
|
860
|
+
res.writeHead(404);
|
|
861
|
+
res.end("Not Found");
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
if (session.pty === null) {
|
|
865
|
+
res.writeHead(409);
|
|
866
|
+
res.end("PTY not running");
|
|
867
|
+
return;
|
|
868
|
+
}
|
|
869
|
+
let execBody;
|
|
870
|
+
try {
|
|
871
|
+
execBody = await readJson(req);
|
|
872
|
+
} catch (err) {
|
|
873
|
+
const status = err.status === 413 ? 413 : 400;
|
|
874
|
+
res.writeHead(status);
|
|
875
|
+
res.end(status === 413 ? "Payload Too Large" : "invalid JSON");
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
const { cmd, args, stdin } = execBody;
|
|
879
|
+
if (typeof cmd !== "string" || cmd.length === 0) {
|
|
880
|
+
res.writeHead(400);
|
|
881
|
+
res.end("invalid body");
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
if (!Array.isArray(args) || !args.every((a) => typeof a === "string")) {
|
|
885
|
+
res.writeHead(400);
|
|
886
|
+
res.end("invalid body");
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
let child;
|
|
890
|
+
try {
|
|
891
|
+
const execConfig = loadConfig();
|
|
892
|
+
child = spawn3(cmd, args, {
|
|
893
|
+
env: { ...process.env, ...execConfig.env },
|
|
894
|
+
cwd: session.baseDir
|
|
895
|
+
});
|
|
896
|
+
} catch (err) {
|
|
897
|
+
res.writeHead(500);
|
|
898
|
+
res.end(`spawn error: ${String(err)}`);
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
res.writeHead(200, {
|
|
902
|
+
"Content-Type": "application/x-ndjson",
|
|
903
|
+
"Cache-Control": "no-cache"
|
|
904
|
+
});
|
|
905
|
+
let done = false;
|
|
906
|
+
child.on("error", (err) => {
|
|
907
|
+
if (done)
|
|
908
|
+
return;
|
|
909
|
+
done = true;
|
|
910
|
+
const msg = err.message;
|
|
911
|
+
const code = err.code;
|
|
912
|
+
res.write(`${JSON.stringify({ stream: "stderr", data: `${msg}
|
|
913
|
+
` })}
|
|
914
|
+
`);
|
|
915
|
+
res.write(`${JSON.stringify({ exit: 1, error: msg, ...code ? { code } : {} })}
|
|
916
|
+
`);
|
|
917
|
+
res.end();
|
|
918
|
+
});
|
|
919
|
+
if (typeof stdin === "string") {
|
|
920
|
+
child.stdin?.write(stdin);
|
|
921
|
+
}
|
|
922
|
+
child.stdin?.end();
|
|
923
|
+
child.stdout?.on("data", (chunk) => {
|
|
924
|
+
res.write(`${JSON.stringify({ stream: "stdout", data: chunk.toString() })}
|
|
925
|
+
`);
|
|
926
|
+
});
|
|
927
|
+
child.stderr?.on("data", (chunk) => {
|
|
928
|
+
res.write(`${JSON.stringify({ stream: "stderr", data: chunk.toString() })}
|
|
929
|
+
`);
|
|
930
|
+
});
|
|
931
|
+
child.on("close", (code) => {
|
|
932
|
+
if (done)
|
|
933
|
+
return;
|
|
934
|
+
done = true;
|
|
935
|
+
res.write(`${JSON.stringify({ exit: code ?? 1 })}
|
|
936
|
+
`);
|
|
937
|
+
res.end();
|
|
938
|
+
});
|
|
939
|
+
req.on("close", () => {
|
|
940
|
+
if (!done)
|
|
941
|
+
child.kill();
|
|
942
|
+
});
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
807
945
|
const pidMatch = pathname.match(/^\/p\/(\d+)$/);
|
|
808
946
|
if (req.method === "GET" && pidMatch) {
|
|
809
947
|
const pid = parseInt(pidMatch[1], 10);
|
|
@@ -826,8 +964,8 @@ async function handleRequest(req, res, distPath, wasmPath, clientDistPath, onSto
|
|
|
826
964
|
const relativePath = pathname.slice(6);
|
|
827
965
|
const ownFile = path3.resolve(clientDistPath, relativePath);
|
|
828
966
|
if (ownFile.startsWith(clientDistPath + path3.sep)) {
|
|
829
|
-
const
|
|
830
|
-
if (
|
|
967
|
+
const fs4 = await import("node:fs");
|
|
968
|
+
if (fs4.existsSync(ownFile)) {
|
|
831
969
|
serveFile(ownFile, res);
|
|
832
970
|
return;
|
|
833
971
|
}
|
|
@@ -860,7 +998,7 @@ var { distPath, wasmPath } = findGhosttyWeb();
|
|
|
860
998
|
var projectRoot = path4.resolve(__dirname3, "..", "..");
|
|
861
999
|
var builtDistPath = path4.join(projectRoot, "dist");
|
|
862
1000
|
var srcClientPath = path4.join(projectRoot, "src", "client");
|
|
863
|
-
var clientDistPath =
|
|
1001
|
+
var clientDistPath = fs4.existsSync(path4.join(builtDistPath, "client.html")) ? builtDistPath : srcClientPath;
|
|
864
1002
|
function shutdown() {
|
|
865
1003
|
closeAllSessions();
|
|
866
1004
|
wss.close();
|
|
@@ -871,8 +1009,20 @@ function shutdown() {
|
|
|
871
1009
|
exit();
|
|
872
1010
|
});
|
|
873
1011
|
}
|
|
1012
|
+
process.on("uncaughtException", (err) => {
|
|
1013
|
+
console.error("[webtty] uncaughtException:", err);
|
|
1014
|
+
});
|
|
1015
|
+
process.on("unhandledRejection", (reason) => {
|
|
1016
|
+
console.error("[webtty] unhandledRejection:", reason);
|
|
1017
|
+
});
|
|
874
1018
|
var httpServer = http.createServer((req, res) => {
|
|
875
|
-
handleRequest(req, res, distPath, wasmPath, clientDistPath, shutdown)
|
|
1019
|
+
handleRequest(req, res, distPath, wasmPath, clientDistPath, shutdown).catch((err) => {
|
|
1020
|
+
console.error("[webtty] unhandled route error:", err);
|
|
1021
|
+
if (!res.headersSent) {
|
|
1022
|
+
res.writeHead(500);
|
|
1023
|
+
res.end("Internal Server Error");
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
876
1026
|
});
|
|
877
1027
|
var wss = createWebSocketServer(httpServer);
|
|
878
1028
|
setLastSessionClosedHandler(shutdown);
|
|
@@ -886,4 +1036,4 @@ httpServer.listen(HTTP_PORT, HTTP_HOST, () => {
|
|
|
886
1036
|
console.log(`listening on http://${HTTP_HOST_DISPLAY}:${HTTP_PORT}`);
|
|
887
1037
|
});
|
|
888
1038
|
|
|
889
|
-
//# debugId=
|
|
1039
|
+
//# debugId=17F1B5AF7114819864756E2164756E21
|
package/dist/server/index.js.map
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/pty/bun.ts", "../src/pty/node.ts", "../src/server/index.ts", "../src/config.ts", "../src/server/routes.ts", "../src/server/session.ts", "../src/server/static.ts", "../src/server/websocket.ts", "../src/pty/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import
|
|
6
|
-
"import
|
|
7
|
-
"import fs from 'node:fs';\nimport http from 'node:http';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { loadConfig } from '../config';\nimport { handleRequest } from './routes';\nimport { findGhosttyWeb } from './static';\nimport { closeAllSessions, createWebSocketServer, setLastSessionClosedHandler } from './websocket';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nconst config = loadConfig();\nconst HTTP_PORT = Number(process.env.PORT) || config.port;\n// 'localhost' resolves to ::1 (IPv6) on modern macOS/Node; bind to 127.0.0.1 instead\n// but keep 'localhost' as the display host so browser URLs use it as intended.\nconst HTTP_HOST_DISPLAY = config.host;\nconst HTTP_HOST = config.host === 'localhost' ? '127.0.0.1' : config.host;\n\nconst { distPath, wasmPath } = findGhosttyWeb();\nconst projectRoot = path.resolve(__dirname, '..', '..');\nconst builtDistPath = path.join(projectRoot, 'dist');\nconst srcClientPath = path.join(projectRoot, 'src', 'client');\nconst clientDistPath = fs.existsSync(path.join(builtDistPath, 'client.html'))\n ? builtDistPath\n : srcClientPath;\n\nfunction shutdown() {\n closeAllSessions();\n wss.close();\n const exit = () => process.exit(0);\n const shutdownTimeout = setTimeout(exit, 1000);\n httpServer.close(() => {\n clearTimeout(shutdownTimeout);\n exit();\n });\n}\n\nconst httpServer = http.createServer((req, res) => {\n handleRequest(req, res, distPath, wasmPath, clientDistPath, shutdown);\n});\n\nconst wss = createWebSocketServer(httpServer);\nsetLastSessionClosedHandler(shutdown);\n\nprocess.on('SIGINT', () => {\n console.log('\\n\\nShutting down...');\n shutdown();\n});\n\nhttpServer.listen(HTTP_PORT, HTTP_HOST, () => {\n console.log(`listening on http://${HTTP_HOST_DISPLAY}:${HTTP_PORT}`);\n});\n",
|
|
8
|
-
"import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\n/** A single keyboard binding: intercepts `key`+`mods` and sends `chars` to the PTY. */\nexport interface KeyboardBinding {\n /** Key name matched case-insensitively against `KeyboardEvent.key` (e.g. `\"enter\"`, `\"arrowup\"`, `\"a\"`). */\n key: string;\n /** Modifier keys that must be active — and no others — for this binding to match. Accepted values: `\"shift\"`, `\"ctrl\"`, `\"alt\"`, `\"meta\"`. Order irrelevant. Unknown values are filtered out at config load time. */\n mods?: string[];\n /** Byte sequence sent verbatim to the PTY. Standard JSON escapes apply (`\\r`, `\\uXXXX`, etc.). */\n chars: string;\n}\n\n/** Terminal color palette. All keys are optional; omitted keys fall back to the Campbell defaults. */\nexport interface Theme {\n /** Terminal background. */\n background?: string;\n /** Default text color. */\n foreground?: string;\n /** Cursor color. */\n cursor?: string;\n /** Selection highlight. */\n selection?: string;\n /** ANSI 0. */\n black?: string;\n /** ANSI 1. */\n red?: string;\n /** ANSI 2. */\n green?: string;\n /** ANSI 3. */\n yellow?: string;\n /** ANSI 4. */\n blue?: string;\n /** ANSI 5. */\n purple?: string;\n /** ANSI 6. */\n cyan?: string;\n /** ANSI 7. */\n white?: string;\n /** ANSI 8. */\n brightBlack?: string;\n /** ANSI 9. */\n brightRed?: string;\n /** ANSI 10. */\n brightGreen?: string;\n /** ANSI 11. */\n brightYellow?: string;\n /** ANSI 12. */\n brightBlue?: string;\n /** ANSI 13. */\n brightPurple?: string;\n /** ANSI 14. */\n brightCyan?: string;\n /** ANSI 15. */\n brightWhite?: string;\n}\n\n/** Right-click behavior: `\"copyPaste\"` copies selection + clears it if selection exists, otherwise native menu; `\"default\"` always shows native context menu. */\nexport type RightClickBehavior = 'default' | 'copyPaste';\n\n/** Full webtty configuration. All keys are optional in the config file; missing keys fall back to {@link DEFAULT_CONFIG}. */\nexport interface Config {\n /** HTTP listen port; env `PORT` takes precedence. */\n port: number;\n /** Bind address; use `\"0.0.0.0\"` for remote access. */\n host: string;\n /** Shell for new sessions. */\n shell: string;\n /** `$TERM` env var passed to the shell. Fixed to `xterm-256color` — the PTY child talks to webtty, not to the parent terminal. */\n term: string;\n /** `$COLORTERM` env var passed to the shell. */\n colorTerm: string;\n /** PTY history buffer in bytes; used for server-side replay on reload/reconnect. */\n scrollback: number;\n /** Initial terminal width in columns. */\n cols: number;\n /** Initial terminal height in rows. */\n rows: number;\n /** Font size in px. */\n fontSize: number;\n /** CSS font-family stack. */\n fontFamily: string;\n /** Default cursor shape. Apps override at runtime via DECSCUSR — this is the startup default only. */\n cursorStyle: 'block' | 'bar' | 'underline';\n /** Default blink state. Apps override at runtime via DECSCUSR — this is the startup default only. */\n cursorStyleBlink: boolean;\n /** Auto-copy selection to clipboard on mouseup (kitty / Windows Terminal style). */\n copyOnSelect: boolean;\n /** `\"copyPaste\"` copies selection + clears it on right-click if selection exists; `\"default\"` always shows native context menu. */\n rightClickBehavior: RightClickBehavior;\n /** Mouse wheel scroll speed multiplier for apps with mouse tracking. `1` = one SGR per tick. Values `< 1` reduce rate; values `> 1` send multiple SGRs per tick. Must be `> 0`. */\n mouseScrollSpeed: number;\n /** Write server stdout/stderr to `~/.config/webtty/server.log`. Appends on each start. */\n logs: boolean;\n /** Terminal color palette. */\n theme: Theme;\n /** Custom key-to-sequence bindings. Merged with built-in defaults by `(key, mods)` identity. */\n keyboardBindings: KeyboardBinding[];\n}\n\n/** Returns the webtty config directory: `~/.config/webtty`. */\nexport function configDir(): string {\n // os.homedir() is the authoritative home directory. process.env.HOME is used\n // as an override in tests (e.g. to isolate config state), but only when it is\n // an absolute path — guarding against accidental env pollution such as the\n // string \"undefined\" being assigned when HOME was originally unset on Windows.\n const home =\n process.env.HOME && path.isAbsolute(process.env.HOME) ? process.env.HOME : os.homedir();\n return path.join(home, '.config', 'webtty');\n}\n\nfunction getConfigPath(): string {\n return path.join(configDir(), 'config.json');\n}\n\n// NOTE: export for testing only; users should use loadConfig() and initConfig() instead\nexport const DEFAULT_THEME: Theme = {\n background: '#000000',\n foreground: '#CCCCCC',\n cursor: '#FFFFFF',\n selection: '#FFFFFF',\n black: '#0C0C0C',\n red: '#C50F1F',\n green: '#13A10E',\n yellow: '#C19C00',\n blue: '#0037DA',\n purple: '#881798',\n cyan: '#3A96DD',\n white: '#CCCCCC',\n brightBlack: '#767676',\n brightRed: '#E74856',\n brightGreen: '#16C60C',\n brightYellow: '#F9F1A5',\n brightBlue: '#3B78FF',\n brightPurple: '#B4009E',\n brightCyan: '#61D6D6',\n brightWhite: '#F2F2F2',\n};\n\n// NOTE: export for testing only; users should use loadConfig() and initConfig() instead\nexport const DEFAULT_KEYBOARD_BINDINGS: KeyboardBinding[] = [];\n\n// NOTE: export for testing only; users should use loadConfig() and initConfig() instead\nexport const DEFAULT_CONFIG: Config = {\n port: 2346,\n host: '127.0.0.1',\n shell:\n process.platform === 'win32'\n ? (process.env.COMSPEC ?? 'cmd.exe')\n : (process.env.SHELL ?? '/bin/bash'),\n term: 'xterm-256color',\n colorTerm: 'truecolor',\n scrollback: 256 * 1024,\n cols: 80,\n rows: 24,\n fontSize: 13,\n fontFamily: \"Menlo, Consolas, 'DejaVu Sans Mono', monospace\",\n cursorStyle: 'bar',\n cursorStyleBlink: true,\n copyOnSelect: true,\n rightClickBehavior: 'default' as RightClickBehavior,\n mouseScrollSpeed: 1,\n logs: false,\n theme: DEFAULT_THEME,\n keyboardBindings: DEFAULT_KEYBOARD_BINDINGS,\n};\n\nfunction bindingKey(b: KeyboardBinding): string {\n const mods = [...(b.mods ?? [])].sort().join('+');\n return `${b.key.toLowerCase()}|${mods}`;\n}\n\nconst VALID_MODS = new Set(['shift', 'ctrl', 'alt', 'meta']);\n\nfunction isValidBinding(b: unknown): b is KeyboardBinding {\n if (!b || typeof b !== 'object') return false;\n const o = b as Record<string, unknown>;\n if (typeof o.key !== 'string' || typeof o.chars !== 'string') return false;\n if (o.mods !== undefined && !Array.isArray(o.mods)) return false;\n return true;\n}\n\nfunction normalizeBinding(b: KeyboardBinding): KeyboardBinding {\n return {\n ...b,\n key: b.key.toLowerCase(),\n mods: (b.mods ?? []).map((m) => m.toLowerCase()).filter((m) => VALID_MODS.has(m)),\n };\n}\n\n/**\n * Merges user-supplied bindings over a set of defaults by `(key, mods)` identity.\n * User entries replace matching defaults; unmatched user entries are appended.\n *\n * @param defaults - Default bindings to merge over.\n * @param user - User-supplied bindings that override defaults.\n * @returns Merged bindings with user entries replacing matching defaults and new user entries appended.\n */\n// NOTE: export for testing only\nexport function mergeKeyboardBindings(\n defaults: KeyboardBinding[],\n user: KeyboardBinding[],\n): KeyboardBinding[] {\n const deduped = [...new Map(user.map((b) => [bindingKey(b), b])).values()];\n const overrides = new Map(deduped.map((b) => [bindingKey(b), b]));\n const merged = defaults.map((d) => overrides.get(bindingKey(d)) ?? d);\n const defaultKeys = new Set(defaults.map(bindingKey));\n for (const b of deduped) {\n if (!defaultKeys.has(bindingKey(b))) merged.push(b);\n }\n return merged;\n}\n\n/**\n * Load config from `~/.config/webtty/config.json`, merged over `DEFAULT_CONFIG`.\n *\n * On first run (file absent), writes the default port/host stub and returns defaults.\n * Unknown keys and keys with wrong types are silently ignored.\n * Throws if the file exists but cannot be read or contains invalid JSON.\n *\n * @returns The resolved {@link Config}, with all missing keys filled from {@link DEFAULT_CONFIG}.\n */\nexport function loadConfig(): Config {\n if (!fs.existsSync(getConfigPath())) {\n try {\n initConfig();\n } catch (err) {\n console.warn(\n `webtty: failed to write default config to ${getConfigPath()}: ${(err as Error).message}`,\n );\n return { ...DEFAULT_CONFIG };\n }\n }\n\n let raw: string;\n try {\n raw = fs.readFileSync(getConfigPath(), 'utf8');\n } catch (err) {\n throw new Error(\n `webtty: failed to read config at ${getConfigPath()}: ${(err as Error).message}`,\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(`webtty: invalid JSON in config file ${getConfigPath()}`);\n }\n\n const p = parsed as Partial<Config>;\n return {\n ...DEFAULT_CONFIG,\n ...(typeof p.port === 'number' && { port: p.port }),\n ...(typeof p.host === 'string' && { host: p.host }),\n ...(typeof p.shell === 'string' && { shell: p.shell }),\n ...(typeof p.term === 'string' && { term: p.term }),\n ...(typeof p.colorTerm === 'string' && { colorTerm: p.colorTerm }),\n ...(typeof p.scrollback === 'number' && { scrollback: p.scrollback }),\n ...(typeof p.cols === 'number' && { cols: p.cols }),\n ...(typeof p.rows === 'number' && { rows: p.rows }),\n ...(typeof p.fontSize === 'number' && { fontSize: p.fontSize }),\n ...(typeof p.fontFamily === 'string' && { fontFamily: p.fontFamily }),\n ...(typeof p.cursorStyle === 'string' &&\n (p.cursorStyle === 'block' || p.cursorStyle === 'bar' || p.cursorStyle === 'underline') && {\n cursorStyle: p.cursorStyle,\n }),\n ...(typeof p.cursorStyleBlink === 'boolean' && { cursorStyleBlink: p.cursorStyleBlink }),\n ...(typeof p.copyOnSelect === 'boolean' && { copyOnSelect: p.copyOnSelect }),\n ...(typeof p.rightClickBehavior === 'string' && {\n rightClickBehavior: (p.rightClickBehavior === 'copyPaste'\n ? 'copyPaste'\n : 'default') as RightClickBehavior,\n }),\n ...(typeof p.mouseScrollSpeed === 'number' &&\n p.mouseScrollSpeed > 0 && { mouseScrollSpeed: p.mouseScrollSpeed }),\n ...(typeof p.logs === 'boolean' && { logs: p.logs }),\n ...(p.theme && typeof p.theme === 'object' && { theme: { ...DEFAULT_THEME, ...p.theme } }),\n ...(Array.isArray(p.keyboardBindings) && {\n keyboardBindings: mergeKeyboardBindings(\n DEFAULT_KEYBOARD_BINDINGS,\n p.keyboardBindings.filter(isValidBinding).map(normalizeBinding),\n ),\n }),\n };\n}\n\n/**\n * Write the initial config stub (`port` + `host` only) to `~/.config/webtty/config.json`.\n *\n * Only writes the two keys that are safe to persist as defaults; all other\n * keys are intentionally omitted so future webtty versions can add new fields\n * without the stub going stale.\n */\nexport function initConfig(): void {\n fs.mkdirSync(path.dirname(getConfigPath()), { recursive: true });\n const content = JSON.stringify(\n {\n port: DEFAULT_CONFIG.port,\n host: DEFAULT_CONFIG.host,\n },\n null,\n 2,\n );\n fs.writeFileSync(getConfigPath(), content, 'utf8');\n}\n",
|
|
9
|
-
"import type http from 'node:http';\nimport path from 'node:path';\nimport { loadConfig } from '../config';\nimport {\n createSession,\n generateId,\n isValidId,\n lastUsedId,\n sessionRegistry,\n sessionToJson,\n setLastUsedId,\n} from './session';\nimport { serveFile } from './static';\nimport { broadcastToSubscribers, closeSession } from './websocket';\n\nconst MAX_BODY = 64 * 1024;\n\nfunction decodeId(raw: string): string | null {\n try {\n return decodeURIComponent(raw);\n } catch {\n return null;\n }\n}\n\n/**\n * Reads and JSON-parses the request body (max 64 KB).\n *\n * @param req - The incoming HTTP request.\n * @returns A promise resolving to the parsed JSON object, or an empty object if body is empty.\n * @throws {Error} with `status: 413` if body exceeds 64 KB.\n * @throws {Error} if the body contains invalid JSON.\n */\nexport function readJson(req: http.IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let body = '';\n req.on('data', (chunk) => {\n body += chunk;\n if (body.length > MAX_BODY) {\n req.destroy();\n reject(Object.assign(new Error('payload too large'), { status: 413 }));\n }\n });\n req.on('end', () => {\n try {\n resolve(body ? JSON.parse(body) : {});\n } catch {\n reject(new Error('invalid JSON'));\n }\n });\n req.on('error', reject);\n });\n}\n\n/**\n * Main HTTP request handler for the webtty server.\n * Dispatches all REST API routes and serves static client assets.\n *\n * @param req - The incoming HTTP request.\n * @param res - The HTTP response object.\n * @param distPath - Path to the server-side dist directory.\n * @param wasmPath - Path to the ghostty-vt.wasm file.\n * @param clientDistPath - Path to the client dist directory.\n * @param onStop - Callback invoked when `POST /api/server/stop` is received.\n */\nexport async function handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n distPath: string,\n wasmPath: string,\n clientDistPath: string,\n onStop: () => void,\n): Promise<void> {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? '127.0.0.1'}`);\n const pathname = url.pathname;\n\n if (req.method === 'POST' && pathname === '/api/server/stop') {\n res.writeHead(200, { 'Content-Type': 'text/plain' });\n res.end('stopping');\n onStop();\n return;\n }\n\n if (req.method === 'GET' && pathname === '/api/config') {\n const config = loadConfig();\n // Whitelist client-safe keys — avoid exposing server-side config (shell, host, logs, etc.)\n const clientConfig = {\n cols: config.cols,\n rows: config.rows,\n fontSize: config.fontSize,\n fontFamily: config.fontFamily,\n cursorStyle: config.cursorStyle,\n cursorStyleBlink: config.cursorStyleBlink,\n scrollback: config.scrollback,\n theme: config.theme,\n copyOnSelect: config.copyOnSelect,\n rightClickBehavior: config.rightClickBehavior,\n mouseScrollSpeed: config.mouseScrollSpeed,\n keyboardBindings: config.keyboardBindings,\n };\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(clientConfig));\n return;\n }\n\n if (pathname === '/api/sessions') {\n if (req.method === 'GET') {\n const list = [...sessionRegistry.values()].map(sessionToJson);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(list));\n return;\n }\n\n if (req.method === 'POST') {\n let body: { id?: string };\n try {\n body = (await readJson(req)) as { id?: string };\n } catch (err) {\n const status = (err as { status?: number }).status === 413 ? 413 : 400;\n res.writeHead(status);\n res.end(status === 413 ? 'Payload Too Large' : 'invalid JSON');\n return;\n }\n\n const id = body.id ?? generateId();\n if (!isValidId(id)) {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: `invalid id: ${id}` }));\n return;\n }\n if (sessionRegistry.has(id)) {\n res.writeHead(409, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: `session already exists: ${id}` }));\n return;\n }\n const session = createSession(id);\n res.writeHead(201, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(sessionToJson(session)));\n return;\n }\n }\n\n const sessionMatch = pathname.match(/^\\/api\\/sessions\\/([^/]+)$/);\n if (sessionMatch) {\n const id = decodeId(sessionMatch[1]);\n if (!id) {\n res.writeHead(400);\n res.end('Bad Request');\n return;\n }\n\n if (req.method === 'GET') {\n const session = sessionRegistry.get(id);\n if (!session) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(sessionToJson(session)));\n return;\n }\n\n if (req.method === 'PATCH') {\n let body: { id?: string };\n try {\n body = (await readJson(req)) as { id?: string };\n } catch (err) {\n const status = (err as { status?: number }).status === 413 ? 413 : 400;\n res.writeHead(status);\n res.end(status === 413 ? 'Payload Too Large' : 'invalid JSON');\n return;\n }\n\n const session = sessionRegistry.get(id);\n if (!session) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n\n const newId = body.id;\n if (!newId) {\n res.writeHead(400);\n res.end('missing id');\n return;\n }\n if (!isValidId(newId)) {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: `invalid id: ${newId}` }));\n return;\n }\n if (sessionRegistry.has(newId)) {\n res.writeHead(409, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: `session already exists: ${newId}` }));\n return;\n }\n sessionRegistry.delete(id);\n session.id = newId;\n sessionRegistry.set(newId, session);\n if (lastUsedId === id) setLastUsedId(newId);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(sessionToJson(session)));\n return;\n }\n\n if (req.method === 'DELETE') {\n const session = sessionRegistry.get(id);\n if (!session) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n sessionRegistry.delete(id);\n if (lastUsedId === id) setLastUsedId(null);\n closeSession(session);\n res.writeHead(204, { 'X-Sessions-Remaining': String(sessionRegistry.size) });\n res.end();\n return;\n }\n }\n\n if (req.method === 'GET' && (pathname === '/' || pathname === '/index.html')) {\n let targetId = lastUsedId ?? null;\n if (!targetId || !sessionRegistry.has(targetId)) {\n if (!sessionRegistry.has('main')) createSession('main');\n targetId = 'main';\n }\n res.writeHead(302, { Location: `/s/${targetId}` });\n res.end();\n return;\n }\n\n const clientMatch = pathname.match(/^\\/s\\/([^/]+)$/);\n if (req.method === 'GET' && clientMatch) {\n const id = decodeId(clientMatch[1]);\n if (!id || !isValidId(id) || !sessionRegistry.has(id)) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n const clientHtml = path.resolve(clientDistPath, 'client.html');\n serveFile(clientHtml, res);\n return;\n }\n\n const publishMatch = pathname.match(/^\\/s\\/([^/]+)\\/publish$/);\n if (req.method === 'POST' && publishMatch) {\n const id = decodeId(publishMatch[1]);\n if (!id) {\n res.writeHead(400);\n res.end('Bad Request');\n return;\n }\n if (!(req.headers['content-type'] ?? '').startsWith('application/json')) {\n res.writeHead(400);\n res.end('Bad Request');\n return;\n }\n const session = sessionRegistry.get(id);\n if (!session) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n if (session.pty === null) {\n res.writeHead(409);\n res.end('PTY not running');\n return;\n }\n let buf = '';\n req.on('data', (chunk: Buffer) => {\n buf += chunk.toString('utf8');\n const lines = buf.split('\\n');\n buf = lines.pop() ?? '';\n if (buf.length > MAX_BODY) buf = '';\n for (const line of lines) {\n const trimmed = line.replace(/\\r$/, '');\n if (!trimmed) continue;\n try {\n JSON.parse(trimmed);\n broadcastToSubscribers(session, trimmed);\n } catch {\n // skip invalid JSON lines\n }\n }\n });\n req.on('end', () => {\n const trimmed = buf.replace(/\\r$/, '');\n if (trimmed) {\n try {\n JSON.parse(trimmed);\n broadcastToSubscribers(session, trimmed);\n } catch {\n // skip invalid JSON\n }\n }\n res.writeHead(204);\n res.end();\n });\n req.on('error', () => {\n res.writeHead(500);\n res.end();\n });\n return;\n }\n\n const pidMatch = pathname.match(/^\\/p\\/(\\d+)$/);\n if (req.method === 'GET' && pidMatch) {\n const pid = parseInt(pidMatch[1], 10);\n if (!Number.isFinite(pid) || pid <= 0) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n const session = [...sessionRegistry.values()].find((s) => s.pty?.pid === pid);\n if (!session) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n res.writeHead(302, { Location: `/s/${session.id}` });\n res.end();\n return;\n }\n\n if (pathname.startsWith('/dist/')) {\n const relativePath = pathname.slice(6);\n const ownFile = path.resolve(clientDistPath, relativePath);\n if (ownFile.startsWith(clientDistPath + path.sep)) {\n const fs = await import('node:fs');\n if (fs.existsSync(ownFile)) {\n serveFile(ownFile, res);\n return;\n }\n }\n const filePath = path.resolve(distPath, relativePath);\n if (\n !filePath.startsWith(path.resolve(distPath) + path.sep) &&\n filePath !== path.resolve(distPath)\n ) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n serveFile(filePath, res);\n return;\n }\n\n if (pathname === '/ghostty-vt.wasm') {\n serveFile(wasmPath, res);\n return;\n }\n\n res.writeHead(404);\n res.end('Not Found');\n}\n",
|
|
10
|
-
"import type { WebSocket as WS } from 'ws';\nimport type { PtyProcess } from '../pty';\n\n/** A running (or recently created) terminal session. */\nexport interface Session {\n /** Unique session identifier. */\n id: string;\n /** Unix timestamp (ms) when the session was created. */\n createdAt: number;\n /** The underlying PTY process, or `null` if no shell has been spawned yet. */\n pty: PtyProcess | null;\n /** All currently connected WebSocket clients for this session. */\n clients: Set<WS>;\n /** All currently connected event subscribers for this session. */\n subscribers: Set<WS>;\n /** Accumulated PTY output retained for replay when a new client joins. */\n scrollback: string;\n}\n\n/** All active sessions, keyed by session ID. */\nexport const sessionRegistry = new Map<string, Session>();\n\n/** ID of the most recently opened session, used for `GET /` redirect. */\nexport let lastUsedId: string | null = null;\n\n/** Updates {@link lastUsedId}. */\nexport function setLastUsedId(id: string | null): void {\n lastUsedId = id;\n}\n\nconst ID_RE = /^[a-z0-9\\-_.]{1,64}$/;\n\n/**\n * Returns `true` if `id` is a valid session identifier (lowercase alphanumeric + `-_.`, max 64 chars).\n *\n * @param id - The session ID to validate.\n * @returns `true` if the ID matches the valid format, `false` otherwise.\n */\nexport function isValidId(id: string): boolean {\n return ID_RE.test(id);\n}\n\n/**\n * Generates a random 8-character hex session ID.\n *\n * @returns A random session ID.\n */\nexport function generateId(): string {\n return Math.floor(Math.random() * 0xffffffff)\n .toString(16)\n .padStart(8, '0');\n}\n\n/**\n * Creates a new session, registers it in {@link sessionRegistry}, and returns it.\n *\n * @param id - The session ID.\n * @returns The newly created {@link Session}.\n */\nexport function createSession(id: string): Session {\n const session: Session = {\n id,\n createdAt: Date.now(),\n pty: null,\n clients: new Set(),\n subscribers: new Set(),\n scrollback: '',\n };\n sessionRegistry.set(id, session);\n return session;\n}\n\n/**\n * Returns a plain JSON-safe representation of a session for API responses.\n *\n * @param s - The session to serialize.\n * @returns A JSON-safe object with session ID, creation timestamp, connection status, and PTY PID (or `null` if no PTY has been spawned yet).\n */\nexport function sessionToJson(s: Session) {\n return {\n id: s.id,\n createdAt: s.createdAt,\n connected: s.clients.size > 0,\n pid: s.pty?.pid ?? null,\n };\n}\n",
|
|
5
|
+
"import type { PtyProcess } from './types';\n\n/**\n * Spawns a PTY-backed shell using Bun's native `Bun.spawn` terminal API.\n *\n * @param shell - Shell executable path (e.g., `/bin/bash`).\n * @param cols - Terminal width in columns.\n * @param rows - Terminal height in rows.\n * @param term - `$TERM` environment variable (e.g., `xterm-256color`).\n * @param colorTerm - `$COLORTERM` environment variable (e.g., `truecolor`).\n * @param cwd - Working directory for the shell.\n * @returns A {@link PtyProcess} handle for reading/writing and managing the PTY.\n */\nexport function spawn(\n shell: string,\n cols: number,\n rows: number,\n term: string,\n colorTerm: string,\n cwd: string,\n env: Record<string, string>,\n): PtyProcess {\n let onDataCb: ((data: string) => void) | undefined;\n let onExitCb: ((e: { exitCode: number }) => void) | undefined;\n\n const proc = Bun.spawn([shell], {\n terminal: {\n cols,\n rows,\n data(_term: unknown, data: Uint8Array) {\n onDataCb?.(Buffer.from(data).toString('utf8'));\n },\n },\n cwd,\n env: { ...process.env, ...env, TERM: term, COLORTERM: colorTerm },\n });\n\n proc.exited.then((exitCode) => {\n onExitCb?.({ exitCode: exitCode ?? 0 });\n });\n\n return {\n pid: proc.pid,\n onData(cb) {\n onDataCb = cb;\n },\n onExit(cb) {\n onExitCb = cb;\n },\n write(data) {\n proc.terminal?.write(data);\n },\n resize(cols, rows) {\n proc.terminal?.resize(cols, rows);\n },\n kill() {\n proc.kill();\n },\n };\n}\n",
|
|
6
|
+
"import nodePty from '@lydell/node-pty';\nimport type { PtyProcess } from './types';\n\n/**\n * Spawns a PTY-backed shell using the `@lydell/node-pty` native addon.\n *\n * @param shell - Shell executable path (e.g., `/bin/bash`).\n * @param cols - Terminal width in columns.\n * @param rows - Terminal height in rows.\n * @param term - `$TERM` environment variable (e.g., `xterm-256color`).\n * @param colorTerm - `$COLORTERM` environment variable (e.g., `truecolor`).\n * @param cwd - Working directory for the shell.\n * @returns A {@link PtyProcess} handle for reading/writing and managing the PTY.\n */\nexport function spawn(\n shell: string,\n cols: number,\n rows: number,\n term: string,\n colorTerm: string,\n cwd: string,\n env: Record<string, string>,\n): PtyProcess {\n // On Windows, cmd.exe may have an AutoRun registry key that launches shell\n // enhancers (e.g. clink). These take over the ConPTY pipe and cause\n // ERR_SOCKET_CLOSED on the first write. Pass /d to disable AutoRun.\n const shellArgs = process.platform === 'win32' && /cmd\\.exe$/i.test(shell) ? ['/d'] : [];\n\n const ptyProc = nodePty.spawn(shell, shellArgs, {\n name: term,\n cols,\n rows,\n cwd,\n env: { ...process.env, ...env, TERM: term, COLORTERM: colorTerm },\n });\n\n return {\n pid: ptyProc.pid,\n onData(cb) {\n ptyProc.onData(cb);\n },\n onExit(cb) {\n ptyProc.onExit(cb);\n },\n write(data) {\n ptyProc.write(data);\n },\n resize(cols, rows) {\n ptyProc.resize(cols, rows);\n },\n kill() {\n ptyProc.kill();\n },\n };\n}\n",
|
|
7
|
+
"import fs from 'node:fs';\nimport http from 'node:http';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { loadConfig } from '../config';\nimport { handleRequest } from './routes';\nimport { findGhosttyWeb } from './static';\nimport { closeAllSessions, createWebSocketServer, setLastSessionClosedHandler } from './websocket';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nconst config = loadConfig();\nconst HTTP_PORT = Number(process.env.PORT) || config.port;\n// 'localhost' resolves to ::1 (IPv6) on modern macOS/Node; bind to 127.0.0.1 instead\n// but keep 'localhost' as the display host so browser URLs use it as intended.\nconst HTTP_HOST_DISPLAY = config.host;\nconst HTTP_HOST = config.host === 'localhost' ? '127.0.0.1' : config.host;\n\nconst { distPath, wasmPath } = findGhosttyWeb();\nconst projectRoot = path.resolve(__dirname, '..', '..');\nconst builtDistPath = path.join(projectRoot, 'dist');\nconst srcClientPath = path.join(projectRoot, 'src', 'client');\nconst clientDistPath = fs.existsSync(path.join(builtDistPath, 'client.html'))\n ? builtDistPath\n : srcClientPath;\n\nfunction shutdown() {\n closeAllSessions();\n wss.close();\n const exit = () => process.exit(0);\n const shutdownTimeout = setTimeout(exit, 1000);\n httpServer.close(() => {\n clearTimeout(shutdownTimeout);\n exit();\n });\n}\n\nprocess.on('uncaughtException', (err) => {\n console.error('[webtty] uncaughtException:', err);\n});\nprocess.on('unhandledRejection', (reason) => {\n console.error('[webtty] unhandledRejection:', reason);\n});\n\nconst httpServer = http.createServer((req, res) => {\n handleRequest(req, res, distPath, wasmPath, clientDistPath, shutdown).catch((err) => {\n console.error('[webtty] unhandled route error:', err);\n if (!res.headersSent) {\n res.writeHead(500);\n res.end('Internal Server Error');\n }\n });\n});\n\nconst wss = createWebSocketServer(httpServer);\nsetLastSessionClosedHandler(shutdown);\n\nprocess.on('SIGINT', () => {\n console.log('\\n\\nShutting down...');\n shutdown();\n});\n\nhttpServer.listen(HTTP_PORT, HTTP_HOST, () => {\n console.log(`listening on http://${HTTP_HOST_DISPLAY}:${HTTP_PORT}`);\n});\n",
|
|
8
|
+
"import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\n/** A single keyboard binding: intercepts `key`+`mods` and sends `chars` to the PTY. */\nexport interface KeyboardBinding {\n /** Key name matched case-insensitively against `KeyboardEvent.key` (e.g. `\"enter\"`, `\"arrowup\"`, `\"a\"`). */\n key: string;\n /** Modifier keys that must be active — and no others — for this binding to match. Accepted values: `\"shift\"`, `\"ctrl\"`, `\"alt\"`, `\"meta\"`. Order irrelevant. Unknown values are filtered out at config load time. */\n mods?: string[];\n /** Byte sequence sent verbatim to the PTY. Standard JSON escapes apply (`\\r`, `\\uXXXX`, etc.). */\n chars: string;\n}\n\n/** Terminal color palette. All keys are optional; omitted keys fall back to the Campbell defaults. */\nexport interface Theme {\n /** Terminal background. */\n background?: string;\n /** Default text color. */\n foreground?: string;\n /** Cursor color. */\n cursor?: string;\n /** Selection highlight. */\n selection?: string;\n /** ANSI 0. */\n black?: string;\n /** ANSI 1. */\n red?: string;\n /** ANSI 2. */\n green?: string;\n /** ANSI 3. */\n yellow?: string;\n /** ANSI 4. */\n blue?: string;\n /** ANSI 5. */\n purple?: string;\n /** ANSI 6. */\n cyan?: string;\n /** ANSI 7. */\n white?: string;\n /** ANSI 8. */\n brightBlack?: string;\n /** ANSI 9. */\n brightRed?: string;\n /** ANSI 10. */\n brightGreen?: string;\n /** ANSI 11. */\n brightYellow?: string;\n /** ANSI 12. */\n brightBlue?: string;\n /** ANSI 13. */\n brightPurple?: string;\n /** ANSI 14. */\n brightCyan?: string;\n /** ANSI 15. */\n brightWhite?: string;\n}\n\n/** Right-click behavior: `\"copyPaste\"` copies selection + clears it if selection exists, otherwise native menu; `\"default\"` always shows native context menu. */\nexport type RightClickBehavior = 'default' | 'copyPaste';\n\n/** Full webtty configuration. All keys are optional in the config file; missing keys fall back to {@link DEFAULT_CONFIG}. */\nexport interface Config {\n /** HTTP listen port; env `PORT` takes precedence. */\n port: number;\n /** Bind address; use `\"0.0.0.0\"` for remote access. */\n host: string;\n /** Shell for new sessions. */\n shell: string;\n /** `$TERM` env var passed to the shell. Fixed to `xterm-256color` — the PTY child talks to webtty, not to the parent terminal. */\n term: string;\n /** `$COLORTERM` env var passed to the shell. */\n colorTerm: string;\n /** PTY history buffer in bytes; used for server-side replay on reload/reconnect. */\n scrollback: number;\n /** Initial terminal width in columns. */\n cols: number;\n /** Initial terminal height in rows. */\n rows: number;\n /** Font size in px. */\n fontSize: number;\n /** CSS font-family stack. */\n fontFamily: string;\n /** Default cursor shape. Apps override at runtime via DECSCUSR — this is the startup default only. */\n cursorStyle: 'block' | 'bar' | 'underline';\n /** Default blink state. Apps override at runtime via DECSCUSR — this is the startup default only. */\n cursorStyleBlink: boolean;\n /** Auto-copy selection to clipboard on mouseup (kitty / Windows Terminal style). */\n copyOnSelect: boolean;\n /** `\"copyPaste\"` copies selection + clears it on right-click if selection exists; `\"default\"` always shows native context menu. */\n rightClickBehavior: RightClickBehavior;\n /** Mouse wheel scroll speed multiplier for apps with mouse tracking. `1` = one SGR per tick. Values `< 1` reduce rate; values `> 1` send multiple SGRs per tick. Must be `> 0`. */\n mouseScrollSpeed: number;\n /** Write server stdout/stderr to `~/.config/webtty/server.log`. Appends on each start. */\n logs: boolean;\n /** Terminal color palette. */\n theme: Theme;\n /** Custom key-to-sequence bindings. Merged with built-in defaults by `(key, mods)` identity. */\n keyboardBindings: KeyboardBinding[];\n /** Additional environment variables injected into PTY shells and execute commands. Merges over `process.env`; `TERM` and `COLORTERM` always take precedence for PTY spawns. */\n env: Record<string, string>;\n}\n\n/** Returns the webtty config directory: `~/.config/webtty`. */\nexport function configDir(): string {\n // os.homedir() is the authoritative home directory. process.env.HOME is used\n // as an override in tests (e.g. to isolate config state), but only when it is\n // an absolute path — guarding against accidental env pollution such as the\n // string \"undefined\" being assigned when HOME was originally unset on Windows.\n const home =\n process.env.HOME && path.isAbsolute(process.env.HOME) ? process.env.HOME : os.homedir();\n return path.join(home, '.config', 'webtty');\n}\n\nfunction getConfigPath(): string {\n return path.join(configDir(), 'config.json');\n}\n\n// NOTE: export for testing only; users should use loadConfig() and initConfig() instead\nexport const DEFAULT_THEME: Theme = {\n background: '#000000',\n foreground: '#CCCCCC',\n cursor: '#FFFFFF',\n selection: '#FFFFFF',\n black: '#0C0C0C',\n red: '#C50F1F',\n green: '#13A10E',\n yellow: '#C19C00',\n blue: '#0037DA',\n purple: '#881798',\n cyan: '#3A96DD',\n white: '#CCCCCC',\n brightBlack: '#767676',\n brightRed: '#E74856',\n brightGreen: '#16C60C',\n brightYellow: '#F9F1A5',\n brightBlue: '#3B78FF',\n brightPurple: '#B4009E',\n brightCyan: '#61D6D6',\n brightWhite: '#F2F2F2',\n};\n\n// NOTE: export for testing only; users should use loadConfig() and initConfig() instead\nexport const DEFAULT_KEYBOARD_BINDINGS: KeyboardBinding[] = [];\n\n// NOTE: export for testing only; users should use loadConfig() and initConfig() instead\nexport const DEFAULT_CONFIG: Config = {\n port: 2346,\n host: '127.0.0.1',\n shell:\n process.platform === 'win32'\n ? (process.env.COMSPEC ?? 'cmd.exe')\n : (process.env.SHELL ?? '/bin/bash'),\n term: 'xterm-256color',\n colorTerm: 'truecolor',\n scrollback: 256 * 1024,\n cols: 80,\n rows: 24,\n fontSize: 13,\n fontFamily: \"Menlo, Consolas, 'DejaVu Sans Mono', monospace\",\n cursorStyle: 'bar',\n cursorStyleBlink: true,\n copyOnSelect: true,\n rightClickBehavior: 'default' as RightClickBehavior,\n mouseScrollSpeed: 1,\n logs: false,\n theme: DEFAULT_THEME,\n keyboardBindings: DEFAULT_KEYBOARD_BINDINGS,\n env: {},\n};\n\nfunction bindingKey(b: KeyboardBinding): string {\n const mods = [...(b.mods ?? [])].sort().join('+');\n return `${b.key.toLowerCase()}|${mods}`;\n}\n\nconst VALID_MODS = new Set(['shift', 'ctrl', 'alt', 'meta']);\n\nfunction isValidBinding(b: unknown): b is KeyboardBinding {\n if (!b || typeof b !== 'object') return false;\n const o = b as Record<string, unknown>;\n if (typeof o.key !== 'string' || typeof o.chars !== 'string') return false;\n if (o.mods !== undefined && !Array.isArray(o.mods)) return false;\n return true;\n}\n\nfunction normalizeBinding(b: KeyboardBinding): KeyboardBinding {\n return {\n ...b,\n key: b.key.toLowerCase(),\n mods: (b.mods ?? []).map((m) => m.toLowerCase()).filter((m) => VALID_MODS.has(m)),\n };\n}\n\n/**\n * Merges user-supplied bindings over a set of defaults by `(key, mods)` identity.\n * User entries replace matching defaults; unmatched user entries are appended.\n *\n * @param defaults - Default bindings to merge over.\n * @param user - User-supplied bindings that override defaults.\n * @returns Merged bindings with user entries replacing matching defaults and new user entries appended.\n */\n// NOTE: export for testing only\nexport function mergeKeyboardBindings(\n defaults: KeyboardBinding[],\n user: KeyboardBinding[],\n): KeyboardBinding[] {\n const deduped = [...new Map(user.map((b) => [bindingKey(b), b])).values()];\n const overrides = new Map(deduped.map((b) => [bindingKey(b), b]));\n const merged = defaults.map((d) => overrides.get(bindingKey(d)) ?? d);\n const defaultKeys = new Set(defaults.map(bindingKey));\n for (const b of deduped) {\n if (!defaultKeys.has(bindingKey(b))) merged.push(b);\n }\n return merged;\n}\n\n/**\n * Load config from `~/.config/webtty/config.json`, merged over `DEFAULT_CONFIG`.\n *\n * On first run (file absent), writes the default port/host stub and returns defaults.\n * Unknown keys and keys with wrong types are silently ignored.\n * Throws if the file exists but cannot be read or contains invalid JSON.\n *\n * @returns The resolved {@link Config}, with all missing keys filled from {@link DEFAULT_CONFIG}.\n */\nexport function loadConfig(): Config {\n if (!fs.existsSync(getConfigPath())) {\n try {\n initConfig();\n } catch (err) {\n console.warn(\n `webtty: failed to write default config to ${getConfigPath()}: ${(err as Error).message}`,\n );\n return { ...DEFAULT_CONFIG };\n }\n }\n\n let raw: string;\n try {\n raw = fs.readFileSync(getConfigPath(), 'utf8');\n } catch (err) {\n throw new Error(\n `webtty: failed to read config at ${getConfigPath()}: ${(err as Error).message}`,\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(`webtty: invalid JSON in config file ${getConfigPath()}`);\n }\n\n const p = parsed as Partial<Config>;\n return {\n ...DEFAULT_CONFIG,\n ...(typeof p.port === 'number' && { port: p.port }),\n ...(typeof p.host === 'string' && { host: p.host }),\n ...(typeof p.shell === 'string' && { shell: p.shell }),\n ...(typeof p.term === 'string' && { term: p.term }),\n ...(typeof p.colorTerm === 'string' && { colorTerm: p.colorTerm }),\n ...(typeof p.scrollback === 'number' && { scrollback: p.scrollback }),\n ...(typeof p.cols === 'number' && { cols: p.cols }),\n ...(typeof p.rows === 'number' && { rows: p.rows }),\n ...(typeof p.fontSize === 'number' && { fontSize: p.fontSize }),\n ...(typeof p.fontFamily === 'string' && { fontFamily: p.fontFamily }),\n ...(typeof p.cursorStyle === 'string' &&\n (p.cursorStyle === 'block' || p.cursorStyle === 'bar' || p.cursorStyle === 'underline') && {\n cursorStyle: p.cursorStyle,\n }),\n ...(typeof p.cursorStyleBlink === 'boolean' && { cursorStyleBlink: p.cursorStyleBlink }),\n ...(typeof p.copyOnSelect === 'boolean' && { copyOnSelect: p.copyOnSelect }),\n ...(typeof p.rightClickBehavior === 'string' && {\n rightClickBehavior: (p.rightClickBehavior === 'copyPaste'\n ? 'copyPaste'\n : 'default') as RightClickBehavior,\n }),\n ...(typeof p.mouseScrollSpeed === 'number' &&\n p.mouseScrollSpeed > 0 && { mouseScrollSpeed: p.mouseScrollSpeed }),\n ...(typeof p.logs === 'boolean' && { logs: p.logs }),\n ...(p.theme && typeof p.theme === 'object' && { theme: { ...DEFAULT_THEME, ...p.theme } }),\n ...(Array.isArray(p.keyboardBindings) && {\n keyboardBindings: mergeKeyboardBindings(\n DEFAULT_KEYBOARD_BINDINGS,\n p.keyboardBindings.filter(isValidBinding).map(normalizeBinding),\n ),\n }),\n ...(p.env &&\n typeof p.env === 'object' &&\n !Array.isArray(p.env) && {\n env: Object.fromEntries(\n Object.entries(p.env as Record<string, unknown>).filter(\n ([k, v]) => typeof k === 'string' && typeof v === 'string',\n ) as [string, string][],\n ),\n }),\n };\n}\n\n/**\n * Write the initial config stub (`port` + `host` only) to `~/.config/webtty/config.json`.\n *\n * Only writes the two keys that are safe to persist as defaults; all other\n * keys are intentionally omitted so future webtty versions can add new fields\n * without the stub going stale.\n */\nexport function initConfig(): void {\n fs.mkdirSync(path.dirname(getConfigPath()), { recursive: true });\n const content = JSON.stringify(\n {\n port: DEFAULT_CONFIG.port,\n host: DEFAULT_CONFIG.host,\n },\n null,\n 2,\n );\n fs.writeFileSync(getConfigPath(), content, 'utf8');\n}\n",
|
|
9
|
+
"import { spawn } from 'node:child_process';\nimport fs from 'node:fs';\nimport type http from 'node:http';\nimport { homedir } from 'node:os';\nimport path from 'node:path';\nimport { loadConfig } from '../config';\nimport {\n createSession,\n generateId,\n isValidId,\n lastUsedId,\n sessionRegistry,\n sessionToJson,\n setLastUsedId,\n} from './session';\nimport { serveFile } from './static';\nimport { broadcastToSubscribers, closeSession } from './websocket';\n\nconst MAX_BODY = 64 * 1024;\n\nconst CORS_HEADERS = {\n 'Access-Control-Allow-Origin': '*',\n 'Access-Control-Allow-Methods': 'GET, POST, PATCH, DELETE, OPTIONS',\n 'Access-Control-Allow-Headers': 'Content-Type',\n};\n\nfunction setCors(res: http.ServerResponse): void {\n for (const [k, v] of Object.entries(CORS_HEADERS)) res.setHeader(k, v);\n}\n\nfunction decodeId(raw: string): string | null {\n try {\n return decodeURIComponent(raw);\n } catch {\n return null;\n }\n}\n\n/**\n * Reads and JSON-parses the request body (max 64 KB).\n *\n * @param req - The incoming HTTP request.\n * @returns A promise resolving to the parsed JSON object, or an empty object if body is empty.\n * @throws {Error} with `status: 413` if body exceeds 64 KB.\n * @throws {Error} if the body contains invalid JSON.\n */\nexport function readJson(req: http.IncomingMessage): Promise<unknown> {\n return new Promise((resolve, reject) => {\n let body = '';\n req.on('data', (chunk) => {\n body += chunk;\n if (body.length > MAX_BODY) {\n req.destroy();\n reject(Object.assign(new Error('payload too large'), { status: 413 }));\n }\n });\n req.on('end', () => {\n try {\n resolve(body ? JSON.parse(body) : {});\n } catch {\n reject(new Error('invalid JSON'));\n }\n });\n req.on('error', reject);\n });\n}\n\n/**\n * Main HTTP request handler for the webtty server.\n * Dispatches all REST API routes and serves static client assets.\n *\n * @param req - The incoming HTTP request.\n * @param res - The HTTP response object.\n * @param distPath - Path to the server-side dist directory.\n * @param wasmPath - Path to the ghostty-vt.wasm file.\n * @param clientDistPath - Path to the client dist directory.\n * @param onStop - Callback invoked when `POST /api/server/stop` is received.\n */\nexport async function handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n distPath: string,\n wasmPath: string,\n clientDistPath: string,\n onStop: () => void,\n): Promise<void> {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? '127.0.0.1'}`);\n const pathname = url.pathname;\n\n setCors(res);\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.method === 'POST' && pathname === '/api/server/stop') {\n res.writeHead(200, { 'Content-Type': 'text/plain' });\n res.end('stopping');\n onStop();\n return;\n }\n\n if (req.method === 'GET' && pathname === '/api/config') {\n const config = loadConfig();\n // Whitelist client-safe keys — avoid exposing server-side config (shell, host, logs, etc.)\n const clientConfig = {\n cols: config.cols,\n rows: config.rows,\n fontSize: config.fontSize,\n fontFamily: config.fontFamily,\n cursorStyle: config.cursorStyle,\n cursorStyleBlink: config.cursorStyleBlink,\n scrollback: config.scrollback,\n theme: config.theme,\n copyOnSelect: config.copyOnSelect,\n rightClickBehavior: config.rightClickBehavior,\n mouseScrollSpeed: config.mouseScrollSpeed,\n keyboardBindings: config.keyboardBindings,\n };\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(clientConfig));\n return;\n }\n\n if (pathname === '/api/sessions') {\n if (req.method === 'GET') {\n const list = [...sessionRegistry.values()].map(sessionToJson);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(list));\n return;\n }\n\n if (req.method === 'POST') {\n let body: { id?: string; baseDir?: string };\n try {\n body = (await readJson(req)) as { id?: string; baseDir?: string };\n } catch (err) {\n const status = (err as { status?: number }).status === 413 ? 413 : 400;\n res.writeHead(status);\n res.end(status === 413 ? 'Payload Too Large' : 'invalid JSON');\n return;\n }\n\n const id = body.id ?? generateId();\n if (!isValidId(id)) {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: `invalid id: ${id}` }));\n return;\n }\n if (sessionRegistry.has(id)) {\n res.writeHead(409, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: `session already exists: ${id}` }));\n return;\n }\n const baseDir = body.baseDir ?? homedir();\n if (!path.isAbsolute(baseDir)) {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: 'baseDir must be an absolute path' }));\n return;\n }\n if (!fs.existsSync(baseDir)) {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: `baseDir does not exist: ${baseDir}` }));\n return;\n }\n if (!fs.statSync(baseDir).isDirectory()) {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: `baseDir is not a directory: ${baseDir}` }));\n return;\n }\n const session = createSession(id, baseDir);\n res.writeHead(201, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(sessionToJson(session)));\n return;\n }\n }\n\n const sessionMatch = pathname.match(/^\\/api\\/sessions\\/([^/]+)$/);\n if (sessionMatch) {\n const id = decodeId(sessionMatch[1]);\n if (!id) {\n res.writeHead(400);\n res.end('Bad Request');\n return;\n }\n\n if (req.method === 'GET') {\n const session = sessionRegistry.get(id);\n if (!session) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(sessionToJson(session)));\n return;\n }\n\n if (req.method === 'PATCH') {\n let body: { id?: string };\n try {\n body = (await readJson(req)) as { id?: string };\n } catch (err) {\n const status = (err as { status?: number }).status === 413 ? 413 : 400;\n res.writeHead(status);\n res.end(status === 413 ? 'Payload Too Large' : 'invalid JSON');\n return;\n }\n\n const session = sessionRegistry.get(id);\n if (!session) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n\n const newId = body.id;\n if (!newId) {\n res.writeHead(400);\n res.end('missing id');\n return;\n }\n if (!isValidId(newId)) {\n res.writeHead(400, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: `invalid id: ${newId}` }));\n return;\n }\n if (sessionRegistry.has(newId)) {\n res.writeHead(409, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify({ error: `session already exists: ${newId}` }));\n return;\n }\n sessionRegistry.delete(id);\n session.id = newId;\n sessionRegistry.set(newId, session);\n if (lastUsedId === id) setLastUsedId(newId);\n res.writeHead(200, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(sessionToJson(session)));\n return;\n }\n\n if (req.method === 'DELETE') {\n const session = sessionRegistry.get(id);\n if (!session) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n sessionRegistry.delete(id);\n if (lastUsedId === id) setLastUsedId(null);\n closeSession(session);\n res.writeHead(204, { 'X-Sessions-Remaining': String(sessionRegistry.size) });\n res.end();\n return;\n }\n }\n\n if (req.method === 'GET' && (pathname === '/' || pathname === '/index.html')) {\n let targetId = lastUsedId ?? null;\n if (!targetId || !sessionRegistry.has(targetId)) {\n if (!sessionRegistry.has('main')) createSession('main');\n targetId = 'main';\n }\n res.writeHead(302, { Location: `/s/${targetId}` });\n res.end();\n return;\n }\n\n const clientMatch = pathname.match(/^\\/s\\/([^/]+)$/);\n if (req.method === 'GET' && clientMatch) {\n const id = decodeId(clientMatch[1]);\n if (!id || !isValidId(id) || !sessionRegistry.has(id)) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n const clientHtml = path.resolve(clientDistPath, 'client.html');\n serveFile(clientHtml, res);\n return;\n }\n\n const publishMatch = pathname.match(/^\\/s\\/([^/]+)\\/publish$/);\n if (req.method === 'POST' && publishMatch) {\n const id = decodeId(publishMatch[1]);\n if (!id) {\n res.writeHead(400);\n res.end('Bad Request');\n return;\n }\n if (!(req.headers['content-type'] ?? '').startsWith('application/json')) {\n res.writeHead(400);\n res.end('Bad Request');\n return;\n }\n const session = sessionRegistry.get(id);\n if (!session) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n if (session.pty === null) {\n res.writeHead(409);\n res.end('PTY not running');\n return;\n }\n let buf = '';\n req.on('data', (chunk: Buffer) => {\n buf += chunk.toString('utf8');\n const lines = buf.split('\\n');\n buf = lines.pop() ?? '';\n if (buf.length > MAX_BODY) buf = '';\n for (const line of lines) {\n const trimmed = line.replace(/\\r$/, '');\n if (!trimmed) continue;\n try {\n JSON.parse(trimmed);\n broadcastToSubscribers(session, trimmed);\n } catch {\n // skip invalid JSON lines\n }\n }\n });\n req.on('end', () => {\n const trimmed = buf.replace(/\\r$/, '');\n if (trimmed) {\n try {\n JSON.parse(trimmed);\n broadcastToSubscribers(session, trimmed);\n } catch {\n // skip invalid JSON\n }\n }\n res.writeHead(204);\n res.end();\n });\n req.on('error', () => {\n res.writeHead(500);\n res.end();\n });\n return;\n }\n\n const executeMatch = pathname.match(/^\\/s\\/([^/]+)\\/execute$/);\n if (req.method === 'POST' && executeMatch) {\n const id = decodeId(executeMatch[1]);\n if (!id) {\n res.writeHead(400);\n res.end('Bad Request');\n return;\n }\n if (!(req.headers['content-type'] ?? '').startsWith('application/json')) {\n res.writeHead(400);\n res.end('Bad Request');\n return;\n }\n const session = sessionRegistry.get(id);\n if (!session) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n if (session.pty === null) {\n res.writeHead(409);\n res.end('PTY not running');\n return;\n }\n let execBody: unknown;\n try {\n execBody = await readJson(req);\n } catch (err) {\n const status = (err as { status?: number }).status === 413 ? 413 : 400;\n res.writeHead(status);\n res.end(status === 413 ? 'Payload Too Large' : 'invalid JSON');\n return;\n }\n const { cmd, args, stdin } = execBody as { cmd?: unknown; args?: unknown; stdin?: unknown };\n if (typeof cmd !== 'string' || cmd.length === 0) {\n res.writeHead(400);\n res.end('invalid body');\n return;\n }\n if (!Array.isArray(args) || !args.every((a) => typeof a === 'string')) {\n res.writeHead(400);\n res.end('invalid body');\n return;\n }\n let child: ReturnType<typeof spawn>;\n try {\n const execConfig = loadConfig();\n child = spawn(cmd, args as string[], {\n env: { ...process.env, ...execConfig.env },\n cwd: session.baseDir,\n });\n } catch (err) {\n res.writeHead(500);\n res.end(`spawn error: ${String(err)}`);\n return;\n }\n res.writeHead(200, {\n 'Content-Type': 'application/x-ndjson',\n 'Cache-Control': 'no-cache',\n });\n let done = false;\n child.on('error', (err) => {\n if (done) return;\n done = true;\n const msg = err.message;\n const code = (err as NodeJS.ErrnoException).code;\n res.write(`${JSON.stringify({ stream: 'stderr', data: `${msg}\\n` })}\\n`);\n res.write(`${JSON.stringify({ exit: 1, error: msg, ...(code ? { code } : {}) })}\\n`);\n res.end();\n });\n if (typeof stdin === 'string') {\n child.stdin?.write(stdin);\n }\n child.stdin?.end();\n child.stdout?.on('data', (chunk: Buffer) => {\n res.write(`${JSON.stringify({ stream: 'stdout', data: chunk.toString() })}\\n`);\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n res.write(`${JSON.stringify({ stream: 'stderr', data: chunk.toString() })}\\n`);\n });\n child.on('close', (code: number | null) => {\n if (done) return;\n done = true;\n res.write(`${JSON.stringify({ exit: code ?? 1 })}\\n`);\n res.end();\n });\n req.on('close', () => {\n if (!done) child.kill();\n });\n return;\n }\n\n const pidMatch = pathname.match(/^\\/p\\/(\\d+)$/);\n if (req.method === 'GET' && pidMatch) {\n const pid = parseInt(pidMatch[1], 10);\n if (!Number.isFinite(pid) || pid <= 0) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n const session = [...sessionRegistry.values()].find((s) => s.pty?.pid === pid);\n if (!session) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n res.writeHead(302, { Location: `/s/${session.id}` });\n res.end();\n return;\n }\n\n if (pathname.startsWith('/dist/')) {\n const relativePath = pathname.slice(6);\n const ownFile = path.resolve(clientDistPath, relativePath);\n if (ownFile.startsWith(clientDistPath + path.sep)) {\n const fs = await import('node:fs');\n if (fs.existsSync(ownFile)) {\n serveFile(ownFile, res);\n return;\n }\n }\n const filePath = path.resolve(distPath, relativePath);\n if (\n !filePath.startsWith(path.resolve(distPath) + path.sep) &&\n filePath !== path.resolve(distPath)\n ) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n serveFile(filePath, res);\n return;\n }\n\n if (pathname === '/ghostty-vt.wasm') {\n serveFile(wasmPath, res);\n return;\n }\n\n res.writeHead(404);\n res.end('Not Found');\n}\n",
|
|
10
|
+
"import { homedir } from 'node:os';\nimport type { WebSocket as WS } from 'ws';\nimport type { PtyProcess } from '../pty';\n\n/** A running (or recently created) terminal session. */\nexport interface Session {\n /** Unique session identifier. */\n id: string;\n /** Unix timestamp (ms) when the session was created. */\n createdAt: number;\n /** Working directory for the PTY shell. */\n baseDir: string;\n /** The underlying PTY process, or `null` if no shell has been spawned yet. */\n pty: PtyProcess | null;\n /** All currently connected WebSocket clients for this session. */\n clients: Set<WS>;\n /** All currently connected event subscribers for this session. */\n subscribers: Set<WS>;\n /** Accumulated PTY output retained for replay when a new client joins. */\n scrollback: string;\n}\n\n/** All active sessions, keyed by session ID. */\nexport const sessionRegistry = new Map<string, Session>();\n\n/** ID of the most recently opened session, used for `GET /` redirect. */\nexport let lastUsedId: string | null = null;\n\n/** Updates {@link lastUsedId}. */\nexport function setLastUsedId(id: string | null): void {\n lastUsedId = id;\n}\n\nconst ID_RE = /^[a-z0-9\\-_.]{1,64}$/;\n\n/**\n * Returns `true` if `id` is a valid session identifier (lowercase alphanumeric + `-_.`, max 64 chars).\n *\n * @param id - The session ID to validate.\n * @returns `true` if the ID matches the valid format, `false` otherwise.\n */\nexport function isValidId(id: string): boolean {\n return ID_RE.test(id);\n}\n\n/**\n * Generates a random 8-character hex session ID.\n *\n * @returns A random session ID.\n */\nexport function generateId(): string {\n return Math.floor(Math.random() * 0xffffffff)\n .toString(16)\n .padStart(8, '0');\n}\n\n/**\n * Creates a new session, registers it in {@link sessionRegistry}, and returns it.\n *\n * @param id - The session ID.\n * @param baseDir - Working directory for the PTY shell (default: `homedir()`).\n * @returns The newly created {@link Session}.\n */\nexport function createSession(id: string, baseDir = homedir()): Session {\n const session: Session = {\n id,\n createdAt: Date.now(),\n baseDir,\n pty: null,\n clients: new Set(),\n subscribers: new Set(),\n scrollback: '',\n };\n sessionRegistry.set(id, session);\n return session;\n}\n\n/**\n * Returns a plain JSON-safe representation of a session for API responses.\n *\n * @param s - The session to serialize.\n * @returns A JSON-safe object with session ID, creation timestamp, connection status, and PTY PID (or `null` if no PTY has been spawned yet).\n */\nexport function sessionToJson(s: Session) {\n return {\n id: s.id,\n createdAt: s.createdAt,\n baseDir: s.baseDir,\n connected: s.clients.size > 0,\n pid: s.pty?.pid ?? null,\n };\n}\n",
|
|
11
11
|
"import fs from 'node:fs';\nimport type http from 'node:http';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nconst require = createRequire(import.meta.url);\n\n/** File extension → MIME type map used by {@link serveFile}. */\nexport const MIME_TYPES: Record<string, string> = {\n '.html': 'text/html',\n '.js': 'application/javascript',\n '.mjs': 'application/javascript',\n '.css': 'text/css',\n '.json': 'application/json',\n '.wasm': 'application/wasm',\n '.png': 'image/png',\n '.svg': 'image/svg+xml',\n '.ico': 'image/x-icon',\n};\n\n/**\n * Returns the MIME type for `filePath` based on its extension, defaulting to `application/octet-stream`.\n *\n * @param filePath - The file path to determine the MIME type for.\n * @returns The MIME type string.\n */\nexport function mimeType(filePath: string): string {\n const ext = path.extname(filePath);\n return MIME_TYPES[ext] ?? 'application/octet-stream';\n}\n\n/**\n * Strips the `dist/…` suffix from a ghostty-web main entry path to get the package root.\n *\n * @param mainPath - The main entry path from ghostty-web package.\n * @returns The package root directory path.\n */\nexport function ghosttyWebRootFromMain(mainPath: string): string {\n return mainPath.replace(/[/\\\\]dist[/\\\\].*$/, '');\n}\n\n/**\n * Locates the ghostty-web package, preferring assets bundled in `dist/` (npm install)\n * over `node_modules/` (local dev). Exits the process if the package cannot be found.\n *\n * @returns An object with `distPath` (ghostty-web dist directory) and `wasmPath` (ghostty-vt.wasm file).\n * @throws Exits the process with code 1 if ghostty-web cannot be found.\n */\nexport function findGhosttyWeb(): { distPath: string; wasmPath: string } {\n // Prefer assets bundled into dist/ — present when installed via npx/npm.\n const bundledDist = path.join(__dirname, '..', '..', 'dist');\n const bundledWasm = path.join(bundledDist, 'ghostty-vt.wasm');\n if (fs.existsSync(path.join(bundledDist, 'ghostty-web.js')) && fs.existsSync(bundledWasm)) {\n return { distPath: bundledDist, wasmPath: bundledWasm };\n }\n\n // Fall back to node_modules — present during local development.\n try {\n const ghosttyWebMain = require.resolve('ghostty-web') as string;\n const ghosttyWebRoot = ghosttyWebRootFromMain(ghosttyWebMain);\n const distPath = path.join(ghosttyWebRoot, 'dist');\n const wasmPath = path.join(ghosttyWebRoot, 'ghostty-vt.wasm');\n if (fs.existsSync(path.join(distPath, 'ghostty-web.js')) && fs.existsSync(wasmPath)) {\n return { distPath, wasmPath };\n }\n } catch {\n // fall through\n }\n console.error('Error: Could not find ghostty-web package.');\n process.exit(1);\n}\n\n/**\n * Reads `filePath` from disk and writes it to `res` with the correct Content-Type header.\n * Responds with 404 if the file cannot be read.\n *\n * @param filePath - The file path to serve.\n * @param res - The HTTP response object.\n */\nexport function serveFile(filePath: string, res: http.ServerResponse): void {\n const contentType = mimeType(filePath);\n fs.readFile(filePath, (err, data) => {\n if (err) {\n res.writeHead(404);\n res.end('Not Found');\n return;\n }\n res.writeHead(200, { 'Content-Type': contentType });\n res.end(data);\n });\n}\n",
|
|
12
|
-
"import type http from 'node:http';\nimport type { WebSocket as WS } from 'ws';\nimport { WebSocketServer } from 'ws';\nimport { loadConfig } from '../config';\nimport { spawnForSession } from '../pty';\nimport type { Session } from './session';\nimport { sessionRegistry, setLastUsedId } from './session';\n\nconst WS_CLOSE = {\n SERVER_STOPPED: 1001 as const, // RFC 6455: server going away\n BAD_REQUEST: 1008 as const, // RFC 6455: policy violation / bad data\n SESSION_GONE: 4001 as const, // app-level: session deleted or shell exited\n PTY_NOT_RUNNING: 4002 as const, // app-level: PTY not running\n} as const;\n\nfunction closeClients(session: Session, code: number, reason: string): void {\n session.pty?.kill();\n for (const client of session.clients) client.close(code, reason);\n for (const sub of session.subscribers) sub.close(code, reason);\n session.subscribers.clear();\n}\n\n/**\n * Closes all WebSocket clients for `session` with the SESSION_GONE close code.\n *\n * @param session - The session to close.\n */\nexport function closeSession(session: Session): void {\n closeClients(session, WS_CLOSE.SESSION_GONE, 'session deleted');\n}\n\n/** Closes all WebSocket clients across every active session with the SERVER_STOPPED close code. */\nexport function closeAllSessions(): void {\n for (const session of sessionRegistry.values()) {\n closeClients(session, WS_CLOSE.SERVER_STOPPED, 'server stopped');\n }\n}\n\n/**\n * Sends `line` to all open event subscribers of `session`.\n *\n * @param session - The session whose subscribers to notify.\n * @param line - The line to send.\n */\nexport function broadcastToSubscribers(session: Session, line: string): void {\n for (const sub of session.subscribers) {\n if (sub.readyState === sub.OPEN) sub.send(line);\n }\n}\n\nlet onLastSessionClosed: (() => void) | null = null;\n\n/**\n * Registers a callback invoked once the last session closes (e.g. to stop the HTTP server).\n *\n * @param handler - Callback to invoke when the last session closes.\n */\nexport function setLastSessionClosedHandler(handler: () => void): void {\n onLastSessionClosed = handler;\n}\n\nconst DIM = '\\x1b[2m';\nconst RESET = '\\x1b[0m';\nconst BOLD = '\\x1b[1m';\nconst ITALIC = '\\x1b[3m';\nconst YELLOW = '\\x1b[1;33m';\nconst CYAN = '\\x1b[1;36m';\n\nconst isBun = process.execPath.includes('bun');\nconst PKG_RUNNER = isBun ? 'bunx' : 'npx';\n\nfunction sessionBanner(): string {\n const W = 48;\n const pipe = (content: string) => `${CYAN}║${RESET}${content}${CYAN}║${RESET}`;\n const blank = pipe(' '.repeat(W));\n\n const titleVis = ' [ webtty ] Terminal UI in the browser';\n const titleStr = ` ${DIM}[${RESET} ${BOLD}${YELLOW}webtty${RESET} ${DIM}] Terminal UI in the browser${RESET}`;\n const titleLine = pipe(titleStr + ' '.repeat(W - titleVis.length));\n\n const cmd = `${PKG_RUNNER} webtty help`;\n const helpVis = ` Run \\`${cmd}\\` for more information.`;\n const helpStr = ` ${DIM}Run \\`${RESET}${ITALIC}${cmd}${RESET}${DIM}\\` for more information.${RESET}`;\n const helpLine = pipe(helpStr + ' '.repeat(W - helpVis.length));\n\n return [\n `${CYAN}╔${'═'.repeat(W)}╗${RESET}`,\n '\\r\\n',\n blank,\n '\\r\\n',\n titleLine,\n '\\r\\n',\n blank,\n '\\r\\n',\n helpLine,\n '\\r\\n',\n blank,\n '\\r\\n',\n `${CYAN}╚${'═'.repeat(W)}╝${RESET}`,\n '\\r\\n',\n '\\r\\n',\n ].join('');\n}\n\nfunction handleSubscribe(ws: WS, id: string): void {\n const session = sessionRegistry.get(id);\n if (!session) {\n ws.close(WS_CLOSE.SESSION_GONE, 'session deleted');\n return;\n }\n if (session.pty === null) {\n ws.close(WS_CLOSE.PTY_NOT_RUNNING, 'PTY not running');\n return;\n }\n session.subscribers.add(ws);\n ws.on('close', () => {\n session.subscribers.delete(ws);\n });\n ws.on('error', () => {});\n}\n\n/**\n * Attaches a WebSocket server to `httpServer`, handling PTY I/O, session management,\n * scrollback replay, and terminal resize for all `/ws/:id/pty` connections,\n * and event subscriptions for `/ws/:id/events` connections.\n *\n * @param httpServer - The HTTP server to attach the WebSocket server to.\n * @returns The configured {@link WebSocketServer}.\n */\nexport function createWebSocketServer(httpServer: http.Server): WebSocketServer {\n const wss = new WebSocketServer({ noServer: true });\n\n httpServer.on('upgrade', (req, socket, head) => {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? '127.0.0.1'}`);\n if (\n url.pathname.match(/^\\/ws\\/([^/]+)\\/pty$/) ||\n url.pathname.match(/^\\/ws\\/([^/]+)\\/events$/)\n ) {\n wss.handleUpgrade(req, socket, head, (ws) => wss.emit('connection', ws, req));\n } else {\n socket.destroy();\n }\n });\n\n wss.on('connection', (ws: WS, req: http.IncomingMessage) => {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? '127.0.0.1'}`);\n const ptyMatch = url.pathname.match(/^\\/ws\\/([^/]+)\\/pty$/);\n const eventsMatch = url.pathname.match(/^\\/ws\\/([^/]+)\\/events$/);\n const match = ptyMatch ?? eventsMatch;\n\n if (!match) {\n ws.close();\n return;\n }\n\n let id: string;\n try {\n id = decodeURIComponent(match[1]);\n } catch {\n ws.close(WS_CLOSE.BAD_REQUEST, 'Bad Request');\n return;\n }\n\n if (eventsMatch) {\n handleSubscribe(ws, id);\n return;\n }\n\n const cols = Math.max(\n 1,\n Math.min(1000, Number.parseInt(url.searchParams.get('cols') ?? '80', 10) || 80),\n );\n const rows = Math.max(\n 1,\n Math.min(500, Number.parseInt(url.searchParams.get('rows') ?? '24', 10) || 24),\n );\n\n if (!sessionRegistry.has(id)) {\n ws.close(WS_CLOSE.SESSION_GONE, 'session deleted');\n return;\n }\n\n const session = sessionRegistry.get(id);\n if (!session) {\n ws.close(WS_CLOSE.SESSION_GONE, 'session deleted');\n return;\n }\n session.clients.add(ws);\n setLastUsedId(id);\n\n if (!session.pty) {\n const config = loadConfig();\n session.pty = spawnForSession(cols
|
|
13
|
-
"export type { PtyProcess } from './types';\n\n// Bun.Terminal does not implement PTY on Windows (Bun.spawn({ terminal })\n// is a no-op there), so fall back to node-pty on that platform.\n// On all other platforms, prefer Bun.Terminal when running under Bun.\nconst isBun = !!process.versions.bun && process.platform !== 'win32';\nconsole.log(`pty: ${isBun ? 'Bun.Terminal' : 'node-pty'}`);\n\nconst { spawn: _spawn } = await (isBun ? import('./bun') : import('./node'));\n\nexport const spawn = _spawn;\n\n/**\n * Convenience wrapper: spawns a PTY using session-oriented parameters from config.\n *\n * @param cols - Terminal width in columns.\n * @param rows - Terminal height in rows.\n * @param shell - Shell executable path (e.g., `/bin/bash`).\n * @param term - `$TERM` environment variable (e.g., `xterm-256color`).\n * @param colorTerm - `$COLORTERM` environment variable (e.g., `truecolor`).\n * @returns A {@link PtyProcess} handle for reading/writing and managing the PTY.\n */\nexport function spawnForSession(\n cols: number,\n rows: number,\n shell: string,\n term: string,\n colorTerm: string,\n) {\n return _spawn(shell, cols, rows, term, colorTerm);\n}\n"
|
|
12
|
+
"import type http from 'node:http';\nimport type { WebSocket as WS } from 'ws';\nimport { WebSocketServer } from 'ws';\nimport { loadConfig } from '../config';\nimport { spawnForSession } from '../pty';\nimport type { Session } from './session';\nimport { sessionRegistry, setLastUsedId } from './session';\n\nconst WS_CLOSE = {\n SERVER_STOPPED: 1001 as const, // RFC 6455: server going away\n BAD_REQUEST: 1008 as const, // RFC 6455: policy violation / bad data\n SESSION_GONE: 4001 as const, // app-level: session deleted or shell exited\n PTY_NOT_RUNNING: 4002 as const, // app-level: PTY not running\n} as const;\n\nfunction closeClients(session: Session, code: number, reason: string): void {\n session.pty?.kill();\n for (const client of session.clients) client.close(code, reason);\n for (const sub of session.subscribers) sub.close(code, reason);\n session.subscribers.clear();\n}\n\n/**\n * Closes all WebSocket clients for `session` with the SESSION_GONE close code.\n *\n * @param session - The session to close.\n */\nexport function closeSession(session: Session): void {\n closeClients(session, WS_CLOSE.SESSION_GONE, 'session deleted');\n}\n\n/** Closes all WebSocket clients across every active session with the SERVER_STOPPED close code. */\nexport function closeAllSessions(): void {\n for (const session of sessionRegistry.values()) {\n closeClients(session, WS_CLOSE.SERVER_STOPPED, 'server stopped');\n }\n}\n\n/**\n * Sends `line` to all open event subscribers of `session`.\n *\n * @param session - The session whose subscribers to notify.\n * @param line - The line to send.\n */\nexport function broadcastToSubscribers(session: Session, line: string): void {\n for (const sub of session.subscribers) {\n if (sub.readyState === sub.OPEN) sub.send(line);\n }\n}\n\nlet onLastSessionClosed: (() => void) | null = null;\n\n/**\n * Registers a callback invoked once the last session closes (e.g. to stop the HTTP server).\n *\n * @param handler - Callback to invoke when the last session closes.\n */\nexport function setLastSessionClosedHandler(handler: () => void): void {\n onLastSessionClosed = handler;\n}\n\nconst DIM = '\\x1b[2m';\nconst RESET = '\\x1b[0m';\nconst BOLD = '\\x1b[1m';\nconst ITALIC = '\\x1b[3m';\nconst YELLOW = '\\x1b[1;33m';\nconst CYAN = '\\x1b[1;36m';\n\nconst isBun = process.execPath.includes('bun');\nconst PKG_RUNNER = isBun ? 'bunx' : 'npx';\n\nfunction sessionBanner(): string {\n const W = 48;\n const pipe = (content: string) => `${CYAN}║${RESET}${content}${CYAN}║${RESET}`;\n const blank = pipe(' '.repeat(W));\n\n const titleVis = ' [ webtty ] Terminal UI in the browser';\n const titleStr = ` ${DIM}[${RESET} ${BOLD}${YELLOW}webtty${RESET} ${DIM}] Terminal UI in the browser${RESET}`;\n const titleLine = pipe(titleStr + ' '.repeat(W - titleVis.length));\n\n const cmd = `${PKG_RUNNER} webtty help`;\n const helpVis = ` Run \\`${cmd}\\` for more information.`;\n const helpStr = ` ${DIM}Run \\`${RESET}${ITALIC}${cmd}${RESET}${DIM}\\` for more information.${RESET}`;\n const helpLine = pipe(helpStr + ' '.repeat(W - helpVis.length));\n\n return [\n `${CYAN}╔${'═'.repeat(W)}╗${RESET}`,\n '\\r\\n',\n blank,\n '\\r\\n',\n titleLine,\n '\\r\\n',\n blank,\n '\\r\\n',\n helpLine,\n '\\r\\n',\n blank,\n '\\r\\n',\n `${CYAN}╚${'═'.repeat(W)}╝${RESET}`,\n '\\r\\n',\n '\\r\\n',\n ].join('');\n}\n\nfunction handleSubscribe(ws: WS, id: string): void {\n const session = sessionRegistry.get(id);\n if (!session) {\n ws.close(WS_CLOSE.SESSION_GONE, 'session deleted');\n return;\n }\n if (session.pty === null) {\n ws.close(WS_CLOSE.PTY_NOT_RUNNING, 'PTY not running');\n return;\n }\n session.subscribers.add(ws);\n ws.on('close', () => {\n session.subscribers.delete(ws);\n });\n ws.on('error', () => {});\n}\n\n/**\n * Attaches a WebSocket server to `httpServer`, handling PTY I/O, session management,\n * scrollback replay, and terminal resize for all `/ws/:id/pty` connections,\n * and event subscriptions for `/ws/:id/events` connections.\n *\n * @param httpServer - The HTTP server to attach the WebSocket server to.\n * @returns The configured {@link WebSocketServer}.\n */\nexport function createWebSocketServer(httpServer: http.Server): WebSocketServer {\n const wss = new WebSocketServer({ noServer: true });\n\n httpServer.on('upgrade', (req, socket, head) => {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? '127.0.0.1'}`);\n if (\n url.pathname.match(/^\\/ws\\/([^/]+)\\/pty$/) ||\n url.pathname.match(/^\\/ws\\/([^/]+)\\/events$/)\n ) {\n wss.handleUpgrade(req, socket, head, (ws) => wss.emit('connection', ws, req));\n } else {\n socket.destroy();\n }\n });\n\n wss.on('connection', (ws: WS, req: http.IncomingMessage) => {\n const url = new URL(req.url ?? '/', `http://${req.headers.host ?? '127.0.0.1'}`);\n const ptyMatch = url.pathname.match(/^\\/ws\\/([^/]+)\\/pty$/);\n const eventsMatch = url.pathname.match(/^\\/ws\\/([^/]+)\\/events$/);\n const match = ptyMatch ?? eventsMatch;\n\n if (!match) {\n ws.close();\n return;\n }\n\n let id: string;\n try {\n id = decodeURIComponent(match[1]);\n } catch {\n ws.close(WS_CLOSE.BAD_REQUEST, 'Bad Request');\n return;\n }\n\n if (eventsMatch) {\n handleSubscribe(ws, id);\n return;\n }\n\n const cols = Math.max(\n 1,\n Math.min(1000, Number.parseInt(url.searchParams.get('cols') ?? '80', 10) || 80),\n );\n const rows = Math.max(\n 1,\n Math.min(500, Number.parseInt(url.searchParams.get('rows') ?? '24', 10) || 24),\n );\n\n if (!sessionRegistry.has(id)) {\n ws.close(WS_CLOSE.SESSION_GONE, 'session deleted');\n return;\n }\n\n const session = sessionRegistry.get(id);\n if (!session) {\n ws.close(WS_CLOSE.SESSION_GONE, 'session deleted');\n return;\n }\n session.clients.add(ws);\n setLastUsedId(id);\n\n if (!session.pty) {\n const config = loadConfig();\n session.pty = spawnForSession(\n cols,\n rows,\n config.shell,\n config.term,\n config.colorTerm,\n session.baseDir,\n config.env,\n );\n\n session.pty.onData((data: string) => {\n session.scrollback = (session.scrollback + data).slice(-config.scrollback);\n for (const client of session.clients) {\n if (client.readyState === client.OPEN) {\n client.send(data, { binary: false });\n }\n }\n });\n\n session.pty.onExit(() => {\n sessionRegistry.delete(session.id);\n for (const client of session.clients) {\n if (client.readyState === client.OPEN) {\n client.close(WS_CLOSE.SESSION_GONE, 'shell exited');\n }\n }\n for (const sub of session.subscribers) {\n if (sub.readyState === sub.OPEN) {\n sub.close(WS_CLOSE.SESSION_GONE, 'shell exited');\n }\n }\n session.subscribers.clear();\n session.pty = null;\n if (sessionRegistry.size === 0) onLastSessionClosed?.();\n });\n\n const banner = sessionBanner();\n ws.send(banner);\n session.scrollback = banner;\n session.pty.write('\\n');\n } else {\n if (session.scrollback) {\n ws.send(session.scrollback, { binary: false });\n }\n session.pty.resize(cols, rows);\n }\n\n ws.on('message', (data: Buffer) => {\n const message = data.toString('utf8');\n if (message.startsWith('{')) {\n try {\n const msg = JSON.parse(message) as { type: string; cols: number; rows: number };\n if (msg.type === 'resize') {\n const c = Math.max(1, Math.min(1000, Math.trunc(msg.cols) || 80));\n const r = Math.max(1, Math.min(500, Math.trunc(msg.rows) || 24));\n session.pty?.resize(c, r);\n return;\n }\n } catch {\n // fall through\n }\n }\n session.pty?.write(message);\n });\n\n ws.on('close', () => {\n session.clients.delete(ws);\n });\n\n ws.on('error', () => {});\n });\n\n return wss;\n}\n",
|
|
13
|
+
"export type { PtyProcess } from './types';\n\n// Bun.Terminal does not implement PTY on Windows (Bun.spawn({ terminal })\n// is a no-op there), so fall back to node-pty on that platform.\n// On all other platforms, prefer Bun.Terminal when running under Bun.\nconst isBun = !!process.versions.bun && process.platform !== 'win32';\nconsole.log(`pty: ${isBun ? 'Bun.Terminal' : 'node-pty'}`);\n\nconst { spawn: _spawn } = await (isBun ? import('./bun') : import('./node'));\n\nexport const spawn = _spawn;\n\n/**\n * Convenience wrapper: spawns a PTY using session-oriented parameters from config.\n *\n * @param cols - Terminal width in columns.\n * @param rows - Terminal height in rows.\n * @param shell - Shell executable path (e.g., `/bin/bash`).\n * @param term - `$TERM` environment variable (e.g., `xterm-256color`).\n * @param colorTerm - `$COLORTERM` environment variable (e.g., `truecolor`).\n * @param cwd - Working directory for the shell.\n * @returns A {@link PtyProcess} handle for reading/writing and managing the PTY.\n */\nexport function spawnForSession(\n cols: number,\n rows: number,\n shell: string,\n term: string,\n colorTerm: string,\n cwd: string,\n env: Record<string, string>,\n) {\n return _spawn(shell, cols, rows, term, colorTerm, cwd, env);\n}\n"
|
|
14
14
|
],
|
|
15
|
-
"mappings": "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",
|
|
16
|
-
"debugId": "
|
|
15
|
+
"mappings": "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",
|
|
16
|
+
"debugId": "17F1B5AF7114819864756E2164756E21",
|
|
17
17
|
"names": []
|
|
18
18
|
}
|