lockin-mcp 1.0.4 → 1.0.7

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 (66) hide show
  1. package/README.md +1 -1
  2. package/dist/http-auth.d.ts +4 -3
  3. package/dist/http-auth.d.ts.map +1 -1
  4. package/dist/http-auth.js +7 -3
  5. package/dist/http-auth.js.map +1 -1
  6. package/dist/http-auth.test.js +9 -3
  7. package/dist/http-auth.test.js.map +1 -1
  8. package/dist/index.js +64 -41
  9. package/dist/index.js.map +1 -1
  10. package/dist/install.js +9 -0
  11. package/dist/install.js.map +1 -1
  12. package/dist/installer/agent-instructions.d.ts.map +1 -1
  13. package/dist/installer/agent-instructions.js +0 -1
  14. package/dist/installer/agent-instructions.js.map +1 -1
  15. package/dist/installer/agents.d.ts +1 -1
  16. package/dist/installer/agents.d.ts.map +1 -1
  17. package/dist/installer/agents.js +11 -89
  18. package/dist/installer/agents.js.map +1 -1
  19. package/dist/installer/background-service.d.ts +8 -1
  20. package/dist/installer/background-service.d.ts.map +1 -1
  21. package/dist/installer/background-service.js +22 -2
  22. package/dist/installer/background-service.js.map +1 -1
  23. package/dist/installer/dev-reset.d.ts +16 -0
  24. package/dist/installer/dev-reset.d.ts.map +1 -0
  25. package/dist/installer/dev-reset.js +239 -0
  26. package/dist/installer/dev-reset.js.map +1 -0
  27. package/dist/installer/dev-reset.test.d.ts +2 -0
  28. package/dist/installer/dev-reset.test.d.ts.map +1 -0
  29. package/dist/installer/dev-reset.test.js +23 -0
  30. package/dist/installer/dev-reset.test.js.map +1 -0
  31. package/dist/installer/port.d.ts +8 -0
  32. package/dist/installer/port.d.ts.map +1 -1
  33. package/dist/installer/port.js +76 -4
  34. package/dist/installer/port.js.map +1 -1
  35. package/dist/installer/port.test.d.ts +2 -0
  36. package/dist/installer/port.test.d.ts.map +1 -0
  37. package/dist/installer/port.test.js +55 -0
  38. package/dist/installer/port.test.js.map +1 -0
  39. package/dist/installer/run.js +34 -18
  40. package/dist/installer/run.js.map +1 -1
  41. package/dist/license-check.d.ts +3 -1
  42. package/dist/license-check.d.ts.map +1 -1
  43. package/dist/license-check.js +18 -9
  44. package/dist/license-check.js.map +1 -1
  45. package/dist/license-check.test.d.ts +2 -0
  46. package/dist/license-check.test.d.ts.map +1 -0
  47. package/dist/license-check.test.js +64 -0
  48. package/dist/license-check.test.js.map +1 -0
  49. package/dist/license-guard.d.ts.map +1 -1
  50. package/dist/license-guard.js +38 -1
  51. package/dist/license-guard.js.map +1 -1
  52. package/dist/license-guard.test.js +48 -1
  53. package/dist/license-guard.test.js.map +1 -1
  54. package/dist/relay-client.d.ts +7 -0
  55. package/dist/relay-client.d.ts.map +1 -1
  56. package/dist/relay-client.js +87 -4
  57. package/dist/relay-client.js.map +1 -1
  58. package/dist/relay-manager.d.ts +32 -0
  59. package/dist/relay-manager.d.ts.map +1 -0
  60. package/dist/relay-manager.js +168 -0
  61. package/dist/relay-manager.js.map +1 -0
  62. package/dist/relay-manager.test.d.ts +2 -0
  63. package/dist/relay-manager.test.d.ts.map +1 -0
  64. package/dist/relay-manager.test.js +120 -0
  65. package/dist/relay-manager.test.js.map +1 -0
  66. package/package.json +6 -1
