@monotykamary/localterm 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 (103) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +66 -0
  3. package/bin/localterm.mjs +2 -0
  4. package/dist/commands/restart.d.ts +7 -0
  5. package/dist/commands/restart.d.ts.map +1 -0
  6. package/dist/commands/restart.js +43 -0
  7. package/dist/commands/restart.js.map +1 -0
  8. package/dist/commands/start.d.ts +9 -0
  9. package/dist/commands/start.d.ts.map +1 -0
  10. package/dist/commands/start.js +168 -0
  11. package/dist/commands/start.js.map +1 -0
  12. package/dist/commands/status.d.ts +2 -0
  13. package/dist/commands/status.d.ts.map +1 -0
  14. package/dist/commands/status.js +34 -0
  15. package/dist/commands/status.js.map +1 -0
  16. package/dist/commands/stop.d.ts +2 -0
  17. package/dist/commands/stop.d.ts.map +1 -0
  18. package/dist/commands/stop.js +48 -0
  19. package/dist/commands/stop.js.map +1 -0
  20. package/dist/constants.d.ts +26 -0
  21. package/dist/constants.d.ts.map +1 -0
  22. package/dist/constants.js +26 -0
  23. package/dist/constants.js.map +1 -0
  24. package/dist/errors.d.ts +101 -0
  25. package/dist/errors.d.ts.map +1 -0
  26. package/dist/errors.js +164 -0
  27. package/dist/errors.js.map +1 -0
  28. package/dist/index.d.ts +2 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +59 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/paths.d.ts +5 -0
  33. package/dist/paths.d.ts.map +1 -0
  34. package/dist/paths.js +7 -0
  35. package/dist/paths.js.map +1 -0
  36. package/dist/state.d.ts +8 -0
  37. package/dist/state.d.ts.map +1 -0
  38. package/dist/state.js +58 -0
  39. package/dist/state.js.map +1 -0
  40. package/dist/utils/build-daemon-args.d.ts +7 -0
  41. package/dist/utils/build-daemon-args.d.ts.map +1 -0
  42. package/dist/utils/build-daemon-args.js +7 -0
  43. package/dist/utils/build-daemon-args.js.map +1 -0
  44. package/dist/utils/cli-entry.d.ts +2 -0
  45. package/dist/utils/cli-entry.d.ts.map +1 -0
  46. package/dist/utils/cli-entry.js +5 -0
  47. package/dist/utils/cli-entry.js.map +1 -0
  48. package/dist/utils/parse-port-option.d.ts +2 -0
  49. package/dist/utils/parse-port-option.d.ts.map +1 -0
  50. package/dist/utils/parse-port-option.js +14 -0
  51. package/dist/utils/parse-port-option.js.map +1 -0
  52. package/dist/utils/poll-for-daemon-ready.d.ts +20 -0
  53. package/dist/utils/poll-for-daemon-ready.d.ts.map +1 -0
  54. package/dist/utils/poll-for-daemon-ready.js +20 -0
  55. package/dist/utils/poll-for-daemon-ready.js.map +1 -0
  56. package/dist/utils/read-package-version.d.ts +2 -0
  57. package/dist/utils/read-package-version.d.ts.map +1 -0
  58. package/dist/utils/read-package-version.js +12 -0
  59. package/dist/utils/read-package-version.js.map +1 -0
  60. package/dist/utils/report-cli-error.d.ts +3 -0
  61. package/dist/utils/report-cli-error.d.ts.map +1 -0
  62. package/dist/utils/report-cli-error.js +19 -0
  63. package/dist/utils/report-cli-error.js.map +1 -0
  64. package/dist/utils/run-start-preflight.d.ts +3 -0
  65. package/dist/utils/run-start-preflight.d.ts.map +1 -0
  66. package/dist/utils/run-start-preflight.js +16 -0
  67. package/dist/utils/run-start-preflight.js.map +1 -0
  68. package/dist/utils/sleep.d.ts +2 -0
  69. package/dist/utils/sleep.d.ts.map +1 -0
  70. package/dist/utils/sleep.js +2 -0
  71. package/dist/utils/sleep.js.map +1 -0
  72. package/dist/utils/spawn-daemon.d.ts +9 -0
  73. package/dist/utils/spawn-daemon.d.ts.map +1 -0
  74. package/dist/utils/spawn-daemon.js +13 -0
  75. package/dist/utils/spawn-daemon.js.map +1 -0
  76. package/dist/utils/verify-pid-is-localterm.d.ts +2 -0
  77. package/dist/utils/verify-pid-is-localterm.d.ts.map +1 -0
  78. package/dist/utils/verify-pid-is-localterm.js +37 -0
  79. package/dist/utils/verify-pid-is-localterm.js.map +1 -0
  80. package/package.json +67 -0
  81. package/terminal/assets/geist-mono-cyrillic-400-normal-BPBWmzPh.woff +0 -0
  82. package/terminal/assets/geist-mono-cyrillic-400-normal-Ce5q_31Z.woff2 +0 -0
  83. package/terminal/assets/geist-mono-cyrillic-500-normal-CJBLNVQT.woff2 +0 -0
  84. package/terminal/assets/geist-mono-cyrillic-500-normal-mNhfPmgl.woff +0 -0
  85. package/terminal/assets/geist-mono-cyrillic-600-normal-CGND36d7.woff2 +0 -0
  86. package/terminal/assets/geist-mono-cyrillic-600-normal-DrylrLu6.woff +0 -0
  87. package/terminal/assets/geist-mono-latin-400-normal-CoULgQGM.woff +0 -0
  88. package/terminal/assets/geist-mono-latin-400-normal-LC9RFr9I.woff2 +0 -0
  89. package/terminal/assets/geist-mono-latin-500-normal-D3o2eNa9.woff2 +0 -0
  90. package/terminal/assets/geist-mono-latin-500-normal-DOxI7kZ4.woff +0 -0
  91. package/terminal/assets/geist-mono-latin-600-normal-DQQBcVN0.woff2 +0 -0
  92. package/terminal/assets/geist-mono-latin-600-normal-DsVeri3b.woff +0 -0
  93. package/terminal/assets/geist-mono-latin-ext-400-normal-Cgks_Qgx.woff2 +0 -0
  94. package/terminal/assets/geist-mono-latin-ext-400-normal-CxNRRMGd.woff +0 -0
  95. package/terminal/assets/geist-mono-latin-ext-500-normal-CQcGuCNt.woff2 +0 -0
  96. package/terminal/assets/geist-mono-latin-ext-500-normal-diTenJ8L.woff +0 -0
  97. package/terminal/assets/geist-mono-latin-ext-600-normal-CJwYYto2.woff2 +0 -0
  98. package/terminal/assets/geist-mono-latin-ext-600-normal-EvIRCXgu.woff +0 -0
  99. package/terminal/assets/index-CL-BwH94.css +1 -0
  100. package/terminal/assets/index-D_Pz_NsG.js +240 -0
  101. package/terminal/assets/index-D_Pz_NsG.js.map +1 -0
  102. package/terminal/index.html +31 -0
  103. package/terminal/manifest.webmanifest +20 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aiden Bai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # localterm
