@monotykamary/localterm-server 0.0.15

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.
Files changed (62) hide show
  1. package/LICENSE +21 -0
  2. package/dist/constants.d.ts +44 -0
  3. package/dist/constants.d.ts.map +1 -0
  4. package/dist/constants.js +80 -0
  5. package/dist/constants.js.map +1 -0
  6. package/dist/default-shell.d.ts +2 -0
  7. package/dist/default-shell.d.ts.map +1 -0
  8. package/dist/default-shell.js +44 -0
  9. package/dist/default-shell.js.map +1 -0
  10. package/dist/ensure-spawn-helper-executable.d.ts +2 -0
  11. package/dist/ensure-spawn-helper-executable.d.ts.map +1 -0
  12. package/dist/ensure-spawn-helper-executable.js +34 -0
  13. package/dist/ensure-spawn-helper-executable.js.map +1 -0
  14. package/dist/errors.d.ts +64 -0
  15. package/dist/errors.d.ts.map +1 -0
  16. package/dist/errors.js +84 -0
  17. package/dist/errors.js.map +1 -0
  18. package/dist/index.d.ts +22 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +369 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/protocol.d.ts +5 -0
  23. package/dist/protocol.d.ts.map +1 -0
  24. package/dist/protocol.js +3 -0
  25. package/dist/protocol.js.map +1 -0
  26. package/dist/schemas.d.ts +36 -0
  27. package/dist/schemas.d.ts.map +1 -0
  28. package/dist/schemas.js +73 -0
  29. package/dist/schemas.js.map +1 -0
  30. package/dist/security.d.ts +5 -0
  31. package/dist/security.d.ts.map +1 -0
  32. package/dist/security.js +60 -0
  33. package/dist/security.js.map +1 -0
  34. package/dist/session-registry.d.ts +9 -0
  35. package/dist/session-registry.d.ts.map +1 -0
  36. package/dist/session-registry.js +19 -0
  37. package/dist/session-registry.js.map +1 -0
  38. package/dist/session.d.ts +61 -0
  39. package/dist/session.d.ts.map +1 -0
  40. package/dist/session.js +245 -0
  41. package/dist/session.js.map +1 -0
  42. package/dist/static-resolver.d.ts +7 -0
  43. package/dist/static-resolver.d.ts.map +1 -0
  44. package/dist/static-resolver.js +64 -0
  45. package/dist/static-resolver.js.map +1 -0
  46. package/dist/types.d.ts +12 -0
  47. package/dist/types.d.ts.map +1 -0
  48. package/dist/types.js +2 -0
  49. package/dist/types.js.map +1 -0
  50. package/dist/utils/format-working-directory-title.d.ts +2 -0
  51. package/dist/utils/format-working-directory-title.d.ts.map +1 -0
  52. package/dist/utils/format-working-directory-title.js +28 -0
  53. package/dist/utils/format-working-directory-title.js.map +1 -0
  54. package/dist/utils/parse-osc7.d.ts +2 -0
  55. package/dist/utils/parse-osc7.d.ts.map +1 -0
  56. package/dist/utils/parse-osc7.js +50 -0
  57. package/dist/utils/parse-osc7.js.map +1 -0
  58. package/dist/utils/resolve-cwd-for-pid.d.ts +2 -0
  59. package/dist/utils/resolve-cwd-for-pid.d.ts.map +1 -0
  60. package/dist/utils/resolve-cwd-for-pid.js +29 -0
  61. package/dist/utils/resolve-cwd-for-pid.js.map +1 -0
  62. package/package.json +69 -0