@@ -1 +1 @@
1
- {"version":3,"file":"background-service.js","sourceRoot":"","sources":["../../src/installer/background-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAW9C,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,MAA+B;IAE/B,QAAQ,OAAO,CAAC,QAAQ,EAAE,CAAC;QACzB,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,MAAM,EAAE,kBAAkB,EAAE,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;YAC3D,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC,MAAM,CAAC,CAAC;YACnD,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,iCAAiC,SAAS,GAAG,CAAC,CAAC;YACjE,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,IAAI,CAAC,CAAC,MAAM,aAAa,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;gBACxC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;gBACnE,OAAO,KAAK,CAAC;YACf,CAAC;YACD,MAAM,EAAE,yBAAyB,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;YACxE,MAAM,QAAQ,GAAG,MAAM,yBAAyB,CAAC,MAAM,CAAC,CAAC;YACzD,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,iCAAiC,QAAQ,GAAG,CAAC,CAAC;YAChE,OAAO,IAAI,CAAC;QACd,CAAC;QACD,KAAK,OAAO,CAAC,CAAC,CAAC;YACb,MAAM,EAAE,2BAA2B,EAAE,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;YAC5E,MAAM,UAAU,GAAG,MAAM,2BAA2B,CAAC,MAAM,CAAC,CAAC;YAC7D,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,8BAA8B,UAAU,GAAG,CAAC,CAAC;YAC/D,OAAO,IAAI,CAAC;QACd,CAAC;QACD;YACE,OAAO,KAAK,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,MAA+B;IAE/B,IACE,OAAO,CAAC,QAAQ,KAAK,QAAQ;QAC7B,OAAO,CAAC,QAAQ,KAAK,OAAO;QAC5B,OAAO,CAAC,QAAQ,KAAK,OAAO,EAC5B,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC;QACjC,OAAO,EAAE,OAAO,QAAQ,uCAAuC;QAC/D,YAAY,EAAE,IAAI;KACnB,CAAC,CAAC;IAEH,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;QACtC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,wBAAwB,CAAC,MAAM,CAAC,CAAC;AAC1C,CAAC"}
1
+ {"version":3,"file":"background-service.js","sourceRoot":"","sources":["../../src/installer/background-service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ export interface DevResetOptions {
2
+ /** Remove license.json — you'll need to sign in / enter key again. */
3
+ wipeLicense?: boolean;
4
+ /** Delete all of ~/.lockin (implies wipeLicense). */
5
+ wipeAll?: boolean;
6
+ /** Remove LockIn blocks from /etc/hosts. Default true. */
7
+ clearHosts?: boolean;
8
+ /** Remove LockIn from Claude Desktop config. Only with --full. */
9
+ clearClaudeDesktop?: boolean;
10
+ }
11
+ /** Stop any LockIn MCP HTTP server bound to the standard port range. */
12
+ export declare function killLockInHttpServers(): Promise<string[]>;
13
+ export declare function parseDevResetArgs(argv: string[]): DevResetOptions;
14
+ export declare function printDevResetHelp(): void;
15
+ export declare function runDevReset(options?: DevResetOptions): Promise<void>;
16
+ //# sourceMappingURL=dev-reset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-reset.d.ts","sourceRoot":"","sources":["../../src/installer/dev-reset.ts"],"names":[],"mappings":"AAgCA,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,qDAAqD;IACrD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kEAAkE;IAClE,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAmCD,wEAAwE;AACxE,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAyB/D;AAsED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,eAAe,CAiCjE;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAsBxC;AAED,wBAAsB,WAAW,CAAC,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiE9E"}
@@ -0,0 +1,239 @@
1
+ import { execFile } from "node:child_process";
2
+ import { access, mkdir, rm, unlink } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { promisify } from "node:util";
5
+ import * as clack from "@clack/prompts";
6
+ import color from "picocolors";
7
+ import { syncHostsFile } from "../hosts-manager.js";
8
+ import { claudeDesktopConfigPath, LOCKIN_CLAUDE_MCP_SERVER_KEY, } from "./claude-desktop-config.js";
9
+ import { getDataDirPath } from "./data-dir.js";
10
+ import { uninstallLegacyTunnelLaunchAgents } from "./legacy-tunnels.js";
11
+ import { stopLockInMcpLaunchAgent } from "./mcp-cleanup.js";
12
+ import { DEFAULT_LOCKIN_MCP_PORT, fetchLockInHealth, stopLockInListenersOnPort, } from "./port.js";
13
+ const execFileAsync = promisify(execFile);
14
+ const LICENSE_FILE = "license.json";
15
+ const PARTIAL_RESET_FILES = [
16
+ "setup-manifest.json",
17
+ "relay-credentials.json",
18
+ "state.json",
19
+ "mcp.stdout.log",
20
+ "mcp.stderr.log",
21
+ "install.log",
22
+ ];
23
+ function sleep(ms) {
24
+ return new Promise((resolve) => setTimeout(resolve, ms));
25
+ }
26
+ async function pathExists(path) {
27
+ try {
28
+ await access(path);
29
+ return true;
30
+ }
31
+ catch {
32
+ return false;
33
+ }
34
+ }
35
+ async function listenerPidsOnPort(port) {
36
+ const selfPid = process.pid;
37
+ try {
38
+ const { stdout } = await execFileAsync("lsof", [
39
+ "-nP",
40
+ `-iTCP:${port}`,
41
+ "-sTCP:LISTEN",
42
+ "-t",
43
+ ]);
44
+ return stdout
45
+ .trim()
46
+ .split("\n")
47
+ .filter(Boolean)
48
+ .map((value) => Number(value))
49
+ .filter((pid) => Number.isFinite(pid) && pid > 0 && pid !== selfPid);
50
+ }
51
+ catch {
52
+ return [];
53
+ }
54
+ }
55
+ /** Stop any LockIn MCP HTTP server bound to the standard port range. */
56
+ export async function killLockInHttpServers() {
57
+ const killed = [];
58
+ for (let offset = 0; offset < 20; offset += 1) {
59
+ const port = DEFAULT_LOCKIN_MCP_PORT + offset;
60
+ if (!(await fetchLockInHealth(port))) {
61
+ continue;
62
+ }
63
+ const listenerPids = await listenerPidsOnPort(port);
64
+ if (listenerPids.length === 0) {
65
+ continue;
66
+ }
67
+ for (const pid of listenerPids) {
68
+ killed.push(`pid ${pid} (port ${port})`);
69
+ }
70
+ await stopLockInListenersOnPort(port);
71
+ }
72
+ if (killed.length > 0) {
73
+ await sleep(600);
74
+ }
75
+ return killed;
76
+ }
77
+ async function removeClaudeDesktopEntry() {
78
+ const configPath = claudeDesktopConfigPath();
79
+ if (!(await pathExists(configPath))) {
80
+ return false;
81
+ }
82
+ const { readFile, writeFile } = await import("node:fs/promises");
83
+ const raw = await readFile(configPath, "utf8");
84
+ const config = JSON.parse(raw);
85
+ const servers = config.mcpServers && typeof config.mcpServers === "object"
86
+ ? config.mcpServers
87
+ : null;
88
+ if (!servers || !(LOCKIN_CLAUDE_MCP_SERVER_KEY in servers)) {
89
+ return false;
90
+ }
91
+ delete servers[LOCKIN_CLAUDE_MCP_SERVER_KEY];
92
+ config.mcpServers = servers;
93
+ await writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`);
94
+ return true;
95
+ }
96
+ async function wipeDataDir(options) {
97
+ const dataDir = getDataDirPath();
98
+ const removed = [];
99
+ if (options.wipeAll) {
100
+ if (await pathExists(dataDir)) {
101
+ await rm(dataDir, { recursive: true, force: true });
102
+ removed.push(dataDir);
103
+ }
104
+ await mkdir(dataDir, { recursive: true, mode: 0o700 });
105
+ return removed;
106
+ }
107
+ for (const name of PARTIAL_RESET_FILES) {
108
+ const path = join(dataDir, name);
109
+ if (await pathExists(path)) {
110
+ await unlink(path);
111
+ removed.push(path);
112
+ }
113
+ }
114
+ if (options.wipeLicense) {
115
+ const licensePath = join(dataDir, LICENSE_FILE);
116
+ if (await pathExists(licensePath)) {
117
+ await unlink(licensePath);
118
+ removed.push(licensePath);
119
+ }
120
+ }
121
+ return removed;
122
+ }
123
+ async function removeHostsSudoersHint() {
124
+ const sudoersPath = "/etc/sudoers.d/lockin-mcp-hosts";
125
+ if (process.platform !== "darwin" || !(await pathExists(sudoersPath))) {
126
+ return;
127
+ }
128
+ clack.log.info(color.dim(`Optional: remove passwordless hosts sudo — sudo rm ${sudoersPath}`));
129
+ }
130
+ export function parseDevResetArgs(argv) {
131
+ const options = {
132
+ clearHosts: true,
133
+ };
134
+ for (const arg of argv) {
135
+ switch (arg) {
136
+ case "--full":
137
+ options.wipeAll = true;
138
+ options.wipeLicense = true;
139
+ options.clearClaudeDesktop = true;
140
+ break;
141
+ case "--wipe-license":
142
+ options.wipeLicense = true;
143
+ break;
144
+ case "--no-hosts":
145
+ options.clearHosts = false;
146
+ break;
147
+ case "--help":
148
+ case "-h":
149
+ printDevResetHelp();
150
+ process.exit(0);
151
+ break;
152
+ default:
153
+ throw new Error(`Unknown option: ${arg}`);
154
+ }
155
+ }
156
+ if (options.wipeAll) {
157
+ options.wipeLicense = true;
158
+ }
159
+ return options;
160
+ }
161
+ export function printDevResetHelp() {
162
+ console.log(`\
163
+ LockIn MCP — developer reset
164
+
165
+ Stops background daemons and dev servers, then wipes local install state so
166
+ the next install behaves like a fresh user machine.
167
+
168
+ Usage:
169
+ npx lockin-mcp dev-reset [options]
170
+ npm run dev:reset
171
+
172
+ Options:
173
+ --full Delete all of ~/.lockin including license (true fresh user)
174
+ --wipe-license Also remove license.json (re-auth on next install)
175
+ --no-hosts Leave /etc/hosts unchanged
176
+ -h, --help Show this help
177
+
178
+ Workflow:
179
+ npm run install:fresh # reset + wipe license + install (new-user flow)
180
+ npm run install:fresh:full # full ~/.lockin wipe + Claude Desktop + install
181
+ npm run dev:reset # reset relay/state only (keeps license for fast iteration)
182
+ `);
183
+ }
184
+ export async function runDevReset(options = {}) {
185
+ const clearHosts = options.clearHosts !== false;
186
+ clack.intro(color.bold("LockIn developer reset"));
187
+ const killed = await killLockInHttpServers();
188
+ if (killed.length > 0) {
189
+ clack.log.success(`Stopped LockIn HTTP server(s): ${killed.join(", ")}`);
190
+ }
191
+ else {
192
+ clack.log.info("No LockIn HTTP servers found on ports 3847–3866");
193
+ }
194
+ await uninstallLegacyTunnelLaunchAgents();
195
+ const stoppedAgent = await stopLockInMcpLaunchAgent();
196
+ if (stoppedAgent) {
197
+ clack.log.success("Stopped com.lockin.mcp LaunchAgent");
198
+ }
199
+ const removedPaths = await wipeDataDir(options);
200
+ if (removedPaths.length > 0) {
201
+ clack.log.success(options.wipeAll
202
+ ? `Removed ${color.cyan("~/.lockin")} and recreated empty directory`
203
+ : `Removed ${removedPaths.length} file(s) from ${color.cyan("~/.lockin")}`);
204
+ }
205
+ if (options.clearClaudeDesktop) {
206
+ const cleared = await removeClaudeDesktopEntry();
207
+ if (cleared) {
208
+ clack.log.success("Removed LockIn from Claude Desktop config");
209
+ }
210
+ }
211
+ if (clearHosts) {
212
+ try {
213
+ const sync = await syncHostsFile([]);
214
+ if (sync.method === "unchanged") {
215
+ clack.log.info("Hosts file had no LockIn entries");
216
+ }
217
+ else {
218
+ clack.log.success(`Cleared LockIn entries from /etc/hosts (${sync.method})`);
219
+ }
220
+ }
221
+ catch (error) {
222
+ const message = error instanceof Error ? error.message : String(error);
223
+ clack.log.warn(`Could not clear hosts file: ${message}`);
224
+ }
225
+ }
226
+ await removeHostsSudoersHint();
227
+ const licenseNote = options.wipeAll || options.wipeLicense
228
+ ? color.dim("License removed — install will prompt for sign-in or key.")
229
+ : color.dim("License kept — use --wipe-license or install:fresh for a new-user flow.");
230
+ clack.outro([
231
+ "Ready for a clean install.",
232
+ licenseNote,
233
+ "",
234
+ ` ${color.cyan("npm run install:local")} — install from this repo`,
235
+ ` ${color.cyan("npm run install:prod")} — install against production`,
236
+ ` ${color.cyan("npx lockin-mcp install")} — install published package`,
237
+ ].join("\n"));
238
+ }
239
+ //# sourceMappingURL=dev-reset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-reset.js","sourceRoot":"","sources":["../../src/installer/dev-reset.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=dev-reset.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-reset.test.d.ts","sourceRoot":"","sources":["../../src/installer/dev-reset.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,23 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { parseDevResetArgs } from "./dev-reset.js";
3
+ describe("parseDevResetArgs", () => {
4
+ it("defaults to partial reset with hosts cleanup", () => {
5
+ expect(parseDevResetArgs([])).toEqual({
6
+ clearHosts: true,
7
+ });
8
+ });
9
+ it("enables full wipe flags", () => {
10
+ expect(parseDevResetArgs(["--full"])).toEqual({
11
+ wipeAll: true,
12
+ wipeLicense: true,
13
+ clearHosts: true,
14
+ clearClaudeDesktop: true,
15
+ });
16
+ });
17
+ it("can skip hosts cleanup", () => {
18
+ expect(parseDevResetArgs(["--no-hosts"])).toEqual({
19
+ clearHosts: false,
20
+ });
21
+ });
22
+ });
23
+ //# sourceMappingURL=dev-reset.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev-reset.test.js","sourceRoot":"","sources":["../../src/installer/dev-reset.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YACpC,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,CAAC,iBAAiB,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YAC5C,OAAO,EAAE,IAAI;YACb,WAAW,EAAE,IAAI;YACjB,UAAU,EAAE,IAAI;YAChB,kBAAkB,EAAE,IAAI;SACzB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,CAAC,iBAAiB,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YAChD,UAAU,EAAE,KAAK;SAClB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,5 +1,13 @@
1
1
  export declare const DEFAULT_LOCKIN_MCP_PORT = 3847;
2
+ export interface LockInHealthSnapshot {
3
+ service: string;
4
+ relayConnected: boolean;
5
+ }
6
+ export declare function fetchLockInHealthSnapshot(port: number): Promise<LockInHealthSnapshot | null>;
2
7
  export declare function fetchLockInHealth(port: number): Promise<boolean>;
8
+ /** Stop a LockIn MCP listener so launchd or a fresh install can recover the port. */
9
+ export declare function stopLockInListenersOnPort(port: number): Promise<void>;
10
+ export declare function waitForLocalRelayHealth(port: number, attempts?: number, delayMs?: number): Promise<LockInHealthSnapshot>;
3
11
  export declare function isPortAvailable(port: number): Promise<boolean>;
4
12
  export declare function describePortConflict(port: number): Promise<string | null>;
5
13
  export declare function resolveMcpPort(preferred: number): Promise<number>;
@@ -1 +1 @@
1
- {"version":3,"file":"port.d.ts","sourceRoot":"","sources":["../../src/installer/port.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAatE;AAED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASpE;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuB/E;AAED,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BvE"}
1
+ {"version":3,"file":"port.d.ts","sourceRoot":"","sources":["../../src/installer/port.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAsBtC;AAED,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAEtE;AAuBD,qFAAqF;AACrF,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0B3E;AAED,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,MAAM,EACZ,QAAQ,SAAK,EACb,OAAO,SAAM,GACZ,OAAO,CAAC,oBAAoB,CAAC,CAY/B;AAMD,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASpE;AAED,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAuB/E;AAED,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA4BvE"}
@@ -1,20 +1,92 @@
1
+ import { execFile } from "node:child_process";
1
2
  import { createServer } from "node:net";
3
+ import { promisify } from "node:util";
2
4
  import { localHttpRequest } from "../local-http.js";
5
+ const execFileAsync = promisify(execFile);
3
6
  export const DEFAULT_LOCKIN_MCP_PORT = 3847;
4
- export async function fetchLockInHealth(port) {
7
+ export async function fetchLockInHealthSnapshot(port) {
5
8
  try {
6
9
  const response = await localHttpRequest(`http://127.0.0.1:${port}/health`, {
7
10
  timeoutMs: 2000,
8
11
  });
9
12
  if (!response.ok) {
10
- return false;
13
+ return null;
11
14
  }
12
15
  const body = (await response.json());
13
- return body.service === "lockin-mcp";
16
+ if (body.service !== "lockin-mcp") {
17
+ return null;
18
+ }
19
+ return {
20
+ service: body.service,
21
+ relayConnected: body.relay?.connected === true,
22
+ };
14
23
  }
