@monotykamary/localterm 1.6.0 → 1.8.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/README.md CHANGED
@@ -49,8 +49,8 @@ State lives in `~/.localterm/` (PID, port, server log at `~/.localterm/server.lo
49
49
 
50
50
  ## Security
51
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).
52
+ - By default, binds loopback (`127.0.0.1`) and enforces loopback `Host`/`Origin` headers to defeat DNS-rebinding and cross-origin attacks.
53
+ - Pass `-H 0.0.0.0` (or any non-loopback address) to expose the server on all network interfaces. In this mode, `Host`/`Origin` must be from a private network (RFC 1918, CGNAT/Tailscale `100.64.127.x`, link-local, `*.localhost`) and WebSocket source IPs are filtered to private ranges — only use on trusted networks.
54
54
  - One PTY per WebSocket. Closing the tab kills the shell — no orphaned processes.
55
55
 
56
56
  ## Resources & Contributing Back
@@ -4,7 +4,7 @@ export declare const STOP_MAX_WAIT_MS = 5000;
4
4
  export declare const DAEMON_PROBE_INTERVAL_MS = 100;
5
5
  export declare const DAEMON_PROBE_MAX_WAIT_MS = 5000;
6
6
  export declare const VERIFY_PID_TIMEOUT_MS = 1000;
7
- export declare const MIN_TCP_PORT = 0;
7
+ export declare const MIN_TCP_PORT = 1;
8
8
  export declare const MAX_TCP_PORT = 65535;
9
9
  export declare const FRIENDLY_HOSTNAME = "localterm.localhost";
10
10
  export declare const STOP_COMMAND = "npx @monotykamary/localterm@latest stop";
package/dist/constants.js CHANGED
@@ -4,7 +4,7 @@ export const STOP_MAX_WAIT_MS = 5000;
4
4
  export const DAEMON_PROBE_INTERVAL_MS = 100;
5
5
  export const DAEMON_PROBE_MAX_WAIT_MS = 5000;
6
6
  export const VERIFY_PID_TIMEOUT_MS = 1000;
7
- export const MIN_TCP_PORT = 0;
7
+ export const MIN_TCP_PORT = 1;
8
8
  export const MAX_TCP_PORT = 65535;
9
9
  export const FRIENDLY_HOSTNAME = "localterm.localhost";
10
10
  export const STOP_COMMAND = "npx @monotykamary/localterm@latest stop";
@@ -15,6 +15,7 @@ export interface DaemonProbeOptions {
15
15
  isAlive: (pid: number) => boolean;
16
16
  readPort: () => number | null;
17
17
  sleep: (durationMs: number) => Promise<void>;
18
+ probeHealth?: (port: number) => Promise<boolean>;
18
19
  }
19
20
  export declare const pollForDaemonReady: (options: DaemonProbeOptions) => Promise<DaemonReadyResult>;
20
21
  //# sourceMappingURL=poll-for-daemon-ready.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"poll-for-daemon-ready.d.ts","sourceRoot":"","sources":["../../src/utils/poll-for-daemon-ready.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAY,MAAM,cAAc,CAAC;AAEvD,MAAM,MAAM,iBAAiB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,CAAC;AAE5F,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC,QAAQ,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAC9B,KAAK,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9C;AAED,eAAO,MAAM,kBAAkB,GAC7B,SAAS,kBAAkB,KAC1B,OAAO,CAAC,iBAAiB,CAiB3B,CAAC"}
1
+ {"version":3,"file":"poll-for-daemon-ready.d.ts","sourceRoot":"","sources":["../../src/utils/poll-for-daemon-ready.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAY,MAAM,cAAc,CAAC;AAEvD,MAAM,MAAM,iBAAiB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,CAAC;AAE5F,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IAClC,QAAQ,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAC9B,KAAK,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAClD;AAaD,eAAO,MAAM,kBAAkB,GAC7B,SAAS,kBAAkB,KAC1B,OAAO,CAAC,iBAAiB,CAsB3B,CAAC"}
@@ -1,5 +1,17 @@
1
1
  import { cliError } from "../errors.js";
2
+ const defaultProbeHealth = async (port) => {
3
+ try {
4
+ const response = await fetch(`http://127.0.0.1:${port}/api/health`, {
5
+ signal: AbortSignal.timeout(2000),
6
+ });
7
+ return response.ok;
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ };
2
13
  export const pollForDaemonReady = async (options) => {
14
+ const probeHealth = options.probeHealth ?? defaultProbeHealth;
3
15
  let waited = 0;
4
16
  while (waited < options.maxWaitMs) {
5
17
  await options.sleep(options.intervalMs);
@@ -11,6 +23,11 @@ export const pollForDaemonReady = async (options) => {
11
23
  if (observedPort !== null && observedPort !== options.initialPort) {
12
24
  return { ok: true, port: observedPort };
13
25
  }
26
+ if (observedPort !== null) {
27
+ const healthy = await probeHealth(observedPort);
28
+ if (healthy)
29
+ return { ok: true, port: observedPort };
30
+ }
14
31
  }
15
32
  return {
16
33
  ok: false,
@@ -1 +1 @@
1
- {"version":3,"file":"poll-for-daemon-ready.js","sourceRoot":"","sources":["../../src/utils/poll-for-daemon-ready.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,QAAQ,EAAE,MAAM,cAAc,CAAC;AAevD,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,OAA2B,EACC,EAAE;IAC9B,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,OAAO,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;QAClC,MAAM,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACxC,MAAM,IAAI,OAAO,CAAC,UAAU,CAAC;QAC7B,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACvC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACtF,CAAC;QACD,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QACxC,IAAI,YAAY,KAAK,IAAI,IAAI,YAAY,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC;YAClE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;QAC1C,CAAC;IACH,CAAC;IACD,OAAO;QACL,EAAE,EAAE,KAAK;QACT,KAAK,EAAE,QAAQ,CAAC,kBAAkB,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC;KACzF,CAAC;AACJ,CAAC,CAAC"}
1
+ {"version":3,"file":"poll-for-daemon-ready.js","sourceRoot":"","sources":["../../src/utils/poll-for-daemon-ready.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,QAAQ,EAAE,MAAM,cAAc,CAAC;AAgBvD,MAAM,kBAAkB,GAAG,KAAK,EAAE,IAAY,EAAoB,EAAE;IAClE,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,oBAAoB,IAAI,aAAa,EAAE;YAClE,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC;SAClC,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,EAAE,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EACrC,OAA2B,EACC,EAAE;IAC9B,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,kBAAkB,CAAC;IAC9D,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,OAAO,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;QAClC,MAAM,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACxC,MAAM,IAAI,OAAO,CAAC,UAAU,CAAC;QAC7B,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACvC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QACtF,CAAC;QACD,MAAM,YAAY,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QACxC,IAAI,YAAY,KAAK,IAAI,IAAI,YAAY,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC;YAClE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;QAC1C,CAAC;QACD,IAAI,YAAY,KAAK,IAAI,EAAE,CAAC;YAC1B,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,YAAY,CAAC,CAAC;YAChD,IAAI,OAAO;gBAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;QACvD,CAAC;IACH,CAAC;IACD,OAAO;QACL,EAAE,EAAE,KAAK;QACT,KAAK,EAAE,QAAQ,CAAC,kBAAkB,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC;KACzF,CAAC;AACJ,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monotykamary/localterm",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "A browser-based terminal: one browser tab is one PTY session. Friendly URL, persistent xterm.js front-end, hono + node-pty back-end.",
5
5
  "keywords": [
6
6
  "browser-terminal",
@@ -43,12 +43,19 @@
43
43
  "publishConfig": {
44
44
  "access": "public"
45
45
  },
46
+ "scripts": {
47
+ "build": "tsc -p tsconfig.build.json",
48
+ "dev": "tsc -p tsconfig.build.json --watch",
49
+ "test": "vp test --run",
50
+ "typecheck": "tsc --noEmit",
51
+ "prepack": "node ../../scripts/prepare-cli-publish.mjs"
52
+ },
46
53
  "dependencies": {
54
+ "@monotykamary/localterm-server": "workspace:*",
47
55
  "commander": "^12.1.0",
48
56
  "kleur": "^4.1.5",
49
57
  "open": "^10.1.0",
50
- "zod": "^4.3.6",
51
- "@monotykamary/localterm-server": "1.6.0"
58
+ "zod": "^4.3.6"
52
59
  },
53
60
  "devDependencies": {
54
61
  "@types/node": "^25.5.0",
@@ -57,11 +64,5 @@
57
64
  },
58
65
  "engines": {
59
66
  "node": ">=22"
60
- },
61
- "scripts": {
62
- "build": "tsc -p tsconfig.build.json",
63
- "dev": "tsc -p tsconfig.build.json --watch",
64
- "test": "vp test --run",
65
- "typecheck": "tsc --noEmit"
66
67
  }
67
- }
68
+ }