@@ -0,0 +1,61 @@
1
+ import { EventEmitter } from "node:events";
2
+ import type { SpawnPtyInput } from "./types.js";
3
+ interface SessionEvents {
4
+ output: [data: string];
5
+ exit: [code: number | null];
6
+ title: [title: string];
7
+ cwd: [cwd: string];
8
+ foreground: [process: string | null];
9
+ }
10
+ export declare class Session extends EventEmitter<SessionEvents> {
11
+ readonly shell: string;
12
+ readonly cwd: string;
13
+ readonly createdAt: number;
14
+ private readonly pty;
15
+ private readonly shellName;
16
+ private currentCols;
17
+ private currentRows;
18
+ private exited;
19
+ private paused;
20
+ private titlePollTimer;
21
+ private lastEmittedTitle;
22
+ private lastEmittedCwd;
23
+ private lastEmittedForeground;
24
+ private nextCwdResolveAt;
25
+ private osc7Detected;
26
+ constructor(input: SpawnPtyInput);
27
+ get pid(): number;
28
+ get shellBaseName(): string;
29
+ get cols(): number;
30
+ get rows(): number;
31
+ get isExited(): boolean;
32
+ get isPaused(): boolean;
33
+ write(data: string): void;
34
+ /**
35
+ * Stop reading data from the PTY. node-pty buffers further child output in
36
+ * the OS pipe; once that fills, write() in the child process blocks, which
37
+ * propagates flow control all the way back to the producing program (e.g.
38
+ * `cat large_file`). Used by the WS layer to pause heavy output streams
39
+ * when the outbound socket buffer is filling, so we don't have to kill the
40
+ * connection just to recover memory.
41
+ */
42
+ pause(): void;
43
+ /**
44
+ * Reverse of `pause()`. Buffered child output starts flowing again and the
45
+ * child process unblocks if it was stuck in write().
46
+ */
47
+ resume(): void;
48
+ resize(cols: number, rows: number): void;
49
+ kill(signal?: NodeJS.Signals): void;
50
+ dispose(): void;
51
+ private onPtyOutput;
52
+ private scheduleTitlePoll;
53
+ private runTitlePoll;
54
+ private foregroundProcess;
55
+ private pollForeground;
56
+ private computeTitle;
57
+ private resolveLiveCwd;
58
+ private stopTitlePolling;
59
+ }
60
+ export {};
61
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAqB3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAKhD,UAAU,aAAa;IACrB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACvB,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5B,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACvB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACnB,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;CACtC;AAED,qBAAa,OAAQ,SAAQ,YAAY,CAAC,aAAa,CAAC;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAO;IAC3B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,gBAAgB,CAAM;IAC9B,OAAO,CAAC,cAAc,CAAM;IAC5B,OAAO,CAAC,qBAAqB,CAAwC;IACrE,OAAO,CAAC,gBAAgB,CAAK;IAI7B,OAAO,CAAC,YAAY,CAAS;gBAEjB,KAAK,EAAE,aAAa;IA8ChC,IAAI,GAAG,IAAI,MAAM,CAEhB;IAED,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKzB;;;;;;;OAOG;IACH,KAAK,IAAI,IAAI;IAUb;;;OAGG;IACH,MAAM,IAAI,IAAI;IAUd,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAaxC,IAAI,CAAC,MAAM,GAAE,MAAM,CAAC,OAAkB,GAAG,IAAI;IAS7C,OAAO,IAAI,IAAI;IAOf,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,iBAAiB;YAQX,YAAY;IA2B1B,OAAO,CAAC,iBAAiB;IAKzB,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,YAAY;YAMN,cAAc;IAS5B,OAAO,CAAC,gBAAgB;CAKzB"}
@@ -0,0 +1,245 @@
1
+ import { EventEmitter } from "node:events";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { spawn } from "node-pty";
5
+ import { COLORTERM_VALUE, CWD_RESOLVE_BACKOFF_MS, CWD_RESOLVE_COOLDOWN_MS, DEFAULT_COLS, DEFAULT_ROWS, PTY_ENV_DENYLIST, TERM_TYPE, TITLE_POLL_INTERVAL_MS, } from "./constants.js";
6
+ // Note: titles are emitted on a dedicated `title` event so they travel as a
7
+ // separate WebSocket frame. We deliberately do NOT splice OSC sequences into
8
+ // the PTY output stream — doing so corrupts in-flight escape sequences from
9
+ // modern TUIs (e.g. Cursor Agent / Claude Code use DECSET 2026 synchronized
10
+ // output mode and any byte landing inside that frame breaks the parser state).
11
+ import { ensureSpawnHelperExecutable } from "./ensure-spawn-helper-executable.js";
12
+ import { getDefaultShell } from "./default-shell.js";
13
+ import { formatWorkingDirectoryTitle } from "./utils/format-working-directory-title.js";
14
+ import { parseOsc7FromChunk } from "./utils/parse-osc7.js";
15
+ import { resolveCwdForPid } from "./utils/resolve-cwd-for-pid.js";
16
+ export class Session extends EventEmitter {
17
+ shell;
18
+ cwd;
19
+ createdAt;
20
+ pty;
21
+ shellName;
22
+ currentCols;
23
+ currentRows;
24
+ exited = false;
25
+ paused = false;
26
+ titlePollTimer = null;
27
+ lastEmittedTitle = "";
28
+ lastEmittedCwd = "";
29
+ lastEmittedForeground = undefined;
30
+ nextCwdResolveAt = 0;
31
+ // Set to true once OSC 7 is observed from the PTY output. Once the shell
32
+ // advertises its working directory via OSC 7 we stop polling lsof entirely —
33
+ // the stream is the source of truth and lsof is just an expensive fallback.
34
+ osc7Detected = false;
35
+ constructor(input) {
36
+ super();
37
+ ensureSpawnHelperExecutable();
38
+ this.shell = input.shell ?? getDefaultShell();
39
+ this.shellName = path.basename(this.shell);
40
+ this.cwd = input.cwd ?? os.homedir();
41
+ this.currentCols = input.cols ?? DEFAULT_COLS;
42
+ this.currentRows = input.rows ?? DEFAULT_ROWS;
43
+ this.createdAt = Date.now();
44
+ const env = {};
45
+ const denied = new Set(PTY_ENV_DENYLIST);
46
+ for (const [key, value] of Object.entries(process.env)) {
47
+ if (denied.has(key))
48
+ continue;
49
+ if (typeof value === "string")
50
+ env[key] = value;
51
+ }
52
+ if (input.env) {
53
+ for (const [key, value] of Object.entries(input.env)) {
54
+ env[key] = value;
55
+ }
56
+ }
57
+ env.TERM = TERM_TYPE;
58
+ env.COLORTERM = COLORTERM_VALUE;
59
+ this.pty = spawn(this.shell, [], {
60
+ name: TERM_TYPE,
61
+ cols: this.currentCols,
62
+ rows: this.currentRows,
63
+ cwd: this.cwd,
64
+ env,
65
+ });
66
+ this.pty.onData((data) => {
67
+ this.onPtyOutput(data);
68
+ this.emit("output", data);
69
+ });
70
+ this.pty.onExit(({ exitCode }) => {
71
+ this.exited = true;
72
+ this.stopTitlePolling();
73
+ this.emit("exit", exitCode);
74
+ });
75
+ this.scheduleTitlePoll(0);
76
+ }
77
+ get pid() {
78
+ return this.pty.pid;
79
+ }
80
+ get shellBaseName() {
81
+ return this.shellName;
82
+ }
83
+ get cols() {
84
+ return this.currentCols;
85
+ }
86
+ get rows() {
87
+ return this.currentRows;
88
+ }
89
+ get isExited() {
90
+ return this.exited;
91
+ }
92
+ get isPaused() {
93
+ return this.paused;
94
+ }
95
+ write(data) {
96
+ if (this.exited)
97
+ return;
98
+ this.pty.write(data);
99
+ }
100
+ /**
101
+ * Stop reading data from the PTY. node-pty buffers further child output in
102
+ * the OS pipe; once that fills, write() in the child process blocks, which
103
+ * propagates flow control all the way back to the producing program (e.g.
104
+ * `cat large_file`). Used by the WS layer to pause heavy output streams
105
+ * when the outbound socket buffer is filling, so we don't have to kill the
106
+ * connection just to recover memory.
107
+ */
108
+ pause() {
109
+ if (this.exited || this.paused)
110
+ return;
111
+ this.paused = true;
112
+ try {
113
+ this.pty.pause();
114
+ }
115
+ catch {
116
+ /* PTY may have died between the exited check and the call */
117
+ }
118
+ }
119
+ /**
120
+ * Reverse of `pause()`. Buffered child output starts flowing again and the
121
+ * child process unblocks if it was stuck in write().
122
+ */
123
+ resume() {
124
+ if (this.exited || !this.paused)
125
+ return;
126
+ this.paused = false;
127
+ try {
128
+ this.pty.resume();
129
+ }
130
+ catch {
131
+ /* see pause() */
132
+ }
133
+ }
134
+ resize(cols, rows) {
135
+ if (this.exited)
136
+ return;
137
+ if (cols <= 0 || rows <= 0)
138
+ return;
139
+ if (cols === this.currentCols && rows === this.currentRows)
140
+ return;
141
+ this.currentCols = cols;
142
+ this.currentRows = rows;
143
+ try {
144
+ this.pty.resize(cols, rows);
145
+ }
146
+ catch {
147
+ /* PTY may have died between checks */
148
+ }
149
+ }
150
+ kill(signal = "SIGHUP") {
151
+ if (this.exited)
152
+ return;
153
+ try {
154
+ this.pty.kill(signal);
155
+ }
156
+ catch {
157
+ /* already gone */
158
+ }
159
+ }
160
+ dispose() {
161
+ this.kill();
162
+ this.exited = true;
163
+ this.stopTitlePolling();
164
+ this.removeAllListeners();
165
+ }
166
+ onPtyOutput(data) {
167
+ const osc7Path = parseOsc7FromChunk(data);
168
+ if (osc7Path) {
169
+ this.osc7Detected = true;
170
+ if (osc7Path !== this.lastEmittedCwd) {
171
+ this.lastEmittedCwd = osc7Path;
172
+ this.emit("cwd", osc7Path);
173
+ }
174
+ }
175
+ }
176
+ scheduleTitlePoll(delayMs) {
177
+ if (this.exited)
178
+ return;
179
+ this.titlePollTimer = setTimeout(() => {
180
+ void this.runTitlePoll();
181
+ }, delayMs);
182
+ this.titlePollTimer.unref();
183
+ }
184
+ async runTitlePoll() {
185
+ if (this.exited)
186
+ return;
187
+ try {
188
+ // Once the shell advertises its CWD via OSC 7 we skip lsof — the stream
189
+ // is authoritative and lsof is expensive (spawns a child process that
190
+ // triggers syspolicyd validation on every call).
191
+ const liveCwd = this.osc7Detected ? this.lastEmittedCwd || null : await this.resolveLiveCwd();
192
+ if (this.exited)
193
+ return;
194
+ if (!this.osc7Detected && liveCwd && liveCwd !== this.lastEmittedCwd) {
195
+ this.lastEmittedCwd = liveCwd;
196
+ this.emit("cwd", liveCwd);
197
+ }
198
+ const nextTitle = this.computeTitle(this.osc7Detected ? this.lastEmittedCwd || this.cwd : (liveCwd ?? this.cwd));
199
+ if (nextTitle && nextTitle !== this.lastEmittedTitle) {
200
+ this.lastEmittedTitle = nextTitle;
201
+ this.emit("title", nextTitle);
202
+ }
203
+ this.pollForeground();
204
+ }
205
+ catch {
206
+ /* polling errors are non-fatal; the next tick will retry */
207
+ }
208
+ finally {
209
+ this.scheduleTitlePoll(TITLE_POLL_INTERVAL_MS);
210
+ }
211
+ }
212
+ foregroundProcess() {
213
+ const raw = this.pty.process?.trim() ?? "";
214
+ return raw && raw !== this.shellName ? raw : null;
215
+ }
216
+ pollForeground() {
217
+ const next = this.foregroundProcess();
218
+ if (next === this.lastEmittedForeground)
219
+ return;
220
+ this.lastEmittedForeground = next;
221
+ this.emit("foreground", next);
222
+ }
223
+ computeTitle(liveCwd) {
224
+ const foreground = this.foregroundProcess();
225
+ if (foreground)
226
+ return foreground;
227
+ return formatWorkingDirectoryTitle(liveCwd);
228
+ }
229
+ async resolveLiveCwd() {
230
+ const now = Date.now();
231
+ if (now < this.nextCwdResolveAt)
232
+ return null;
233
+ const liveCwd = await resolveCwdForPid(this.pid).catch(() => null);
234
+ this.nextCwdResolveAt =
235
+ now + (liveCwd === null ? CWD_RESOLVE_BACKOFF_MS : CWD_RESOLVE_COOLDOWN_MS);
236
+ return liveCwd;
237
+ }
238
+ stopTitlePolling() {
239
+ if (this.titlePollTimer === null)
240
+ return;
241
+ clearTimeout(this.titlePollTimer);
242
+ this.titlePollTimer = null;
243
+ }
244
+ }
245
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,KAAK,EAAa,MAAM,UAAU,CAAC;AAC5C,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,uBAAuB,EACvB,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,SAAS,EACT,sBAAsB,GACvB,MAAM,gBAAgB,CAAC;AACxB,4EAA4E;AAC5E,6EAA6E;AAC7E,4EAA4E;AAC5E,4EAA4E;AAC5E,+EAA+E;AAC/E,OAAO,EAAE,2BAA2B,EAAE,MAAM,qCAAqC,CAAC;AAClF,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAC;AACxF,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAUlE,MAAM,OAAO,OAAQ,SAAQ,YAA2B;IAC7C,KAAK,CAAS;IACd,GAAG,CAAS;IACZ,SAAS,CAAS;IAEV,GAAG,CAAO;IACV,SAAS,CAAS;IAC3B,WAAW,CAAS;IACpB,WAAW,CAAS;IACpB,MAAM,GAAG,KAAK,CAAC;IACf,MAAM,GAAG,KAAK,CAAC;IACf,cAAc,GAA0B,IAAI,CAAC;IAC7C,gBAAgB,GAAG,EAAE,CAAC;IACtB,cAAc,GAAG,EAAE,CAAC;IACpB,qBAAqB,GAA8B,SAAS,CAAC;IAC7D,gBAAgB,GAAG,CAAC,CAAC;IAC7B,yEAAyE;IACzE,6EAA6E;IAC7E,4EAA4E;IACpE,YAAY,GAAG,KAAK,CAAC;IAE7B,YAAY,KAAoB;QAC9B,KAAK,EAAE,CAAC;QACR,2BAA2B,EAAE,CAAC;QAC9B,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,IAAI,eAAe,EAAE,CAAC;QAC9C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3C,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,EAAE,CAAC,OAAO,EAAE,CAAC;QACrC,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,IAAI,IAAI,YAAY,CAAC;QAC9C,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,IAAI,IAAI,YAAY,CAAC;QAC9C,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE5B,MAAM,GAAG,GAA2B,EAAE,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACzC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACvD,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YAC9B,IAAI,OAAO,KAAK,KAAK,QAAQ;gBAAE,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QAClD,CAAC;QACD,IAAI,KAAK,CAAC,GAAG,EAAE,CAAC;YACd,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBACrD,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;YACnB,CAAC;QACH,CAAC;QACD,GAAG,CAAC,IAAI,GAAG,SAAS,CAAC;QACrB,GAAG,CAAC,SAAS,GAAG,eAAe,CAAC;QAEhC,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,EAAE;YAC/B,IAAI,EAAE,SAAS;YACf,IAAI,EAAE,IAAI,CAAC,WAAW;YACtB,IAAI,EAAE,IAAI,CAAC,WAAW;YACtB,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,GAAG;SACJ,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;YACvB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;YACvB,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;YAC/B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACxB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAC5B,CAAC;IAED,IAAI,GAAG;QACL,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;IACtB,CAAC;IAED,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,SAAS,CAAC;IACxB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,IAAY;QAChB,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACvB,CAAC;IAED;;;;;;;OAOG;IACH,KAAK;QACH,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACvC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;QACnB,CAAC;QAAC,MAAM,CAAC;YACP,6DAA6D;QAC/D,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,MAAM;QACJ,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO;QACxC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;QACpB,CAAC;QAAC,MAAM,CAAC;YACP,iBAAiB;QACnB,CAAC;IACH,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,IAAY;QAC/B,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC;YAAE,OAAO;QACnC,IAAI,IAAI,KAAK,IAAI,CAAC,WAAW,IAAI,IAAI,KAAK,IAAI,CAAC,WAAW;YAAE,OAAO;QACnE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QACxB,IAAI,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC9B,CAAC;QAAC,MAAM,CAAC;YACP,sCAAsC;QACxC,CAAC;IACH,CAAC;IAED,IAAI,CAAC,SAAyB,QAAQ;QACpC,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,CAAC;YACH,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,kBAAkB;QACpB,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI,CAAC,IAAI,EAAE,CAAC;QACZ,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACnB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACxB,IAAI,CAAC,kBAAkB,EAAE,CAAC;IAC5B,CAAC;IAEO,WAAW,CAAC,IAAY;QAC9B,MAAM,QAAQ,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,IAAI,QAAQ,KAAK,IAAI,CAAC,cAAc,EAAE,CAAC;gBACrC,IAAI,CAAC,cAAc,GAAG,QAAQ,CAAC;gBAC/B,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;IACH,CAAC;IAEO,iBAAiB,CAAC,OAAe;QACvC,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,CAAC,cAAc,GAAG,UAAU,CAAC,GAAG,EAAE;YACpC,KAAK,IAAI,CAAC,YAAY,EAAE,CAAC;QAC3B,CAAC,EAAE,OAAO,CAAC,CAAC;QACZ,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;IAC9B,CAAC;IAEO,KAAK,CAAC,YAAY;QACxB,IAAI,IAAI,CAAC,MAAM;YAAE,OAAO;QACxB,IAAI,CAAC;YACH,wEAAwE;YACxE,sEAAsE;YACtE,iDAAiD;YACjD,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;YAC9F,IAAI,IAAI,CAAC,MAAM;gBAAE,OAAO;YACxB,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,OAAO,IAAI,OAAO,KAAK,IAAI,CAAC,cAAc,EAAE,CAAC;gBACrE,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC;gBAC9B,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAC5B,CAAC;YACD,MAAM,SAAS,GAAG,IAAI,CAAC,YAAY,CACjC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,CAAC,CAC5E,CAAC;YACF,IAAI,SAAS,IAAI,SAAS,KAAK,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBACrD,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAC;gBAClC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YAChC,CAAC;YACD,IAAI,CAAC,cAAc,EAAE,CAAC;QACxB,CAAC;QAAC,MAAM,CAAC;YACP,4DAA4D;QAC9D,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,iBAAiB,CAAC,sBAAsB,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAEO,iBAAiB;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QAC3C,OAAO,GAAG,IAAI,GAAG,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;IACpD,CAAC;IAEO,cAAc;QACpB,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACtC,IAAI,IAAI,KAAK,IAAI,CAAC,qBAAqB;YAAE,OAAO;QAChD,IAAI,CAAC,qBAAqB,GAAG,IAAI,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IAChC,CAAC;IAEO,YAAY,CAAC,OAAe;QAClC,MAAM,UAAU,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAC5C,IAAI,UAAU;YAAE,OAAO,UAAU,CAAC;QAClC,OAAO,2BAA2B,CAAC,OAAO,CAAC,CAAC;IAC9C,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,IAAI,GAAG,GAAG,IAAI,CAAC,gBAAgB;YAAE,OAAO,IAAI,CAAC;QAC7C,MAAM,OAAO,GAAG,MAAM,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACnE,IAAI,CAAC,gBAAgB;YACnB,GAAG,GAAG,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC;QAC9E,OAAO,OAAO,CAAC;IACjB,CAAC;IAEO,gBAAgB;QACtB,IAAI,IAAI,CAAC,cAAc,KAAK,IAAI;YAAE,OAAO;QACzC,YAAY,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAClC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC;IAC7B,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ export interface StaticAsset {
2
+ body: Buffer;
3
+ contentType: string;
4
+ status: number;
5
+ }
6
+ export declare const resolveStaticAsset: (root: string, urlPath: string) => StaticAsset | null;
7
+ //# sourceMappingURL=static-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-resolver.d.ts","sourceRoot":"","sources":["../src/static-resolver.ts"],"names":[],"mappings":"AAsBA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAkBD,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,EAAE,SAAS,MAAM,KAAG,WAAW,GAAG,IAqBhF,CAAC"}
@@ -0,0 +1,64 @@
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import path from "node:path";
3
+ const MIME = {
4
+ ".html": "text/html; charset=utf-8",
5
+ ".js": "text/javascript; charset=utf-8",
6
+ ".mjs": "text/javascript; charset=utf-8",
7
+ ".css": "text/css; charset=utf-8",
8
+ ".svg": "image/svg+xml",
9
+ ".png": "image/png",
10
+ ".jpg": "image/jpeg",
11
+ ".jpeg": "image/jpeg",
12
+ ".gif": "image/gif",
13
+ ".webp": "image/webp",
14
+ ".ico": "image/x-icon",
15
+ ".json": "application/json; charset=utf-8",
16
+ ".woff": "font/woff",
17
+ ".woff2": "font/woff2",
18
+ ".ttf": "font/ttf",
19
+ ".map": "application/json; charset=utf-8",
20
+ };
21
+ const isContained = (root, resolved) => {
22
+ const relative = path.relative(root, resolved);
23
+ return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
24
+ };
25
+ const readFile = (target) => {
26
+ if (!existsSync(target))
27
+ return null;
28
+ if (!statSync(target).isFile())
29
+ return null;
30
+ const ext = path.extname(target).toLowerCase();
31
+ return {
32
+ body: readFileSync(target),
33
+ contentType: MIME[ext] ?? "application/octet-stream",
34
+ status: 200,
35
+ };
36
+ };
37
+ export const resolveStaticAsset = (root, urlPath) => {
38
+ const normalizedRoot = path.resolve(root);
39
+ let decoded;
40
+ try {
41
+ decoded = decodeURIComponent(urlPath.split("?", 1)[0] ?? "/");
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ const trimmed = decoded.replace(/^[/\\]+/, "");
47
+ const resolvedExact = path.resolve(normalizedRoot, trimmed);
48
+ if (isContained(normalizedRoot, resolvedExact)) {
49
+ const direct = readFile(resolvedExact);
50
+ if (direct)
51
+ return direct;
52
+ const indexed = readFile(path.join(resolvedExact, "index.html"));
53
+ if (indexed)
54
+ return indexed;
55
+ }
56
+ const hasFileExtension = /\.[a-z0-9]+$/i.test(trimmed);
57
+ if (hasFileExtension)
58
+ return null;
59
+ const fallback = readFile(path.join(normalizedRoot, "index.html"));
60
+ if (!fallback)
61
+ return null;
62
+ return { ...fallback, status: 200 };
63
+ };
64
+ //# sourceMappingURL=static-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-resolver.js","sourceRoot":"","sources":["../src/static-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { z } from "zod";
2
+ import type { clientToServerMessageSchema, serverToClientMessageSchema } from "./schemas.js";
3
+ export interface SpawnPtyInput {
4
+ cols?: number;
5
+ rows?: number;
6
+ shell?: string;
7
+ cwd?: string;
8
+ env?: Record<string, string>;
9
+ }
10
+ export type ClientToServerMessage = z.infer<typeof clientToServerMessageSchema>;
11
+ export type ServerToClientMessage = z.infer<typeof serverToClientMessageSchema>;
12
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,2BAA2B,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAE7F,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAChF,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export declare const formatWorkingDirectoryTitle: (cwd: string, home?: string) => string;
2
+ //# sourceMappingURL=format-working-directory-title.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-working-directory-title.d.ts","sourceRoot":"","sources":["../../src/utils/format-working-directory-title.ts"],"names":[],"mappings":"AAkBA,eAAO,MAAM,2BAA2B,GAAI,KAAK,MAAM,EAAE,aAA4B,KAAG,MAQvF,CAAC"}
@@ -0,0 +1,28 @@
1
+ import os from "node:os";
2
+ import path from "node:path";
3
+ import { TITLE_MAX_PATH_SEGMENTS, TITLE_TRUNCATION_PREFIX } from "../constants.js";
4
+ const HOME_PREFIX = "~";
5
+ const PATH_SEPARATOR = "/";
6
+ const CACHED_HOME_DIRECTORY = os.homedir();
7
+ const abbreviateHome = (cwd, home) => {
8
+ const normalizedCwd = path.resolve(cwd);
9
+ const normalizedHome = path.resolve(home);
10
+ if (normalizedCwd === normalizedHome)
11
+ return HOME_PREFIX;
12
+ if (normalizedCwd.startsWith(`${normalizedHome}${PATH_SEPARATOR}`)) {
13
+ return `${HOME_PREFIX}${normalizedCwd.slice(normalizedHome.length)}`;
14
+ }
15
+ return normalizedCwd;
16
+ };
17
+ export const formatWorkingDirectoryTitle = (cwd, home = CACHED_HOME_DIRECTORY) => {
18
+ if (!cwd)
19
+ return cwd;
20
+ const abbreviated = abbreviateHome(cwd, home);
21
+ const segments = abbreviated.split(PATH_SEPARATOR).filter(Boolean);
22
+ if (segments.length <= TITLE_MAX_PATH_SEGMENTS)
23
+ return abbreviated;
24
+ return `${TITLE_TRUNCATION_PREFIX}${PATH_SEPARATOR}${segments
25
+ .slice(-TITLE_MAX_PATH_SEGMENTS)
26
+ .join(PATH_SEPARATOR)}`;
27
+ };
28
+ //# sourceMappingURL=format-working-directory-title.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-working-directory-title.js","sourceRoot":"","sources":["../../src/utils/format-working-directory-title.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,uBAAuB,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAEnF,MAAM,WAAW,GAAG,GAAG,CAAC;AACxB,MAAM,cAAc,GAAG,GAAG,CAAC;AAC3B,MAAM,qBAAqB,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;AAE3C,MAAM,cAAc,GAAG,CAAC,GAAW,EAAE,IAAY,EAAU,EAAE;IAC3D,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACxC,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,aAAa,KAAK,cAAc;QAAE,OAAO,WAAW,CAAC;IACzD,IAAI,aAAa,CAAC,UAAU,CAAC,GAAG,cAAc,GAAG,cAAc,EAAE,CAAC,EAAE,CAAC;QACnE,OAAO,GAAG,WAAW,GAAG,aAAa,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;IACvE,CAAC;IACD,OAAO,aAAa,CAAC;AACvB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,2BAA2B,GAAG,CAAC,GAAW,EAAE,IAAI,GAAG,qBAAqB,EAAU,EAAE;IAC/F,IAAI,CAAC,GAAG;QAAE,OAAO,GAAG,CAAC;IACrB,MAAM,WAAW,GAAG,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IAC9C,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACnE,IAAI,QAAQ,CAAC,MAAM,IAAI,uBAAuB;QAAE,OAAO,WAAW,CAAC;IACnE,OAAO,GAAG,uBAAuB,GAAG,cAAc,GAAG,QAAQ;SAC1D,KAAK,CAAC,CAAC,uBAAuB,CAAC;SAC/B,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC;AAC5B,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const parseOsc7FromChunk: (data: string) => string | null;
2
+ //# sourceMappingURL=parse-osc7.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-osc7.d.ts","sourceRoot":"","sources":["../../src/utils/parse-osc7.ts"],"names":[],"mappings":"AAuBA,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,KAAG,MAAM,GAAG,IA6B1D,CAAC"}
@@ -0,0 +1,50 @@
1
+ // OSC 7 (Set Working Directory) format:
2
+ // ESC ] 7 ; file://HOST/PATH ST
3
+ // where ST is either BEL (0x07) or ESC \ (0x1b 0x5c).
4
+ // The path is URL-encoded (spaces = %20, etc.).
5
+ //
6
+ // This parser scans a chunk of PTY output for the last OSC 7 in that chunk,
7
+ // returning the decoded filesystem path (or null if none found).
8
+ // It is deliberately simple: we only care about the *most recent* OSC 7,
9
+ // and the sequences are short enough that they won't span huge buffers.
10
+ const OSC7_PREFIX = "\x1b]7;";
11
+ const BEL = "\x07";
12
+ const ST = "\x1b\\";
13
+ const extractPath = (url) => {
14
+ try {
15
+ const parsed = new URL(url);
16
+ return decodeURIComponent(parsed.pathname);
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ };
22
+ export const parseOsc7FromChunk = (data) => {
23
+ let searchFrom = 0;
24
+ let lastResult = null;
25
+ while (searchFrom < data.length) {
26
+ const start = data.indexOf(OSC7_PREFIX, searchFrom);
27
+ if (start === -1)
28
+ break;
29
+ const payloadStart = start + OSC7_PREFIX.length;
30
+ const belIndex = data.indexOf(BEL, payloadStart);
31
+ const stIndex = data.indexOf(ST, payloadStart);
32
+ let payloadEnd;
33
+ if (belIndex !== -1 && (stIndex === -1 || belIndex < stIndex)) {
34
+ payloadEnd = belIndex;
35
+ }
36
+ else if (stIndex !== -1) {
37
+ payloadEnd = stIndex;
38
+ }
39
+ else {
40
+ break;
41
+ }
42
+ const payload = data.slice(payloadStart, payloadEnd);
43
+ const path = extractPath(payload);
44
+ if (path)
45
+ lastResult = path;
46
+ searchFrom = payloadEnd + 1;
47
+ }
48
+ return lastResult;
49
+ };
50
+ //# sourceMappingURL=parse-osc7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse-osc7.js","sourceRoot":"","sources":["../../src/utils/parse-osc7.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,kCAAkC;AAClC,sDAAsD;AACtD,gDAAgD;AAChD,EAAE;AACF,4EAA4E;AAC5E,iEAAiE;AACjE,yEAAyE;AACzE,wEAAwE;AAExE,MAAM,WAAW,GAAG,SAAS,CAAC;AAC9B,MAAM,GAAG,GAAG,MAAM,CAAC;AACnB,MAAM,EAAE,GAAG,QAAQ,CAAC;AAEpB,MAAM,WAAW,GAAG,CAAC,GAAW,EAAiB,EAAE;IACjD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,OAAO,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,IAAY,EAAiB,EAAE;IAChE,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,UAAU,GAAkB,IAAI,CAAC;IAErC,OAAO,UAAU,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QACpD,IAAI,KAAK,KAAK,CAAC,CAAC;YAAE,MAAM;QAExB,MAAM,YAAY,GAAG,KAAK,GAAG,WAAW,CAAC,MAAM,CAAC;QAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,YAAY,CAAC,CAAC;QAE/C,IAAI,UAAkB,CAAC;QACvB,IAAI,QAAQ,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,CAAC,IAAI,QAAQ,GAAG,OAAO,CAAC,EAAE,CAAC;YAC9D,UAAU,GAAG,QAAQ,CAAC;QACxB,CAAC;aAAM,IAAI,OAAO,KAAK,CAAC,CAAC,EAAE,CAAC;YAC1B,UAAU,GAAG,OAAO,CAAC;QACvB,CAAC;aAAM,CAAC;YACN,MAAM;QACR,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;QACrD,MAAM,IAAI,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,IAAI;YAAE,UAAU,GAAG,IAAI,CAAC;QAE5B,UAAU,GAAG,UAAU,GAAG,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const resolveCwdForPid: (pid: number) => Promise<string | null>;
2
+ //# sourceMappingURL=resolve-cwd-for-pid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-cwd-for-pid.d.ts","sourceRoot":"","sources":["../../src/utils/resolve-cwd-for-pid.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,gBAAgB,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAuBzE,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { execFile } from "node:child_process";
2
+ import { readlink } from "node:fs/promises";
3
+ import { promisify } from "node:util";
4
+ import { CWD_RESOLVE_TIMEOUT_MS } from "../constants.js";
5
+ const execFileAsync = promisify(execFile);
6
+ export const resolveCwdForPid = async (pid) => {
7
+ if (!Number.isFinite(pid) || pid <= 0)
8
+ return null;
9
+ if (process.platform === "linux") {
10
+ try {
11
+ return await readlink(`/proc/${pid}/cwd`);
12
+ }
13
+ catch {
14
+ return null;
15
+ }
16
+ }
17
+ if (process.platform === "darwin") {
18
+ try {
19
+ const { stdout } = await execFileAsync("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], { timeout: CWD_RESOLVE_TIMEOUT_MS, windowsHide: true });
20
+ const cwdLine = stdout.split("\n").find((line) => line.startsWith("n"));
21
+ return cwdLine ? cwdLine.slice(1) : null;
22
+ }
23
+ catch {
24
+ return null;
25
+ }
26
+ }
27
+ return null;
28
+ };
29
+ //# sourceMappingURL=resolve-cwd-for-pid.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-cwd-for-pid.js","sourceRoot":"","sources":["../../src/utils/resolve-cwd-for-pid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAE1C,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,EAAE,GAAW,EAA0B,EAAE;IAC5E,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACnD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,OAAO,MAAM,QAAQ,CAAC,SAAS,GAAG,MAAM,CAAC,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CACpC,MAAM,EACN,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,CAAC,EAC7C,EAAE,OAAO,EAAE,sBAAsB,EAAE,WAAW,EAAE,IAAI,EAAE,CACvD,CAAC;YACF,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;YACxE,OAAO,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@monotykamary/localterm-server",
3
+ "version": "0.0.15",
4
+ "description": "PTY-backed HTTP/WebSocket server that powers localterm, built on Hono + node-pty.",
5
+ "keywords": [
6
+ "hono",
7
+ "localterm",
8
+ "node-pty",
9
+ "pty",
10
+ "terminal",
11
+ "websocket"
12
+ ],
13
+ "homepage": "https://github.com/aidenybai/localterm#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/aidenybai/localterm/issues"
16
+ },
17
+ "license": "MIT",
18
+ "author": "Aiden Bai",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/aidenybai/localterm.git",
22
+ "directory": "packages/server"
23
+ },
24
+ "files": [
25
+ "dist"
26
+ ],
27
+ "os": [
28
+ "darwin",
29
+ "linux"
30
+ ],
31
+ "type": "module",
32
+ "main": "./dist/index.js",
33
+ "module": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/index.d.ts",
38
+ "import": "./dist/index.js"
39
+ },
40
+ "./protocol": {
41
+ "types": "./dist/protocol.d.ts",
42
+ "import": "./dist/protocol.js"
43
+ }
44
+ },
45
+ "publishConfig": {
46
+ "access": "public"
47
+ },
48
+ "dependencies": {
49
+ "@hono/node-server": "^1.14.0",
50
+ "@hono/node-ws": "^1.1.1",
51
+ "hono": "^4.6.13",
52
+ "node-pty": "^1.0.0",
53
+ "zod": "^4.3.6"
54
+ },
55
+ "devDependencies": {
56
+ "@types/node": "^25.5.0",
57
+ "typescript": "^5.9.3",
58
+ "vite-plus": "^0.1.12"
59
+ },
60
+ "engines": {
61
+ "node": ">=22"
62
+ },
63
+ "scripts": {
64
+ "build": "tsc -p tsconfig.build.json",
65
+ "dev": "tsc -p tsconfig.build.json --watch",
66
+ "test": "vp test --run",
67
+ "typecheck": "tsc --noEmit"
68
+ }
69
+ }