15
24
  catch {
16
- return false;
25
+ return null;
26
+ }
27
+ }
28
+ export async function fetchLockInHealth(port) {
29
+ return (await fetchLockInHealthSnapshot(port)) !== null;
30
+ }
31
+ function sleep(ms) {
32
+ return new Promise((resolve) => setTimeout(resolve, ms));
33
+ }
34
+ async function findListenerPids(port) {
35
+ try {
36
+ const { stdout } = await execFileAsync("lsof", [
37
+ "-nP",
38
+ `-iTCP:${port}`,
39
+ "-sTCP:LISTEN",
40
+ "-t",
41
+ ]);
42
+ return stdout
43
+ .split("\n")
44
+ .map((line) => Number(line.trim()))
45
+ .filter((pid) => Number.isFinite(pid) && pid > 0);
46
+ }
47
+ catch {
48
+ return [];
49
+ }
50
+ }
51
+ /** Stop a LockIn MCP listener so launchd or a fresh install can recover the port. */
52
+ export async function stopLockInListenersOnPort(port) {
53
+ const pids = await findListenerPids(port);
54
+ for (const pid of pids) {
55
+ try {
56
+ process.kill(pid, "SIGTERM");
57
+ }
58
+ catch {
59
+ // already exited
60
+ }
17
61
  }
62
+ for (let attempt = 0; attempt < 10; attempt += 1) {
63
+ if (await isPortAvailable(port)) {
64
+ return;
65
+ }
66
+ await sleep(300);
67
+ }
68
+ for (const pid of await findListenerPids(port)) {
69
+ try {
70
+ process.kill(pid, "SIGKILL");
71
+ }
72
+ catch {
73
+ // already exited
74
+ }
75
+ }
76
+ await sleep(300);
77
+ }
78
+ export async function waitForLocalRelayHealth(port, attempts = 40, delayMs = 500) {
79
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
80
+ const snapshot = await fetchLockInHealthSnapshot(port);
81
+ if (snapshot?.relayConnected) {
82
+ return snapshot;
83
+ }
84
+ await sleep(delayMs);
85
+ }
86
+ throw new Error(`LockIn MCP on port ${port} never connected to relay — check ${getDataDirHint()}/mcp.stderr.log`);
87
+ }
88
+ function getDataDirHint() {
89
+ return process.platform === "win32" ? "%USERPROFILE%\\.lockin" : "~/.lockin";
18
90
  }