2
+
3
+ [![version](https://img.shields.io/npm/v/@monotykamary/localterm?style=flat&colorA=000000&colorB=000000)](https://npmjs.com/package/@monotykamary/localterm)
4
+ [![downloads](https://img.shields.io/npm/dt/@monotykamary/localterm.svg?style=flat&colorA=000000&colorB=000000)](https://npmjs.com/package/@monotykamary/localterm)
5
+
6
+ Your terminal should just be a browser tab.
7
+
8
+ Run `npx @monotykamary/localterm@latest start` and every browser tab is one shell. Open a new tab to spawn another. Close the tab to kill it. That's the whole product.
9
+
10
+ ![demo](https://www.localterm.dev/demo.png)
11
+
12
+ ## Install
13
+
14
+ Run this command anywhere:
15
+
16
+ ```bash
17
+ npx @monotykamary/localterm@latest start
18
+ ```
19
+
20
+ This boots a local daemon and opens [`http://localterm.localhost:3417`](http://localterm.localhost:3417) in your browser. (`*.localhost` is reserved by [RFC 6761](https://datatracker.ietf.org/doc/html/rfc6761) and resolves to `127.0.0.1` in every modern browser, so no `/etc/hosts` edit needed.)
21
+
22
+ To install globally:
23
+
24
+ ```bash
25
+ npm install -g @monotykamary/localterm
26
+ localterm start
27
+ ```
28
+
29
+ ## Usage
30
+
31
+ The mental model is **shell = browser tab**:
32
+
33
+ - **New tab** → new shell
34
+ - **Close tab** → shell dies immediately
35
+ - **Reload tab** → fresh shell (the prior one is gone)
36
+
37
+ No session ids, no URL slugs, no reconnects. If you want a long-lived shell that survives reloads, run `tmux` _inside_ localterm.
38
+
39
+ ## CLI
40
+
41
+ ```bash
42
+ localterm start [-p 3417] [-H 127.0.0.1] [--no-open] # daemonizes by default
43
+ localterm stop
44
+ localterm status
45
+ localterm restart
46
+ ```
47
+
48
+ State lives in `~/.localterm/` (PID, port, server log at `~/.localterm/server.log`).
49
+
50
+ ## Security
51
+
52
+ - By default, binds loopback hosts only: `127.0.0.1`, `localhost`, `*.localhost`, `::1`, and enforces loopback `Host`/`Origin` headers to defeat DNS-rebinding attacks.
53
+ - Pass `-H 0.0.0.0` (or any non-loopback address) to expose the server on all network interfaces. Loopback header enforcement is disabled in this mode — only use on trusted networks (e.g. Tailscale, a home LAN behind a firewall).
54
+ - One PTY per WebSocket. Closing the tab kills the shell — no orphaned processes.
55
+
56
+ ## Resources & Contributing Back
57
+
58
+ Looking to contribute back? Check out the [Contributing Guide](https://github.com/millionco/localterm/blob/main/CONTRIBUTING.md) and [`AGENTS.md`](https://github.com/millionco/localterm/blob/main/AGENTS.md) for code style.
59
+
60
+ Find a bug? Head over to our [issue tracker](https://github.com/millionco/localterm/issues) and we'll do our best to help. We love pull requests, too!
61
+
62
+ [**→ Start contributing on GitHub**](https://github.com/millionco/localterm/blob/main/CONTRIBUTING.md)
63
+
64
+ ### License
65
+
66
+ localterm is MIT-licensed open-source software.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "../dist/index.js";
@@ -0,0 +1,7 @@
1
+ export interface RestartOptions {
2
+ port: number;
3
+ host: string;
4
+ open: boolean;
5
+ }
6
+ export declare const runRestart: (options: RestartOptions) => Promise<void>;
7
+ //# sourceMappingURL=restart.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restart.d.ts","sourceRoot":"","sources":["../../src/commands/restart.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;CACf;AAED,eAAO,MAAM,UAAU,GAAU,SAAS,cAAc,KAAG,OAAO,CAAC,IAAI,CAoCtE,CAAC"}
@@ -0,0 +1,43 @@
1
+ import { openSync } from "node:fs";
2
+ import kleur from "kleur";
3
+ import { DAEMON_PROBE_INTERVAL_MS, DAEMON_PROBE_MAX_WAIT_MS } from "../constants.js";
4
+ import { cliError, exitCodeForCliError } from "../errors.js";
5
+ import { ensureLogFile, isAlive, readPort } from "../state.js";
6
+ import { buildDaemonStartArgs } from "../utils/build-daemon-args.js";
7
+ import { pollForDaemonReady } from "../utils/poll-for-daemon-ready.js";
8
+ import { reportCliError } from "../utils/report-cli-error.js";
9
+ import { sleep } from "../utils/sleep.js";
10
+ import { spawnDaemon } from "../utils/spawn-daemon.js";
11
+ import { runStop } from "./stop.js";
12
+ export const runRestart = async (options) => {
13
+ await runStop();
14
+ const portBeforeSpawn = readPort();
15
+ const logPath = ensureLogFile();
16
+ const logFd = openSync(logPath, "a");
17
+ const { pid: childPid } = spawnDaemon({
18
+ args: buildDaemonStartArgs(options),
19
+ logFd,
20
+ });
21
+ if (childPid === undefined) {
22
+ const error = cliError.daemonSpawnFailed(process.execPath, logPath);
23
+ reportCliError(error);
24
+ process.exit(exitCodeForCliError(error));
25
+ }
26
+ const result = await pollForDaemonReady({
27
+ childPid,
28
+ initialPort: portBeforeSpawn,
29
+ intervalMs: DAEMON_PROBE_INTERVAL_MS,
30
+ maxWaitMs: DAEMON_PROBE_MAX_WAIT_MS,
31
+ logPath,
32
+ isAlive,
33
+ readPort,
34
+ sleep,
35
+ });
36
+ if (result.ok) {
37
+ console.log(kleur.green(`✔ restarted (pid ${childPid}, port ${result.port}, logs: ${logPath})`));
38
+ return;
39
+ }
40
+ reportCliError(result.error);
41
+ process.exit(exitCodeForCliError(result.error));
42
+ };
43
+ //# sourceMappingURL=restart.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"restart.js","sourceRoot":"","sources":["../../src/commands/restart.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AACrF,OAAO,EAAE,QAAQ,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,MAAM,CAAC,MAAM,UAAU,GAAG,KAAK,EAAE,OAAuB,EAAiB,EAAE;IACzE,MAAM,OAAO,EAAE,CAAC;IAChB,MAAM,eAAe,GAAG,QAAQ,EAAE,CAAC;IACnC,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IACrC,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,WAAW,CAAC;QACpC,IAAI,EAAE,oBAAoB,CAAC,OAAO,CAAC;QACnC,KAAK;KACN,CAAC,CAAC;IAEH,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,KAAK,GAAG,QAAQ,CAAC,iBAAiB,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QACpE,cAAc,CAAC,KAAK,CAAC,CAAC;QACtB,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC;QACtC,QAAQ;QACR,WAAW,EAAE,eAAe;QAC5B,UAAU,EAAE,wBAAwB;QACpC,SAAS,EAAE,wBAAwB;QACnC,OAAO;QACP,OAAO;QACP,QAAQ;QACR,KAAK;KACN,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;QACd,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,KAAK,CAAC,oBAAoB,QAAQ,UAAU,MAAM,CAAC,IAAI,WAAW,OAAO,GAAG,CAAC,CACpF,CAAC;QACF,OAAO;IACT,CAAC;IAED,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7B,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAClD,CAAC,CAAC"}
@@ -0,0 +1,9 @@
1
+ export interface StartOptions {
2
+ port: number;
3
+ host: string;
4
+ open: boolean;
5
+ foreground: boolean;
6
+ }
7
+ export declare const runStart: (options: StartOptions) => Promise<void>;
8
+ export declare const startDefaults: StartOptions;
9
+ //# sourceMappingURL=start.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAwCA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,OAAO,CAAC;CACrB;AAID,eAAO,MAAM,QAAQ,GAAU,SAAS,YAAY,KAAG,OAAO,CAAC,IAAI,CAMlE,CAAC;AA+IF,eAAO,MAAM,aAAa,EAAE,YAK3B,CAAC"}
@@ -0,0 +1,168 @@
1
+ import { existsSync, openSync } from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+ import { createServer, DEFAULT_HOST, DEFAULT_PORT } from "@monotykamary/localterm-server";
5
+ import kleur from "kleur";
6
+ import open from "open";
7
+ import { DAEMON_CHILD_ENV_FLAG, DAEMON_PROBE_INTERVAL_MS, DAEMON_PROBE_MAX_WAIT_MS, DAEMON_PROCESS_TITLE, EXIT_FAILURE, EXIT_OK, FORCE_EXIT_TIMEOUT_MS, getFriendlyUrl, STOP_COMMAND, } from "../constants.js";
8
+ import { cliError, exitCodeForCliError } from "../errors.js";
9
+ import { clearPid, ensureLogFile, isAlive, readPort, writePid } from "../state.js";
10
+ import { buildDaemonStartArgs } from "../utils/build-daemon-args.js";
11
+ import { pollForDaemonReady } from "../utils/poll-for-daemon-ready.js";
12
+ import { reportCliError } from "../utils/report-cli-error.js";
13
+ import { runStartPreflight } from "../utils/run-start-preflight.js";
14
+ import { sleep } from "../utils/sleep.js";
15
+ import { spawnDaemon } from "../utils/spawn-daemon.js";
16
+ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
17
+ const resolveStaticRoot = () => {
18
+ const candidates = [
19
+ path.resolve(moduleDir, "../../../../apps/terminal/dist"),
20
+ path.resolve(moduleDir, "../../terminal"),
21
+ path.resolve(moduleDir, "../terminal"),
22
+ ];
23
+ for (const candidate of candidates) {
24
+ if (existsSync(path.join(candidate, "index.html")))
25
+ return candidate;
26
+ }
27
+ return null;
28
+ };
29
+ const isRunningAsDaemonChild = () => process.env[DAEMON_CHILD_ENV_FLAG] === "1";
30
+ export const runStart = async (options) => {
31
+ if (options.foreground || isRunningAsDaemonChild()) {
32
+ await runStartInForeground(options);
33
+ return;
34
+ }
35
+ await runStartAsDaemon(options);
36
+ };
37
+ const runStartAsDaemon = async (options) => {
38
+ const preflightError = runStartPreflight();
39
+ if (preflightError !== null) {
40
+ reportCliError(preflightError);
41
+ process.exit(exitCodeForCliError(preflightError));
42
+ }
43
+ const portBeforeSpawn = readPort();
44
+ const logPath = ensureLogFile();
45
+ const logFd = openSync(logPath, "a");
46
+ const { pid: childPid } = spawnDaemon({
47
+ args: buildDaemonStartArgs(options),
48
+ logFd,
49
+ });
50
+ if (childPid === undefined) {
51
+ const error = cliError.daemonSpawnFailed(process.execPath, logPath);
52
+ reportCliError(error);
53
+ process.exit(exitCodeForCliError(error));
54
+ }
55
+ const result = await pollForDaemonReady({
56
+ childPid,
57
+ initialPort: portBeforeSpawn,
58
+ intervalMs: DAEMON_PROBE_INTERVAL_MS,
59
+ maxWaitMs: DAEMON_PROBE_MAX_WAIT_MS,
60
+ logPath,
61
+ isAlive,
62
+ readPort,
63
+ sleep,
64
+ });
65
+ if (result.ok) {
66
+ printDaemonStartedBanner(result.port);
67
+ if (options.open)
68
+ await openInBrowser(getFriendlyUrl(result.port));
69
+ return;
70
+ }
71
+ if (result.error.kind === "daemon-ready-timeout" && isAlive(childPid)) {
72
+ const finalPort = readPort();
73
+ if (finalPort !== null && finalPort !== portBeforeSpawn) {
74
+ printDaemonStartedBanner(finalPort);
75
+ if (options.open)
76
+ await openInBrowser(getFriendlyUrl(finalPort));
77
+ return;
78
+ }
79
+ }
80
+ reportCliError(result.error);
81
+ process.exit(exitCodeForCliError(result.error));
82
+ };
83
+ const printDaemonStartedBanner = (port) => {
84
+ console.log(`${kleur.green("✔")} running at ${kleur.cyan(getFriendlyUrl(port))}`);
85
+ console.log(` stop with ${kleur.bold(STOP_COMMAND)}`);
86
+ };
87
+ const openInBrowser = async (url) => {
88
+ try {
89
+ await open(url);
90
+ }
91
+ catch {
92
+ /* headless environments (CI, ssh) have no browser to open; not fatal */
93
+ }
94
+ };
95
+ const runStartInForeground = async (options) => {
96
+ const preflightError = runStartPreflight();
97
+ if (preflightError !== null) {
98
+ reportCliError(preflightError);
99
+ process.exit(exitCodeForCliError(preflightError));
100
+ }
101
+ process.title = DAEMON_PROCESS_TITLE;
102
+ const staticRoot = resolveStaticRoot();
103
+ if (!staticRoot) {
104
+ console.log(kleur.yellow("warning: terminal bundle not found. run 'pnpm build' first or only the API will be served."));
105
+ }
106
+ let server;
107
+ try {
108
+ server = await createServer({
109
+ port: options.port,
110
+ host: options.host,
111
+ staticRoot,
112
+ });
113
+ }
114
+ catch (caughtError) {
115
+ const startError = cliError.serverStartFailed(caughtError instanceof Error ? caughtError : new Error(String(caughtError)));
116
+ reportCliError(startError);
117
+ process.exit(exitCodeForCliError(startError));
118
+ }
119
+ writePid(process.pid, server.port);
120
+ const namedUrl = getFriendlyUrl(server.port);
121
+ if (isRunningAsDaemonChild()) {
122
+ console.log(`${kleur.green("✔")} daemon listening on ${namedUrl} (pid ${process.pid})`);
123
+ }
124
+ else {
125
+ console.log(`${kleur.green("✔")} running at ${kleur.cyan(namedUrl)}`);
126
+ console.log(` press ${kleur.bold("Ctrl+C")} to stop`);
127
+ }
128
+ if (options.open && !isRunningAsDaemonChild())
129
+ await openInBrowser(namedUrl);
130
+ let shuttingDown = false;
131
+ const shutdown = async (signal) => {
132
+ if (shuttingDown) {
133
+ console.log(kleur.red("force exit"));
134
+ clearPid();
135
+ process.exit(EXIT_FAILURE);
136
+ }
137
+ shuttingDown = true;
138
+ console.log(`\n${kleur.dim(`received ${signal}, shutting down…`)}`);
139
+ const forceExit = setTimeout(() => {
140
+ console.log(kleur.red("forcing exit (server.stop took too long)"));
141
+ clearPid();
142
+ process.exit(EXIT_FAILURE);
143
+ }, FORCE_EXIT_TIMEOUT_MS);
144
+ forceExit.unref();
145
+ try {
146
+ await server.stop();
147
+ }
148
+ catch (error) {
149
+ const message = error instanceof Error ? error.message : String(error);
150
+ console.log(kleur.red(`stop error: ${message}`));
151
+ }
152
+ finally {
153
+ clearTimeout(forceExit);
154
+ clearPid();
155
+ process.exit(EXIT_OK);
156
+ }
157
+ };
158
+ process.on("SIGINT", () => void shutdown("SIGINT"));
159
+ process.on("SIGTERM", () => void shutdown("SIGTERM"));
160
+ process.on("SIGHUP", () => void shutdown("SIGHUP"));
161
+ };
162
+ export const startDefaults = {
163
+ port: DEFAULT_PORT,
164
+ host: DEFAULT_HOST,
165
+ open: false,
166
+ foreground: false,
167
+ };
168
+ //# sourceMappingURL=start.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare const runStatus: () => Promise<void>;
2
+ //# sourceMappingURL=status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,SAAS,QAAa,OAAO,CAAC,IAAI,CAgC9C,CAAC"}
@@ -0,0 +1,34 @@
1
+ import kleur from "kleur";
2
+ import { healthSchema } from "@monotykamary/localterm-server";
3
+ import { getFriendlyUrl } from "../constants.js";
4
+ import { cliError } from "../errors.js";
5
+ import { isAlive, readPid, readPort } from "../state.js";
6
+ import { reportCliError } from "../utils/report-cli-error.js";
7
+ export const runStatus = async () => {
8
+ const pid = readPid();
9
+ const port = readPort();
10
+ if (!pid || !port) {
11
+ console.log(kleur.dim("localterm is not running."));
12
+ return;
13
+ }
14
+ if (!isAlive(pid)) {
15
+ console.log(kleur.yellow(`pid ${pid} is gone (stale state). run 'localterm start'.`));
16
+ return;
17
+ }
18
+ try {
19
+ const response = await fetch(`http://127.0.0.1:${port}/api/health`);
20
+ if (!response.ok)
21
+ throw new Error(`health check failed: ${response.status}`);
22
+ const health = healthSchema.parse(await response.json());
23
+ console.log(kleur.green("● running"));
24
+ console.log(` pid: ${pid}`);
25
+ console.log(` port: ${port}`);
26
+ console.log(` url: ${kleur.cyan(getFriendlyUrl(port))}`);
27
+ console.log(` raw: ${kleur.dim(`http://127.0.0.1:${port}`)}`);
28
+ console.log(` sessions: ${health.sessions}`);
29
+ }
30
+ catch (error) {
31
+ reportCliError(cliError.healthCheckFailed(pid, port, error instanceof Error ? error : new Error(String(error))));
32
+ }
33
+ };
34
+ //# sourceMappingURL=status.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status.js","sourceRoot":"","sources":["../../src/commands/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAE9D,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,IAAmB,EAAE;IACjD,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,MAAM,IAAI,GAAG,QAAQ,EAAE,CAAC;IAExB,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC,CAAC;QACpD,OAAO;IACT,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,GAAG,gDAAgD,CAAC,CAAC,CAAC;QACtF,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,oBAAoB,IAAI,aAAa,CAAC,CAAC;QACpE,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7E,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;QACzD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,eAAe,GAAG,EAAE,CAAC,CAAC;QAClC,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,CAAC,GAAG,CAAC,oBAAoB,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;QACpE,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IAChD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,cAAc,CACZ,QAAQ,CAAC,iBAAiB,CACxB,GAAG,EACH,IAAI,EACJ,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAC1D,CACF,CAAC;IACJ,CAAC;AACH,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare const runStop: () => Promise<void>;
2
+ //# sourceMappingURL=stop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop.d.ts","sourceRoot":"","sources":["../../src/commands/stop.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,OAAO,QAAa,OAAO,CAAC,IAAI,CA0C5C,CAAC"}
@@ -0,0 +1,48 @@
1
+ import kleur from "kleur";
2
+ import { STOP_MAX_WAIT_MS, STOP_POLL_INTERVAL_MS } from "../constants.js";
3
+ import { cliError } from "../errors.js";
4
+ import { clearPid, isAlive, readPid } from "../state.js";
5
+ import { reportCliError } from "../utils/report-cli-error.js";
6
+ import { sleep } from "../utils/sleep.js";
7
+ import { verifyPidIsLocalterm } from "../utils/verify-pid-is-localterm.js";
8
+ export const runStop = async () => {
9
+ const pid = readPid();
10
+ if (!pid) {
11
+ console.log(kleur.dim("localterm is not running."));
12
+ return;
13
+ }
14
+ if (!isAlive(pid)) {
15
+ clearPid();
16
+ console.log(kleur.dim("stale pid file removed."));
17
+ return;
18
+ }
19
+ const isOurDaemon = await verifyPidIsLocalterm(pid);
20
+ if (!isOurDaemon) {
21
+ reportCliError(cliError.pidNotOurs(pid));
22
+ clearPid();
23
+ return;
24
+ }
25
+ try {
26
+ process.kill(pid, "SIGTERM");
27
+ }
28
+ catch (error) {
29
+ reportCliError(cliError.signalFailed(pid, error instanceof Error ? error : new Error(String(error))));
30
+ return;
31
+ }
32
+ let waited = 0;
33
+ while (isAlive(pid) && waited < STOP_MAX_WAIT_MS) {
34
+ await sleep(STOP_POLL_INTERVAL_MS);
35
+ waited += STOP_POLL_INTERVAL_MS;
36
+ }
37
+ if (isAlive(pid)) {
38
+ try {
39
+ process.kill(pid, "SIGKILL");
40
+ }
41
+ catch {
42
+ /* process exited between SIGTERM and SIGKILL */
43
+ }
44
+ }
45
+ clearPid();
46
+ console.log(kleur.green(`✔ stopped pid ${pid}`));
47
+ };
48
+ //# sourceMappingURL=stop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop.js","sourceRoot":"","sources":["../../src/commands/stop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAE3E,MAAM,CAAC,MAAM,OAAO,GAAG,KAAK,IAAmB,EAAE;IAC/C,MAAM,GAAG,GAAG,OAAO,EAAE,CAAC;IACtB,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC,CAAC;QACpD,OAAO;IACT,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QAClB,QAAQ,EAAE,CAAC;QACX,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC,CAAC;QAClD,OAAO;IACT,CAAC;IACD,MAAM,WAAW,GAAG,MAAM,oBAAoB,CAAC,GAAG,CAAC,CAAC;IACpD,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,cAAc,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;QACzC,QAAQ,EAAE,CAAC;QACX,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,cAAc,CACZ,QAAQ,CAAC,YAAY,CAAC,GAAG,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CACtF,CAAC;QACF,OAAO;IACT,CAAC;IAED,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,MAAM,GAAG,gBAAgB,EAAE,CAAC;QACjD,MAAM,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACnC,MAAM,IAAI,qBAAqB,CAAC;IAClC,CAAC;IAED,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACjB,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,gDAAgD;QAClD,CAAC;IACH,CAAC;IACD,QAAQ,EAAE,CAAC;IACX,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,iBAAiB,GAAG,EAAE,CAAC,CAAC,CAAC;AACnD,CAAC,CAAC"}
@@ -0,0 +1,26 @@
1
+ export declare const FORCE_EXIT_TIMEOUT_MS = 3000;
2
+ export declare const STOP_POLL_INTERVAL_MS = 100;
3
+ export declare const STOP_MAX_WAIT_MS = 5000;
4
+ export declare const DAEMON_PROBE_INTERVAL_MS = 100;
5
+ export declare const DAEMON_PROBE_MAX_WAIT_MS = 5000;
6
+ export declare const VERIFY_PID_TIMEOUT_MS = 1000;
7
+ export declare const MIN_TCP_PORT = 0;
8
+ export declare const MAX_TCP_PORT = 65535;
9
+ export declare const FRIENDLY_HOSTNAME = "localterm.localhost";
10
+ export declare const STOP_COMMAND = "npx @monotykamary/localterm@latest stop";
11
+ export declare const DAEMON_CHILD_ENV_FLAG = "LOCALTERM_DAEMON_CHILD";
12
+ /**
13
+ * Distinctive process name set on the daemon at startup (via `process.title`).
14
+ * `localterm stop` verifies the recorded pid's kernel-reported comm matches
15
+ * this exact string before sending SIGTERM, so we never signal an unrelated
16
+ * process that happens to live at the recycled pid.
17
+ *
18
+ * Length stays under 15 chars so Linux's PR_SET_NAME (limited to TASK_COMM_LEN
19
+ * = 16 including the null) doesn't truncate the value differently than macOS.
20
+ */
21
+ export declare const DAEMON_PROCESS_TITLE = "localtermd";
22
+ export declare const EXIT_OK = 0;
23
+ export declare const EXIT_FAILURE = 1;
24
+ export declare const EXIT_USAGE_ERROR = 2;
25
+ export declare const getFriendlyUrl: (port: number) => string;
26
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAC1C,eAAO,MAAM,qBAAqB,MAAM,CAAC;AACzC,eAAO,MAAM,gBAAgB,OAAO,CAAC;AACrC,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,wBAAwB,OAAO,CAAC;AAC7C,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C,eAAO,MAAM,YAAY,IAAI,CAAC;AAC9B,eAAO,MAAM,YAAY,QAAQ,CAAC;AAElC,eAAO,MAAM,iBAAiB,wBAAwB,CAAC;AACvD,eAAO,MAAM,YAAY,4CAA4C,CAAC;AACtE,eAAO,MAAM,qBAAqB,2BAA2B,CAAC;AAC9D;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAEjD,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,YAAY,IAAI,CAAC;AAC9B,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAElC,eAAO,MAAM,cAAc,GAAI,MAAM,MAAM,KAAG,MAA+C,CAAC"}
@@ -0,0 +1,26 @@
1
+ export const FORCE_EXIT_TIMEOUT_MS = 3000;
2
+ export const STOP_POLL_INTERVAL_MS = 100;
3
+ export const STOP_MAX_WAIT_MS = 5000;
4
+ export const DAEMON_PROBE_INTERVAL_MS = 100;
5
+ export const DAEMON_PROBE_MAX_WAIT_MS = 5000;
6
+ export const VERIFY_PID_TIMEOUT_MS = 1000;
7
+ export const MIN_TCP_PORT = 0;
8
+ export const MAX_TCP_PORT = 65535;
9
+ export const FRIENDLY_HOSTNAME = "localterm.localhost";
10
+ export const STOP_COMMAND = "npx @monotykamary/localterm@latest stop";
11
+ export const DAEMON_CHILD_ENV_FLAG = "LOCALTERM_DAEMON_CHILD";
12
+ /**
13
+ * Distinctive process name set on the daemon at startup (via `process.title`).
14
+ * `localterm stop` verifies the recorded pid's kernel-reported comm matches
15
+ * this exact string before sending SIGTERM, so we never signal an unrelated
16
+ * process that happens to live at the recycled pid.
17
+ *
18
+ * Length stays under 15 chars so Linux's PR_SET_NAME (limited to TASK_COMM_LEN
19
+ * = 16 including the null) doesn't truncate the value differently than macOS.
20
+ */
21
+ export const DAEMON_PROCESS_TITLE = "localtermd";
22
+ export const EXIT_OK = 0;
23
+ export const EXIT_FAILURE = 1;
24
+ export const EXIT_USAGE_ERROR = 2;
25
+ export const getFriendlyUrl = (port) => `http://${FRIENDLY_HOSTNAME}:${port}`;
26
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAC1C,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAC;AACzC,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AACrC,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAC5C,MAAM,CAAC,MAAM,wBAAwB,GAAG,IAAI,CAAC;AAC7C,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAE1C,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC;AAC9B,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAC;AAElC,MAAM,CAAC,MAAM,iBAAiB,GAAG,qBAAqB,CAAC;AACvD,MAAM,CAAC,MAAM,YAAY,GAAG,yCAAyC,CAAC;AACtE,MAAM,CAAC,MAAM,qBAAqB,GAAG,wBAAwB,CAAC;AAC9D;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,YAAY,CAAC;AAEjD,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAC;AACzB,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC;AAC9B,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAElC,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,UAAU,iBAAiB,IAAI,IAAI,EAAE,CAAC"}
@@ -0,0 +1,101 @@
1
+ interface InvalidPortError {
2
+ kind: "invalid-port";
3
+ code: "E_LT_CLI_INVALID_PORT";
4
+ severity: "error";
5
+ raw: string;
6
+ reason: string;
7
+ }
8
+ interface InvalidHostError {
9
+ kind: "invalid-host";
10
+ code: "E_LT_CLI_INVALID_HOST";
11
+ severity: "error";
12
+ host: string;
13
+ }
14
+ interface AlreadyRunningError {
15
+ kind: "already-running";
16
+ code: "E_LT_CLI_ALREADY_RUNNING";
17
+ severity: "warning";
18
+ pid: number;
19
+ port: number;
20
+ }
21
+ interface StalePortFileError {
22
+ kind: "stale-port-file";
23
+ code: "E_LT_CLI_STALE_PORT_FILE";
24
+ severity: "warning";
25
+ pid: number;
26
+ }
27
+ interface DaemonSpawnFailedError {
28
+ kind: "daemon-spawn-failed";
29
+ code: "E_LT_CLI_DAEMON_SPAWN_FAILED";
30
+ severity: "error";
31
+ execPath: string;
32
+ logPath: string;
33
+ }
34
+ interface DaemonDiedError {
35
+ kind: "daemon-died";
36
+ code: "E_LT_CLI_DAEMON_DIED";
37
+ severity: "error";
38
+ pid: number;
39
+ logPath: string;
40
+ }
41
+ interface DaemonReadyTimeoutError {
42
+ kind: "daemon-ready-timeout";
43
+ code: "E_LT_CLI_DAEMON_READY_TIMEOUT";
44
+ severity: "warning";
45
+ pid: number;
46
+ waitedMs: number;
47
+ logPath: string;
48
+ }
49
+ interface ServerStartFailedError {
50
+ kind: "server-start-failed";
51
+ code: "E_LT_CLI_SERVER_START_FAILED";
52
+ severity: "error";
53
+ cause: Error;
54
+ }
55
+ interface PidNotOursError {
56
+ kind: "pid-not-ours";
57
+ code: "E_LT_CLI_PID_NOT_OURS";
58
+ severity: "warning";
59
+ pid: number;
60
+ }
61
+ interface SignalFailedError {
62
+ kind: "signal-failed";
63
+ code: "E_LT_CLI_SIGNAL_FAILED";
64
+ severity: "error";
65
+ pid: number;
66
+ cause: Error;
67
+ }
68
+ interface HealthCheckFailedError {
69
+ kind: "health-check-failed";
70
+ code: "E_LT_CLI_HEALTH_CHECK_FAILED";
71
+ severity: "warning";
72
+ pid: number;
73
+ port: number;
74
+ cause: Error;
75
+ }
76
+ export type CliError = InvalidPortError | InvalidHostError | AlreadyRunningError | StalePortFileError | DaemonSpawnFailedError | DaemonDiedError | DaemonReadyTimeoutError | ServerStartFailedError | PidNotOursError | SignalFailedError | HealthCheckFailedError;
77
+ export type CliErrorCode = CliError["code"];
78
+ export type CliErrorKind = CliError["kind"];
79
+ export declare const cliError: {
80
+ invalidPort: (raw: string, reason: string) => InvalidPortError;
81
+ invalidHost: (host: string) => InvalidHostError;
82
+ alreadyRunning: (pid: number, port: number) => AlreadyRunningError;
83
+ stalePortFile: (pid: number) => StalePortFileError;
84
+ daemonSpawnFailed: (execPath: string, logPath: string) => DaemonSpawnFailedError;
85
+ daemonDied: (pid: number, logPath: string) => DaemonDiedError;
86
+ daemonReadyTimeout: (pid: number, waitedMs: number, logPath: string) => DaemonReadyTimeoutError;
87
+ serverStartFailed: (cause: Error) => ServerStartFailedError;
88
+ pidNotOurs: (pid: number) => PidNotOursError;
89
+ signalFailed: (pid: number, cause: Error) => SignalFailedError;
90
+ healthCheckFailed: (pid: number, port: number, cause: Error) => HealthCheckFailedError;
91
+ };
92
+ export declare const formatCliError: (error: CliError) => string;
93
+ export declare const hintForCliError: (error: CliError) => string | null;
94
+ export declare const exitCodeForCliError: (error: CliError) => number;
95
+ export declare class CliErrorException extends Error {
96
+ readonly error: CliError;
97
+ constructor(error: CliError);
98
+ }
99
+ export declare const isCliErrorException: (value: unknown) => value is CliErrorException;
100
+ export {};
101
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAQA,UAAU,gBAAgB;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,uBAAuB,CAAC;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,gBAAgB;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,uBAAuB,CAAC;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,mBAAmB;IAC3B,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,0BAA0B,CAAC;IACjC,QAAQ,EAAE,SAAS,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,kBAAkB;IAC1B,IAAI,EAAE,iBAAiB,CAAC;IACxB,IAAI,EAAE,0BAA0B,CAAC;IACjC,QAAQ,EAAE,SAAS,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,UAAU,sBAAsB;IAC9B,IAAI,EAAE,qBAAqB,CAAC;IAC5B,IAAI,EAAE,8BAA8B,CAAC;IACrC,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,eAAe;IACvB,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,EAAE,sBAAsB,CAAC;IAC7B,QAAQ,EAAE,OAAO,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,uBAAuB;IAC/B,IAAI,EAAE,sBAAsB,CAAC;IAC7B,IAAI,EAAE,+BAA+B,CAAC;IACtC,QAAQ,EAAE,SAAS,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,sBAAsB;IAC9B,IAAI,EAAE,qBAAqB,CAAC;IAC5B,IAAI,EAAE,8BAA8B,CAAC;IACrC,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,CAAC;CACd;AAED,UAAU,eAAe;IACvB,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,uBAAuB,CAAC;IAC9B,QAAQ,EAAE,SAAS,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,UAAU,iBAAiB;IACzB,IAAI,EAAE,eAAe,CAAC;IACtB,IAAI,EAAE,wBAAwB,CAAC;IAC/B,QAAQ,EAAE,OAAO,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,KAAK,CAAC;CACd;AAED,UAAU,sBAAsB;IAC9B,IAAI,EAAE,qBAAqB,CAAC;IAC5B,IAAI,EAAE,8BAA8B,CAAC;IACrC,QAAQ,EAAE,SAAS,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,KAAK,CAAC;CACd;AAED,MAAM,MAAM,QAAQ,GAChB,gBAAgB,GAChB,gBAAgB,GAChB,mBAAmB,GACnB,kBAAkB,GAClB,sBAAsB,GACtB,eAAe,GACf,uBAAuB,GACvB,sBAAsB,GACtB,eAAe,GACf,iBAAiB,GACjB,sBAAsB,CAAC;AAE3B,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC5C,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;AAE5C,eAAO,MAAM,QAAQ;uBACA,MAAM,UAAU,MAAM,KAAG,gBAAgB;wBAOxC,MAAM,KAAG,gBAAgB;0BAMvB,MAAM,QAAQ,MAAM,KAAG,mBAAmB;yBAO3C,MAAM,KAAG,kBAAkB;kCAMlB,MAAM,WAAW,MAAM,KAAG,sBAAsB;sBAO5D,MAAM,WAAW,MAAM,KAAG,eAAe;8BAQpD,MAAM,YACD,MAAM,WACP,MAAM,KACd,uBAAuB;+BAQC,KAAK,KAAG,sBAAsB;sBAMvC,MAAM,KAAG,eAAe;wBAMtB,MAAM,SAAS,KAAK,KAAG,iBAAiB;6BAOnC,MAAM,QAAQ,MAAM,SAAS,KAAK,KAAG,sBAAsB;CAQrF,CAAC;AAMF,eAAO,MAAM,cAAc,GAAI,OAAO,QAAQ,KAAG,MA2BhD,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,OAAO,QAAQ,KAAG,MAAM,GAAG,IAsB1D,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,OAAO,QAAQ,KAAG,MAmBrD,CAAC;AAEF,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;gBACb,KAAK,EAAE,QAAQ;CAO5B;AAED,eAAO,MAAM,mBAAmB,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,iBAC1B,CAAC"}