19
91
  export async function isPortAvailable(port) {
20
92
  return new Promise((resolve) => {
@@ -1 +1 @@
1
- {"version":3,"file":"port.js","sourceRoot":"","sources":["../../src/installer/port.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"port.js","sourceRoot":"","sources":["../../src/installer/port.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=port.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"port.test.d.ts","sourceRoot":"","sources":["../../src/installer/port.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,55 @@
1
+ import { describe, expect, it, vi, afterEach } from "vitest";
2
+ import { fetchLockInHealthSnapshot, waitForLocalRelayHealth, } from "./port.js";
3
+ vi.mock("../local-http.js", () => ({
4
+ localHttpRequest: vi.fn(),
5
+ }));
6
+ import { localHttpRequest } from "../local-http.js";
7
+ function mockHealthResponse(body) {
8
+ return {
9
+ ok: true,
10
+ status: 200,
11
+ headers: {},
12
+ text: async () => JSON.stringify(body),
13
+ json: async () => body,
14
+ };
15
+ }
16
+ describe("fetchLockInHealthSnapshot", () => {
17
+ afterEach(() => {
18
+ vi.mocked(localHttpRequest).mockReset();
19
+ });
20
+ it("returns relay connection state from /health", async () => {
21
+ vi.mocked(localHttpRequest).mockResolvedValue(mockHealthResponse({
22
+ service: "lockin-mcp",
23
+ relay: { connected: true },
24
+ }));
25
+ await expect(fetchLockInHealthSnapshot(3848)).resolves.toEqual({
26
+ service: "lockin-mcp",
27
+ relayConnected: true,
28
+ });
29
+ });
30
+ it("returns null for non-lockin listeners", async () => {
31
+ vi.mocked(localHttpRequest).mockResolvedValue(mockHealthResponse({ service: "other-app" }));
32
+ await expect(fetchLockInHealthSnapshot(3848)).resolves.toBeNull();
33
+ });
34
+ });
35
+ describe("waitForLocalRelayHealth", () => {
36
+ afterEach(() => {
37
+ vi.mocked(localHttpRequest).mockReset();
38
+ });
39
+ it("waits until relay.connected is true", async () => {
40
+ vi.mocked(localHttpRequest)
41
+ .mockResolvedValueOnce(mockHealthResponse({
42
+ service: "lockin-mcp",
43
+ relay: { connected: false },
44
+ }))
45
+ .mockResolvedValueOnce(mockHealthResponse({
46
+ service: "lockin-mcp",
47
+ relay: { connected: true },
48
+ }));
49
+ await expect(waitForLocalRelayHealth(3848, 5, 1)).resolves.toEqual({
50
+ service: "lockin-mcp",
51
+ relayConnected: true,
52
+ });
53
+ });
54
+ });
55
+ //# sourceMappingURL=port.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"port.test.js","sourceRoot":"","sources":["../../src/installer/port.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAE7D,OAAO,EACL,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,WAAW,CAAC;AAEnB,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,CAAC;IACjC,gBAAgB,EAAE,EAAE,CAAC,EAAE,EAAE;CAC1B,CAAC,CAAC,CAAC;AAEJ,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,SAAS,kBAAkB,CAAC,IAAa;IACvC,OAAO;QACL,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,GAAG;QACX,OAAO,EAAE,EAAE;QACX,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;QACtC,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI;KACvB,CAAC;AACJ,CAAC;AAED,QAAQ,CAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,SAAS,EAAE,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;QAC3D,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,iBAAiB,CAC3C,kBAAkB,CAAC;YACjB,OAAO,EAAE,YAAY;YACrB,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE;SAC3B,CAAC,CACH,CAAC;QAEF,MAAM,MAAM,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;YAC7D,OAAO,EAAE,YAAY;YACrB,cAAc,EAAE,IAAI;SACrB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,iBAAiB,CAC3C,kBAAkB,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAC7C,CAAC;QAEF,MAAM,MAAM,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;IACpE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,SAAS,EAAE,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;QACnD,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC;aACxB,qBAAqB,CACpB,kBAAkB,CAAC;YACjB,OAAO,EAAE,YAAY;YACrB,KAAK,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE;SAC5B,CAAC,CACH;aACA,qBAAqB,CACpB,kBAAkB,CAAC;YACjB,OAAO,EAAE,YAAY;YACrB,KAAK,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE;SAC3B,CAAC,CACH,CAAC;QAEJ,MAAM,MAAM,CACV,uBAAuB,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CACpC,CAAC,QAAQ,CAAC,OAAO,CAAC;YACjB,OAAO,EAAE,YAAY;YACrB,cAAc,EAAE,IAAI;SACrB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -9,9 +9,9 @@ import { canWriteHostsFile } from "../hosts-manager.js";
9
9
  import { verifyLicenseInfo } from "../license-check.js";
10
10
  import { resolveLicense } from "./license.js";
11
11
  import { detectPlatform, ensureNodeVersion } from "./platform.js";
12
- import { describePortConflict, resolveMcpPort } from "./port.js";
12
+ import { describePortConflict, resolveMcpPort, stopLockInListenersOnPort, fetchLockInHealth } from "./port.js";
13
13
  import { buildPokeUrls, POKE_LOCKIN_RECIPE_URL } from "./poke.js";
14
- import { promptConnectAgent, promptBackgroundService, installBackgroundService } from "./agents.js";
14
+ import { promptConnectAgent, promptBackgroundService, handoffToBackgroundService } from "./agents.js";
15
15
  import { promptInstallHostsSudoers } from "./hosts-sudo.js";
16
16
  import { uninstallLegacyTunnelLaunchAgents } from "./legacy-tunnels.js";
17
17
  import { stopLockInMcpLaunchAgent } from "./mcp-cleanup.js";
@@ -167,6 +167,10 @@ async function runInstallerInner(argv) {
167
167
  if (stoppedBackground) {
168
168
  clack.log.info("Stopped previous LockIn session for a clean setup.");
169
169
  }
170
+ if (await fetchLockInHealth(options.port)) {
171
+ await stopLockInListenersOnPort(options.port);
172
+ clack.log.info("Cleared previous LockIn HTTP server on this port.");
173
+ }
170
174
  const portConflict = await describePortConflict(options.port);
171
175
  if (portConflict) {
172
176
  const resolvedPort = await resolveMcpPort(options.port);
@@ -294,7 +298,7 @@ async function runInstallerInner(argv) {
294
298
  const hasRemoteRelay = !options.skipTunnel && remoteMcpUrl.startsWith("https://");
295
299
  const serverEntry = join(dirname(fileURLToPath(import.meta.url)), "..", "index.js");
296
300
  if (!options.nonInteractive) {
297
- const installedBackground = await promptBackgroundService({
301
+ const wantsBackground = await promptBackgroundService({
298
302
  port: options.port,
299
303
  authToken: mcpAuthToken,
300
304
  serverEntry,
@@ -302,9 +306,21 @@ async function runInstallerInner(argv) {
302
306
  deviceId: relayCredentials?.deviceId,
303
307
  relayDeviceToken: relayCredentials?.relayDeviceToken,
304
308
  });
305
- if (installedBackground) {
306
- mcpProcess.kill("SIGTERM");
307
- await waitForHttp(`${localMcpUrl}/health`, 20, 500);
309
+ let installedBackground = false;
310
+ if (wantsBackground) {
311
+ await handoffToBackgroundService({
312
+ mcpProcess,
313
+ port: options.port,
314
+ service: {
315
+ port: options.port,
316
+ authToken: mcpAuthToken,
317
+ serverEntry,
318
+ relayUrl: relayCredentials?.relayConnectUrl ?? relayCredentials?.relayUrl,
319
+ deviceId: relayCredentials?.deviceId,
320
+ relayDeviceToken: relayCredentials?.relayDeviceToken,
321
+ },
322
+ });
323
+ installedBackground = true;
308
324
  }
309
325
  if (hasRemoteRelay) {
310
326
  await promptConnectAgent({
@@ -325,20 +341,20 @@ async function runInstallerInner(argv) {
325
341
  }
326
342
  }
327
343
  else if (relayCredentials) {
328
- const installedBackground = await installBackgroundService({
344
+ await handoffToBackgroundService({
345
+ mcpProcess,
329
346
  port: options.port,
330
- authToken: mcpAuthToken,
331
- serverEntry,
332
- relayUrl: relayCredentials.relayConnectUrl ?? relayCredentials.relayUrl,
333
- deviceId: relayCredentials.deviceId,
334
- relayDeviceToken: relayCredentials.relayDeviceToken,
347
+ service: {
348
+ port: options.port,
349
+ authToken: mcpAuthToken,
350
+ serverEntry,
351
+ relayUrl: relayCredentials.relayConnectUrl ?? relayCredentials.relayUrl,
352
+ deviceId: relayCredentials.deviceId,
353
+ relayDeviceToken: relayCredentials.relayDeviceToken,
354
+ },
335
355
  });
336
- if (installedBackground) {
337
- mcpProcess.kill("SIGTERM");
338
- await waitForHttp(`${localMcpUrl}/health`, 20, 500);
339
- clack.outro(color.dim(`LockIn MCP runs in the background on port ${options.port}. Remote URL: ${remoteMcpUrl}`));
340
- process.exit(0);
341
- }
356
+ clack.outro(color.dim(`LockIn MCP runs in the background on port ${options.port}. Remote URL: ${remoteMcpUrl}`));
357
+ process.exit(0);
342
358
  }
343
359
  if (hasRemoteRelay) {
344
360
  clack.outro(color.dim("LockIn is running. Turn on background mode next time to close this window."));