@elitedcs/ghl-mcp 3.58.0 → 3.59.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -114,8 +114,8 @@ var init_ghl_client = __esm({
114
114
  Version: version || GHL_API_VERSION
115
115
  };
116
116
  }
117
- buildUrl(path13, params) {
118
- const url = new URL(path13, GHL_BASE_URL);
117
+ buildUrl(path14, params) {
118
+ const url = new URL(path14, GHL_BASE_URL);
119
119
  if (params) {
120
120
  for (const [key, value] of Object.entries(params)) {
121
121
  if (value !== void 0 && value !== null) {
@@ -125,8 +125,8 @@ var init_ghl_client = __esm({
125
125
  }
126
126
  return url.toString();
127
127
  }
128
- async request(method, path13, options = {}, attempt = 0) {
129
- const url = this.buildUrl(path13, options.params);
128
+ async request(method, path14, options = {}, attempt = 0) {
129
+ const url = this.buildUrl(path14, options.params);
130
130
  const headers = this.buildHeaders(options.version);
131
131
  const fetchOptions = {
132
132
  method,
@@ -144,14 +144,14 @@ var init_ghl_client = __esm({
144
144
  } catch (error) {
145
145
  clearTimeout(timeout);
146
146
  if (error instanceof Error && error.name === "AbortError") {
147
- throw new Error(`Request timeout (30s): ${method} ${path13}`);
147
+ throw new Error(`Request timeout (30s): ${method} ${path14}`);
148
148
  }
149
149
  if (!options.noRetry && attempt < MAX_RETRIES) {
150
150
  const delay4 = computeRetryDelay(null, attempt, BASE_DELAY_MS);
151
- process.stderr.write(`[ghl-mcp] Network error on ${method} ${path13}, retry ${attempt + 1}/${MAX_RETRIES} in ${delay4}ms
151
+ process.stderr.write(`[ghl-mcp] Network error on ${method} ${path14}, retry ${attempt + 1}/${MAX_RETRIES} in ${delay4}ms
152
152
  `);
153
153
  await new Promise((r) => setTimeout(r, delay4));
154
- return this.request(method, path13, options, attempt + 1);
154
+ return this.request(method, path14, options, attempt + 1);
155
155
  }
156
156
  throw error;
157
157
  } finally {
@@ -159,10 +159,10 @@ var init_ghl_client = __esm({
159
159
  }
160
160
  if (!options.noRetry && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES) {
161
161
  const delay4 = computeRetryDelay(response.headers.get("Retry-After"), attempt, BASE_DELAY_MS);
162
- process.stderr.write(`[ghl-mcp] ${response.status} on ${method} ${path13}, retry ${attempt + 1}/${MAX_RETRIES} in ${delay4}ms
162
+ process.stderr.write(`[ghl-mcp] ${response.status} on ${method} ${path14}, retry ${attempt + 1}/${MAX_RETRIES} in ${delay4}ms
163
163
  `);
164
164
  await new Promise((r) => setTimeout(r, delay4));
165
- return this.request(method, path13, options, attempt + 1);
165
+ return this.request(method, path14, options, attempt + 1);
166
166
  }
167
167
  if (!response.ok) {
168
168
  let errorBody = "";
@@ -171,7 +171,7 @@ var init_ghl_client = __esm({
171
171
  } catch {
172
172
  }
173
173
  throw new Error(
174
- `GHL API Error ${response.status} ${response.statusText}: ${method} ${path13}
174
+ `GHL API Error ${response.status} ${response.statusText}: ${method} ${path14}
175
175
  ${errorBody}`
176
176
  );
177
177
  }
@@ -183,20 +183,20 @@ ${errorBody}`
183
183
  return { message: text };
184
184
  }
185
185
  }
186
- async get(path13, options) {
187
- return this.request("GET", path13, options);
186
+ async get(path14, options) {
187
+ return this.request("GET", path14, options);
188
188
  }
189
- async post(path13, options) {
190
- return this.request("POST", path13, options);
189
+ async post(path14, options) {
190
+ return this.request("POST", path14, options);
191
191
  }
192
- async put(path13, options) {
193
- return this.request("PUT", path13, options);
192
+ async put(path14, options) {
193
+ return this.request("PUT", path14, options);
194
194
  }
195
- async patch(path13, options) {
196
- return this.request("PATCH", path13, options);
195
+ async patch(path14, options) {
196
+ return this.request("PATCH", path14, options);
197
197
  }
198
- async delete(path13, options) {
199
- return this.request("DELETE", path13, options);
198
+ async delete(path14, options) {
199
+ return this.request("DELETE", path14, options);
200
200
  }
201
201
  /**
202
202
  * Helper: resolves locationId from args or falls back to default
@@ -1270,9 +1270,9 @@ function planFromPayload(payload) {
1270
1270
  return payload?.plan === "command-os" ? "command-os" : "mcp";
1271
1271
  }
1272
1272
  function legacyDeviceFingerprint() {
1273
- const os6 = require("node:os");
1273
+ const os7 = require("node:os");
1274
1274
  const crypto5 = require("node:crypto");
1275
- const raw = `${os6.hostname()}:${os6.userInfo().username}:${os6.platform()}:${os6.arch()}`;
1275
+ const raw = `${os7.hostname()}:${os7.userInfo().username}:${os7.platform()}:${os7.arch()}`;
1276
1276
  return crypto5.createHash("sha256").update(raw).digest("hex").slice(0, 16);
1277
1277
  }
1278
1278
  function deviceFingerprint(opts) {
@@ -1508,7 +1508,7 @@ function registerSetupTool(server2, pkgVersion) {
1508
1508
  if (pkgVersion) setupPkgVersion = pkgVersion;
1509
1509
  server2.tool(
1510
1510
  "setup_ghl_mcp",
1511
- "First-run setup for GHL Command MCP. Validates your license and GHL credentials, then writes them to a per-user credentials file. Restart Claude after this completes to load all 233 tools (179 if you skip the optional Firebase fields; add Firebase later with enable_workflow_builder).",
1511
+ "First-run setup for GHL Command MCP. Validates your license and GHL credentials, then writes them to a per-user credentials file. Restart Claude after this completes to load all 233 tools (179 if you skip the optional Firebase fields; add Firebase later with enable_workflow_builder). No license yet? Paid ($97/mo) at https://ghlcommand.com \u2014 or a FREE read-only key, instantly, at https://ghlcommand.com/free.",
1512
1512
  {
1513
1513
  email: import_zod42.z.string().email().describe("Email used at purchase."),
1514
1514
  license_key: import_zod42.z.string().min(20).describe("License key from your purchase email."),
@@ -1531,7 +1531,7 @@ function registerSetupTool(server2, pkgVersion) {
1531
1531
  emit("setup_failed", { pkgVersion: setupPkgVersion, reasonCode: LICENSE_REASON_MAP[lic.reason] });
1532
1532
  return { content: [{ type: "text", text: `License check failed: ${lic.error}
1533
1533
 
1534
- Purchase a license at https://elitedcs.com/ghl-mcp-server or contact support.` }], isError: true };
1534
+ Purchase a license at https://ghlcommand.com \u2014 or get a FREE read-only key instantly at https://ghlcommand.com/free. Stuck? Reply to any email from us or write support@ghlcommand.com.` }], isError: true };
1535
1535
  }
1536
1536
  const ghl = await validateGhl(args.ghl_api_key, args.ghl_location_id);
1537
1537
  if (!ghl.ok) {
@@ -2390,12 +2390,409 @@ var init_question_set = __esm({
2390
2390
  }
2391
2391
  });
2392
2392
 
2393
+ // src/config-installer.ts
2394
+ var config_installer_exports = {};
2395
+ __export(config_installer_exports, {
2396
+ EXIT_ABORTED: () => EXIT_ABORTED,
2397
+ EXIT_OK: () => EXIT_OK,
2398
+ EXIT_REFUSED: () => EXIT_REFUSED,
2399
+ EXIT_USAGE: () => EXIT_USAGE,
2400
+ atomicReplace: () => atomicReplace,
2401
+ baselineOf: () => baselineOf,
2402
+ candidateConfigPaths: () => candidateConfigPaths,
2403
+ chooseBackupPath: () => chooseBackupPath,
2404
+ resolveConfigPath: () => resolveConfigPath,
2405
+ resolveSymlinkPolicy: () => resolveSymlinkPolicy,
2406
+ runInstall: () => runInstall
2407
+ });
2408
+ function defaultIO() {
2409
+ return {
2410
+ out: (l) => process.stdout.write(l + "\n"),
2411
+ err: (l) => process.stderr.write(l + "\n"),
2412
+ rename: (from, to) => fs9.renameSync(from, to),
2413
+ sleep: (ms) => new Promise((r) => setTimeout(r, ms))
2414
+ };
2415
+ }
2416
+ function candidateConfigPaths(opts) {
2417
+ const platform2 = opts?.platform ?? process.platform;
2418
+ const home = opts?.home ?? os4.homedir();
2419
+ const file = "claude_desktop_config.json";
2420
+ const candidates = [];
2421
+ if (platform2 === "darwin") {
2422
+ candidates.push(path8.join(home, "Library", "Application Support", "Claude", file));
2423
+ } else if (platform2 === "win32") {
2424
+ const appData = opts?.appData ?? process.env.APPDATA ?? path8.join(home, "AppData", "Roaming");
2425
+ candidates.push(path8.join(appData, "Claude", file));
2426
+ const localAppData = opts?.localAppData ?? process.env.LOCALAPPDATA ?? path8.join(home, "AppData", "Local");
2427
+ const packagesDir = path8.join(localAppData, "Packages");
2428
+ try {
2429
+ for (const entry of fs9.readdirSync(packagesDir)) {
2430
+ if (entry.startsWith("Claude_")) {
2431
+ candidates.push(path8.join(packagesDir, entry, "LocalCache", "Roaming", "Claude", file));
2432
+ }
2433
+ }
2434
+ } catch {
2435
+ }
2436
+ } else {
2437
+ candidates.push(path8.join(home, ".config", "Claude", file));
2438
+ }
2439
+ return candidates;
2440
+ }
2441
+ function resolveConfigPath(explicitPath) {
2442
+ if (explicitPath) {
2443
+ const p = path8.resolve(explicitPath);
2444
+ return { configPath: p, exists: fs9.existsSync(p) };
2445
+ }
2446
+ const candidates = candidateConfigPaths();
2447
+ const existing = candidates.filter((c) => fs9.existsSync(c));
2448
+ if (existing.length > 1) {
2449
+ throw new InstallStop(
2450
+ EXIT_REFUSED,
2451
+ [
2452
+ "More than one Claude settings file was found, and picking the wrong one would break your setup:",
2453
+ ...existing.map((p) => ` ${p}`),
2454
+ "Run the command again with --path and the one Claude actually uses. Nothing was changed."
2455
+ ].join("\n")
2456
+ );
2457
+ }
2458
+ if (existing.length === 1) return { configPath: existing[0], exists: true };
2459
+ return { configPath: candidates[0], exists: false };
2460
+ }
2461
+ function resolveSymlinkPolicy(configPath, explicitPath, home = os4.homedir()) {
2462
+ let st;
2463
+ try {
2464
+ st = fs9.lstatSync(configPath);
2465
+ } catch {
2466
+ return configPath;
2467
+ }
2468
+ if (!st.isSymbolicLink()) return configPath;
2469
+ let real;
2470
+ try {
2471
+ real = fs9.realpathSync(configPath);
2472
+ } catch {
2473
+ throw new InstallStop(
2474
+ EXIT_REFUSED,
2475
+ `${configPath} is a link that points to a file that does not exist. Fix or remove the link, or run again with --path to a real file. Nothing was changed.`
2476
+ );
2477
+ }
2478
+ const rel = path8.relative(path8.resolve(home), real);
2479
+ const outsideHome = rel.startsWith("..") || path8.isAbsolute(rel);
2480
+ if (outsideHome && !explicitPath) {
2481
+ throw new InstallStop(
2482
+ EXIT_REFUSED,
2483
+ `${configPath} is a link pointing outside your home folder (to ${real}). If that is intentional, run again with --path pointing at it directly. Nothing was changed.`
2484
+ );
2485
+ }
2486
+ return real;
2487
+ }
2488
+ function readConfigBytes(configPath) {
2489
+ const bytes = fs9.readFileSync(configPath);
2490
+ const stat = fs9.statSync(configPath);
2491
+ if (bytes.length >= 2 && (bytes[0] === 255 && bytes[1] === 254 || bytes[0] === 254 && bytes[1] === 255)) {
2492
+ throw new InstallStop(
2493
+ EXIT_REFUSED,
2494
+ "This settings file is saved in an encoding this command doesn't edit (UTF-16). Nothing was changed. Reply to your setup email and we'll sort it out."
2495
+ );
2496
+ }
2497
+ const hadBom = bytes.length >= 3 && bytes.subarray(0, 3).equals(BOM_UTF8);
2498
+ const text = (hadBom ? bytes.subarray(3) : bytes).toString("utf8");
2499
+ return { text, hadBom, bytes, stat };
2500
+ }
2501
+ function isPlainObject(v) {
2502
+ return typeof v === "object" && v !== null && !Array.isArray(v);
2503
+ }
2504
+ function wrongShapeStop() {
2505
+ return new InstallStop(
2506
+ EXIT_REFUSED,
2507
+ "This settings file doesn't have the layout Claude uses, so this command won't rewrite it. Nothing was changed. Reply to your setup email and we'll sort it out."
2508
+ );
2509
+ }
2510
+ function parseTiered(text) {
2511
+ if (text.trim() === "") return { tier: 1, root: {} };
2512
+ try {
2513
+ const strict = JSON.parse(text);
2514
+ if (!isPlainObject(strict)) throw wrongShapeStop();
2515
+ return { tier: 1, root: strict };
2516
+ } catch (e) {
2517
+ if (e instanceof InstallStop) throw e;
2518
+ }
2519
+ try {
2520
+ const tolerant = import_json5.default.parse(text);
2521
+ if (!isPlainObject(tolerant)) throw wrongShapeStop();
2522
+ return { tier: 2, root: tolerant };
2523
+ } catch (e) {
2524
+ if (e instanceof InstallStop) throw e;
2525
+ }
2526
+ return { tier: 3, root: null };
2527
+ }
2528
+ function entriesEqual(a) {
2529
+ if (!isPlainObject(a)) return false;
2530
+ return a.command === DESIRED_ENTRY.command && Array.isArray(a.args) && a.args.length === DESIRED_ENTRY.args.length && a.args.every((v, i) => v === DESIRED_ENTRY.args[i]);
2531
+ }
2532
+ function mergeGhlEntry(root, force) {
2533
+ const existing = root.mcpServers;
2534
+ if (existing !== void 0 && !isPlainObject(existing)) {
2535
+ throw wrongShapeStop();
2536
+ }
2537
+ const servers = isPlainObject(existing) ? existing : {};
2538
+ const keptServers = Object.keys(servers).filter((k) => k !== SERVER_KEY);
2539
+ let action;
2540
+ if (SERVER_KEY in servers) {
2541
+ if (entriesEqual(servers[SERVER_KEY])) {
2542
+ action = "identical";
2543
+ } else if (!force) {
2544
+ throw new InstallStop(
2545
+ EXIT_REFUSED,
2546
+ "A different GHL Command entry is already in this file. Run again with --force to replace it, or leave it as is. Nothing was changed."
2547
+ );
2548
+ } else {
2549
+ action = "replaced";
2550
+ }
2551
+ } else {
2552
+ action = "added";
2553
+ }
2554
+ servers[SERVER_KEY] = { command: DESIRED_ENTRY.command, args: [...DESIRED_ENTRY.args] };
2555
+ root.mcpServers = servers;
2556
+ return { merged: root, keptServers, action };
2557
+ }
2558
+ function backupStamp(now) {
2559
+ const p = (n, w = 2) => String(n).padStart(w, "0");
2560
+ return `${now.getFullYear()}${p(now.getMonth() + 1)}${p(now.getDate())}-${p(now.getHours())}${p(now.getMinutes())}${p(now.getSeconds())}`;
2561
+ }
2562
+ function chooseBackupPath(configPath, now = /* @__PURE__ */ new Date()) {
2563
+ const base = `${configPath}.bak-${backupStamp(now)}`;
2564
+ if (!fs9.existsSync(base)) return base;
2565
+ for (let i = 2; ; i++) {
2566
+ const candidate = `${base}-${i}`;
2567
+ if (!fs9.existsSync(candidate)) return candidate;
2568
+ }
2569
+ }
2570
+ function writeVerifiedBackup(configPath, originalBytes) {
2571
+ const backupPath = chooseBackupPath(configPath);
2572
+ try {
2573
+ fs9.writeFileSync(backupPath, originalBytes);
2574
+ const readBack = fs9.readFileSync(backupPath);
2575
+ if (!readBack.equals(originalBytes)) {
2576
+ throw new Error("backup read-back did not match");
2577
+ }
2578
+ } catch (e) {
2579
+ try {
2580
+ fs9.rmSync(backupPath, { force: true });
2581
+ } catch {
2582
+ }
2583
+ throw new InstallStop(
2584
+ EXIT_ABORTED,
2585
+ `A safety copy of your settings could not be saved, so nothing was changed. (${e instanceof Error ? e.message : String(e)})`
2586
+ );
2587
+ }
2588
+ return backupPath;
2589
+ }
2590
+ function sha2562(bytes) {
2591
+ return (0, import_node_crypto3.createHash)("sha256").update(bytes).digest("hex");
2592
+ }
2593
+ function baselineOf(bytes, stat) {
2594
+ return { size: stat.size, mtimeMs: stat.mtimeMs, hash: sha2562(bytes) };
2595
+ }
2596
+ function assertNotStale(configPath, baseline) {
2597
+ let ok = false;
2598
+ try {
2599
+ const st = fs9.statSync(configPath);
2600
+ if (st.size === baseline.size && st.mtimeMs === baseline.mtimeMs) {
2601
+ ok = sha2562(fs9.readFileSync(configPath)) === baseline.hash;
2602
+ } else {
2603
+ ok = false;
2604
+ }
2605
+ } catch {
2606
+ ok = false;
2607
+ }
2608
+ if (!ok) {
2609
+ throw new InstallStop(
2610
+ EXIT_ABORTED,
2611
+ "Your settings file changed while this command was running, so it stopped without touching it. Run the command again."
2612
+ );
2613
+ }
2614
+ }
2615
+ async function atomicReplace(opts) {
2616
+ const dir = path8.dirname(opts.configPath);
2617
+ const tempPath = path8.join(dir, `.${path8.basename(opts.configPath)}.tmp-${process.pid}-${(0, import_node_crypto3.randomBytes)(4).toString("hex")}`);
2618
+ const fd = fs9.openSync(tempPath, "w");
2619
+ try {
2620
+ fs9.writeFileSync(fd, opts.newBytes);
2621
+ fs9.fsyncSync(fd);
2622
+ } finally {
2623
+ fs9.closeSync(fd);
2624
+ }
2625
+ try {
2626
+ for (let attempt = 0; ; attempt++) {
2627
+ if (opts.baseline) assertNotStale(opts.configPath, opts.baseline);
2628
+ try {
2629
+ opts.io.rename(tempPath, opts.configPath);
2630
+ break;
2631
+ } catch (e) {
2632
+ const code = e.code ?? "";
2633
+ const retryable = LOCK_ERROR_CODES.has(code) && attempt < RENAME_RETRY_DELAYS_MS.length;
2634
+ if (!retryable) {
2635
+ if (LOCK_ERROR_CODES.has(code)) {
2636
+ throw new InstallStop(
2637
+ EXIT_ABORTED,
2638
+ "Claude is holding this file. Quit Claude completely (right-click the Claude icon by your clock and choose Quit \u2014 on Mac press Cmd+Q), then run this command again. Nothing was changed."
2639
+ );
2640
+ }
2641
+ throw e;
2642
+ }
2643
+ await opts.io.sleep(RENAME_RETRY_DELAYS_MS[attempt]);
2644
+ }
2645
+ }
2646
+ } catch (e) {
2647
+ try {
2648
+ fs9.rmSync(tempPath, { force: true });
2649
+ } catch {
2650
+ }
2651
+ throw e;
2652
+ }
2653
+ try {
2654
+ const dirFd = fs9.openSync(dir, "r");
2655
+ try {
2656
+ fs9.fsyncSync(dirFd);
2657
+ } finally {
2658
+ fs9.closeSync(dirFd);
2659
+ }
2660
+ } catch {
2661
+ }
2662
+ }
2663
+ function successReport(io, args) {
2664
+ io.out("Added GHL Command to Claude Desktop.");
2665
+ io.out("");
2666
+ io.out(` Settings file: ${args.configPath}`);
2667
+ if (args.backupPath) io.out(` Backup saved: ${args.backupPath}`);
2668
+ if (args.tier !== 3 && args.keptServers.length > 0) {
2669
+ io.out(` Kept your other tools: ${args.keptServers.join(", ")}`);
2670
+ }
2671
+ if (args.tier === 2) {
2672
+ io.out("");
2673
+ io.out(`Your settings file was rewritten in a cleaner format. The original is saved at ${args.backupPath}.`);
2674
+ }
2675
+ if (args.tier === 3 && args.hadOriginal) {
2676
+ io.out("");
2677
+ io.out(
2678
+ `GHL Command is ready. If you had other tools connected before, reply to your setup email and we'll reconnect them \u2014 your previous settings are saved at ${args.backupPath}.`
2679
+ );
2680
+ }
2681
+ io.out("");
2682
+ io.out("NEXT STEP \u2014 quit Claude completely, not just the window:");
2683
+ io.out(" Windows: right-click the Claude icon by the clock, choose Quit");
2684
+ io.out(" Mac: Cmd+Q");
2685
+ io.out("Then reopen Claude and run setup with your license key.");
2686
+ }
2687
+ async function runInstall(argv, ioOverride) {
2688
+ const io = { ...defaultIO(), ...ioOverride };
2689
+ let flags;
2690
+ try {
2691
+ flags = (0, import_node_util.parseArgs)({
2692
+ args: argv,
2693
+ options: {
2694
+ "dry-run": { type: "boolean" },
2695
+ "print-only": { type: "boolean" },
2696
+ path: { type: "string" },
2697
+ force: { type: "boolean" }
2698
+ },
2699
+ strict: true,
2700
+ allowPositionals: false
2701
+ }).values;
2702
+ } catch (e) {
2703
+ io.err(e instanceof Error ? e.message : String(e));
2704
+ io.err("Usage: ghl-mcp cli install [--dry-run] [--print-only] [--path <file>] [--force]");
2705
+ return EXIT_USAGE;
2706
+ }
2707
+ try {
2708
+ const resolved2 = resolveConfigPath(flags.path);
2709
+ const configPath = resolveSymlinkPolicy(resolved2.configPath, flags.path !== void 0);
2710
+ const exists = fs9.existsSync(configPath);
2711
+ let read = null;
2712
+ let outcome;
2713
+ if (exists) {
2714
+ read = readConfigBytes(configPath);
2715
+ outcome = parseTiered(read.text);
2716
+ } else {
2717
+ outcome = { tier: 1, root: {} };
2718
+ }
2719
+ const { merged, keptServers, action } = outcome.tier === 3 ? { merged: { mcpServers: { [SERVER_KEY]: { command: DESIRED_ENTRY.command, args: [...DESIRED_ENTRY.args] } } }, keptServers: [], action: "added" } : mergeGhlEntry(outcome.root, flags.force === true);
2720
+ if (action === "identical") {
2721
+ io.out("GHL Command is already set up in Claude Desktop \u2014 nothing to do.");
2722
+ return EXIT_OK;
2723
+ }
2724
+ const newText = JSON.stringify(merged, null, 2) + "\n";
2725
+ const newBytes = read?.hadBom ? Buffer.concat([BOM_UTF8, Buffer.from(newText, "utf8")]) : Buffer.from(newText, "utf8");
2726
+ if (flags["print-only"]) {
2727
+ io.out(newText.trimEnd());
2728
+ return EXIT_OK;
2729
+ }
2730
+ if (flags["dry-run"]) {
2731
+ io.out(`Would edit: ${configPath}${exists ? "" : " (new file)"}`);
2732
+ io.out(`Would ${action === "replaced" ? "replace" : "add"} the "${SERVER_KEY}" entry.`);
2733
+ if (keptServers.length > 0) io.out(`Would keep: ${keptServers.join(", ")}`);
2734
+ if (exists) io.out(`Would back up: ${chooseBackupPath(configPath)}`);
2735
+ if (outcome.tier === 2) io.out("Would rewrite the file in a cleaner format (original kept as the backup).");
2736
+ if (outcome.tier === 3) io.out("Would write a fresh settings file (original kept as the backup).");
2737
+ return EXIT_OK;
2738
+ }
2739
+ const backupPath = exists && read ? writeVerifiedBackup(configPath, read.bytes) : null;
2740
+ if (!exists) fs9.mkdirSync(path8.dirname(configPath), { recursive: true });
2741
+ await atomicReplace({
2742
+ configPath,
2743
+ newBytes,
2744
+ baseline: exists && read ? baselineOf(read.bytes, read.stat) : null,
2745
+ io
2746
+ });
2747
+ successReport(io, { configPath, backupPath, keptServers, tier: outcome.tier, hadOriginal: exists });
2748
+ return EXIT_OK;
2749
+ } catch (e) {
2750
+ if (e instanceof InstallStop) {
2751
+ io.err(e.message);
2752
+ return e.exitCode;
2753
+ }
2754
+ io.err(`Something went wrong and nothing was changed: ${e instanceof Error ? e.message : String(e)}`);
2755
+ return EXIT_ABORTED;
2756
+ }
2757
+ }
2758
+ var fs9, os4, path8, import_node_crypto3, import_node_util, import_json5, SERVER_KEY, DESIRED_ENTRY, EXIT_OK, EXIT_USAGE, EXIT_REFUSED, EXIT_ABORTED, RENAME_RETRY_DELAYS_MS, LOCK_ERROR_CODES, BOM_UTF8, InstallStop;
2759
+ var init_config_installer = __esm({
2760
+ "src/config-installer.ts"() {
2761
+ "use strict";
2762
+ fs9 = __toESM(require("node:fs"));
2763
+ os4 = __toESM(require("node:os"));
2764
+ path8 = __toESM(require("node:path"));
2765
+ import_node_crypto3 = require("node:crypto");
2766
+ import_node_util = require("node:util");
2767
+ import_json5 = __toESM(require("json5"));
2768
+ SERVER_KEY = "ghl";
2769
+ DESIRED_ENTRY = Object.freeze({
2770
+ command: "npx",
2771
+ args: Object.freeze(["-y", "@elitedcs/ghl-mcp@latest"])
2772
+ });
2773
+ EXIT_OK = 0;
2774
+ EXIT_USAGE = 2;
2775
+ EXIT_REFUSED = 3;
2776
+ EXIT_ABORTED = 4;
2777
+ RENAME_RETRY_DELAYS_MS = [200, 500, 1e3];
2778
+ LOCK_ERROR_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES"]);
2779
+ BOM_UTF8 = Buffer.from([239, 187, 191]);
2780
+ InstallStop = class extends Error {
2781
+ constructor(exitCode, message) {
2782
+ super(message);
2783
+ this.exitCode = exitCode;
2784
+ }
2785
+ exitCode;
2786
+ };
2787
+ }
2788
+ });
2789
+
2393
2790
  // package.json
2394
2791
  var require_package = __commonJS({
2395
2792
  "package.json"(exports2, module2) {
2396
2793
  module2.exports = {
2397
2794
  name: "@elitedcs/ghl-mcp",
2398
- version: "3.58.0",
2795
+ version: "3.59.0",
2399
2796
  mcpName: "io.github.drjerryrelth/ghl-command",
2400
2797
  description: "GoHighLevel MCP Server for Claude. 233 tools \u2014 full CRM, automation, marketing control, account-wide workflow audit, live funnel-capture verification, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
2401
2798
  main: "dist/index.js",
@@ -2458,6 +2855,7 @@ var require_package = __commonJS({
2458
2855
  dependencies: {
2459
2856
  "@modelcontextprotocol/sdk": "^1.29.0",
2460
2857
  dotenv: "^16.5.0",
2858
+ json5: "^2.2.3",
2461
2859
  "playwright-core": "^1.61.1",
2462
2860
  zod: "^3.24.4"
2463
2861
  },
@@ -2485,7 +2883,7 @@ function launchCommand(port = 7300) {
2485
2883
  return `npx -y @elitedcs/ghl-mcp@latest dashboard --port=${port}`;
2486
2884
  }
2487
2885
  function installingEntry() {
2488
- return path9.join(__dirname, "index.js");
2886
+ return path10.join(__dirname, "index.js");
2489
2887
  }
2490
2888
  function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
2491
2889
  const cmd = launchCommand(port);
@@ -2493,12 +2891,12 @@ function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
2493
2891
  const systemApps = "/Applications";
2494
2892
  let base = home;
2495
2893
  try {
2496
- fs10.accessSync(systemApps, fs10.constants.W_OK);
2894
+ fs11.accessSync(systemApps, fs11.constants.W_OK);
2497
2895
  base = "";
2498
2896
  } catch {
2499
2897
  }
2500
- const appDir = base ? path9.join(home, "Applications", "Command OS.app") : path9.join(systemApps, "Command OS.app");
2501
- const macOSDir = path9.join(appDir, "Contents", "MacOS");
2898
+ const appDir = base ? path10.join(home, "Applications", "Command OS.app") : path10.join(systemApps, "Command OS.app");
2899
+ const macOSDir = path10.join(appDir, "Contents", "MacOS");
2502
2900
  const script = [
2503
2901
  "#!/bin/bash",
2504
2902
  "# GHL Command \u2014 Command OS launcher (regenerate: ghl-mcp install-launcher)",
@@ -2527,13 +2925,13 @@ function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
2527
2925
  targetPath: appDir,
2528
2926
  humanLocation: base ? "your personal Applications folder at ~/Applications (Finder \u2192 Go \u2192 Home \u2192 Applications)" : "your Applications folder \u2014 open Finder \u2192 Applications and look for \u201CCommand OS\u201D",
2529
2927
  files: [
2530
- { path: path9.join(macOSDir, "command-os"), contents: script, executable: true },
2531
- { path: path9.join(appDir, "Contents", "Info.plist"), contents: plist, executable: false }
2928
+ { path: path10.join(macOSDir, "command-os"), contents: script, executable: true },
2929
+ { path: path10.join(appDir, "Contents", "Info.plist"), contents: plist, executable: false }
2532
2930
  ]
2533
2931
  };
2534
2932
  }
2535
2933
  if (platform2 === "win32") {
2536
- const target2 = path9.join(home, "Desktop", "Command OS.cmd");
2934
+ const target2 = path10.join(home, "Desktop", "Command OS.cmd");
2537
2935
  return {
2538
2936
  platform: platform2,
2539
2937
  targetPath: target2,
@@ -2546,7 +2944,7 @@ function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
2546
2944
  ].join("\r\n"), executable: false }]
2547
2945
  };
2548
2946
  }
2549
- const target = path9.join(home, ".local", "share", "applications", "command-os.desktop");
2947
+ const target = path10.join(home, ".local", "share", "applications", "command-os.desktop");
2550
2948
  return {
2551
2949
  platform: platform2,
2552
2950
  targetPath: target,
@@ -2570,11 +2968,11 @@ function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
2570
2968
  function installLauncher(argv = []) {
2571
2969
  const portArg = argv.find((a) => a.startsWith("--port="));
2572
2970
  const port = portArg ? Number(portArg.split("=")[1]) : 7300;
2573
- const plan = planLauncher(process.platform, os4.homedir(), port, installingEntry());
2971
+ const plan = planLauncher(process.platform, os5.homedir(), port, installingEntry());
2574
2972
  try {
2575
2973
  for (const f of plan.files) {
2576
- fs10.mkdirSync(path9.dirname(f.path), { recursive: true });
2577
- fs10.writeFileSync(f.path, f.contents, { mode: f.executable ? 493 : 420 });
2974
+ fs11.mkdirSync(path10.dirname(f.path), { recursive: true });
2975
+ fs11.writeFileSync(f.path, f.contents, { mode: f.executable ? 493 : 420 });
2578
2976
  }
2579
2977
  } catch (e) {
2580
2978
  process.stderr.write(`
@@ -2594,13 +2992,13 @@ function installLauncher(argv = []) {
2594
2992
  ].join("\n"));
2595
2993
  return 0;
2596
2994
  }
2597
- var fs10, os4, path9;
2995
+ var fs11, os5, path10;
2598
2996
  var init_launcher = __esm({
2599
2997
  "src/launcher.ts"() {
2600
2998
  "use strict";
2601
- fs10 = __toESM(require("fs"));
2602
- os4 = __toESM(require("os"));
2603
- path9 = __toESM(require("path"));
2999
+ fs11 = __toESM(require("fs"));
3000
+ os5 = __toESM(require("os"));
3001
+ path10 = __toESM(require("path"));
2604
3002
  }
2605
3003
  });
2606
3004
 
@@ -2629,10 +3027,10 @@ function stageBlockedBy(stages, stage) {
2629
3027
  return null;
2630
3028
  }
2631
3029
  function claudeBin() {
2632
- const fallback = path10.join(os5.homedir(), ".local", "bin", "claude");
2633
- const fromPath = (process.env.PATH || "").split(path10.delimiter).map((d) => path10.join(d, "claude")).find((p) => {
3030
+ const fallback = path11.join(os6.homedir(), ".local", "bin", "claude");
3031
+ const fromPath = (process.env.PATH || "").split(path11.delimiter).map((d) => path11.join(d, "claude")).find((p) => {
2634
3032
  try {
2635
- fs11.accessSync(p, fs11.constants.X_OK);
3033
+ fs12.accessSync(p, fs12.constants.X_OK);
2636
3034
  return true;
2637
3035
  } catch {
2638
3036
  return false;
@@ -2640,7 +3038,7 @@ function claudeBin() {
2640
3038
  });
2641
3039
  if (fromPath) return fromPath;
2642
3040
  try {
2643
- fs11.accessSync(fallback, fs11.constants.X_OK);
3041
+ fs12.accessSync(fallback, fs12.constants.X_OK);
2644
3042
  return fallback;
2645
3043
  } catch {
2646
3044
  }
@@ -2689,7 +3087,7 @@ function runStage(locationId2, stage, onEvent, opts = {}) {
2689
3087
  const locationName = SANDBOX_ALLOWLIST[locationId2] || opts.name || locationId2;
2690
3088
  const spec = STAGE_SPECS[stage];
2691
3089
  if (!spec) return Promise.reject(new Error(`Stage ${stage} is not powered yet.`));
2692
- return new Promise((resolve6) => {
3090
+ return new Promise((resolve7) => {
2693
3091
  const bin = claudeBin();
2694
3092
  onEvent({ kind: "status", line: `Starting ${spec.name} on ${locationName} (headless Claude, ${spec.allowedTools.length} tools allowed)\u2026` });
2695
3093
  const child = (0, import_child_process2.spawn)(bin, buildClaudeArgs(spec, locationId2, locationName, opts.context), {
@@ -2735,31 +3133,31 @@ function runStage(locationId2, stage, onEvent, opts = {}) {
2735
3133
  if (code !== 0 && !lastText) {
2736
3134
  const outcome2 = { ok: false, error: `claude exited ${code}: ${stderrTail.trim().slice(-200) || "no output"}` };
2737
3135
  onEvent({ kind: "error", line: outcome2.error });
2738
- resolve6(outcome2);
3136
+ resolve7(outcome2);
2739
3137
  return;
2740
3138
  }
2741
3139
  const outcome = parseResultLine(lastText);
2742
3140
  const detail = outcome.error || (outcome.issues?.length ? outcome.issues.join(" \xB7 ") : "") || outcome.summary || "no detail reported";
2743
3141
  const success = outcome.formId ? `Done: form "${outcome.formName}" (${outcome.formId})` : `Done: ${outcome.summary ?? "stage complete"}`;
2744
3142
  onEvent({ kind: "result", line: outcome.ok ? success : `Not passed: ${detail}` });
2745
- resolve6(outcome);
3143
+ resolve7(outcome);
2746
3144
  });
2747
3145
  child.on("error", (err) => {
2748
3146
  clearTimeout(timeout);
2749
3147
  const outcome = { ok: false, error: `could not start claude: ${err.message}` };
2750
3148
  onEvent({ kind: "error", line: outcome.error });
2751
- resolve6(outcome);
3149
+ resolve7(outcome);
2752
3150
  });
2753
3151
  });
2754
3152
  }
2755
- var import_child_process2, fs11, os5, path10, SANDBOX_ALLOWLIST, PROTECTED_LOCATIONS, STAGE_SPECS, HUMAN_GATE_STAGES;
3153
+ var import_child_process2, fs12, os6, path11, SANDBOX_ALLOWLIST, PROTECTED_LOCATIONS, STAGE_SPECS, HUMAN_GATE_STAGES;
2756
3154
  var init_stage_runner = __esm({
2757
3155
  "src/stage-runner.ts"() {
2758
3156
  "use strict";
2759
3157
  import_child_process2 = require("child_process");
2760
- fs11 = __toESM(require("fs"));
2761
- os5 = __toESM(require("os"));
2762
- path10 = __toESM(require("path"));
3158
+ fs12 = __toESM(require("fs"));
3159
+ os6 = __toESM(require("os"));
3160
+ path11 = __toESM(require("path"));
2763
3161
  SANDBOX_ALLOWLIST = {
2764
3162
  JrV2p35O3hY2wqhr2c0T: "MCP Testing",
2765
3163
  jHP5wkYRineXDlzAOEbW: "Blueprint Demo"
@@ -3479,11 +3877,11 @@ __export(dashboard_exports, {
3479
3877
  writeOverlay: () => writeOverlay
3480
3878
  });
3481
3879
  function overlayPath() {
3482
- return path11.join(appDataDir(), "intake-overlay.json");
3880
+ return path12.join(appDataDir(), "intake-overlay.json");
3483
3881
  }
3484
3882
  function readOverlay() {
3485
3883
  try {
3486
- const raw = JSON.parse(fs12.readFileSync(overlayPath(), "utf8"));
3884
+ const raw = JSON.parse(fs13.readFileSync(overlayPath(), "utf8"));
3487
3885
  if (raw && typeof raw === "object") return raw;
3488
3886
  } catch {
3489
3887
  }
@@ -3491,7 +3889,7 @@ function readOverlay() {
3491
3889
  }
3492
3890
  function writeOverlay(layer) {
3493
3891
  ensureAppDataDir();
3494
- fs12.writeFileSync(overlayPath(), JSON.stringify(layer, null, 2), { mode: 384 });
3892
+ fs13.writeFileSync(overlayPath(), JSON.stringify(layer, null, 2), { mode: 384 });
3495
3893
  }
3496
3894
  function recoverStuckStages(state) {
3497
3895
  let recovered = 0;
@@ -3503,11 +3901,11 @@ function recoverStuckStages(state) {
3503
3901
  return { state: { ...state, clients }, recovered };
3504
3902
  }
3505
3903
  function cockpitStatePath() {
3506
- return path11.join(appDataDir(), "cockpit-state.json");
3904
+ return path12.join(appDataDir(), "cockpit-state.json");
3507
3905
  }
3508
3906
  function readCockpitState() {
3509
3907
  try {
3510
- const raw = JSON.parse(fs12.readFileSync(cockpitStatePath(), "utf8"));
3908
+ const raw = JSON.parse(fs13.readFileSync(cockpitStatePath(), "utf8"));
3511
3909
  if (raw && raw.v === 1 && raw.clients && typeof raw.clients === "object") return raw;
3512
3910
  } catch {
3513
3911
  }
@@ -3515,7 +3913,7 @@ function readCockpitState() {
3515
3913
  }
3516
3914
  function writeCockpitState(state) {
3517
3915
  ensureAppDataDir();
3518
- fs12.writeFileSync(cockpitStatePath(), JSON.stringify(state, null, 2), { mode: 384 });
3916
+ fs13.writeFileSync(cockpitStatePath(), JSON.stringify(state, null, 2), { mode: 384 });
3519
3917
  }
3520
3918
  function setStage(state, locationId2, stageIndex, status) {
3521
3919
  if (!Number.isInteger(stageIndex) || stageIndex < 0 || stageIndex >= STAGES.length) throw new Error("bad stage index");
@@ -3607,7 +4005,7 @@ function notify(message) {
3607
4005
  `);
3608
4006
  try {
3609
4007
  ensureAppDataDir();
3610
- fs12.appendFileSync(path11.join(appDataDir(), "cockpit.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
4008
+ fs13.appendFileSync(path12.join(appDataDir(), "cockpit.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
3611
4009
  `);
3612
4010
  } catch {
3613
4011
  }
@@ -4308,17 +4706,17 @@ async function runDashboard(argv) {
4308
4706
  const source = args.url ? { kind: "url", url: String(args.url) } : args.instruction ? { kind: "recorder", instruction: String(args.instruction) } : { kind: "text", text: String(args.transcript ?? "") };
4309
4707
  let recorderPatterns = [];
4310
4708
  if (source.kind === "recorder") {
4311
- recorderPatterns = await new Promise((resolve6) => {
4709
+ recorderPatterns = await new Promise((resolve7) => {
4312
4710
  const ls = (0, import_child_process4.spawn)(claudeBin(), ["mcp", "list"], { stdio: ["ignore", "pipe", "ignore"] });
4313
4711
  let buf = "";
4314
4712
  ls.stdout.on("data", (d) => {
4315
4713
  buf += d.toString();
4316
4714
  });
4317
- ls.on("close", () => resolve6(recorderAllowPatterns(buf)));
4318
- ls.on("error", () => resolve6([]));
4715
+ ls.on("close", () => resolve7(recorderAllowPatterns(buf)));
4716
+ ls.on("error", () => resolve7([]));
4319
4717
  setTimeout(() => {
4320
4718
  ls.kill("SIGKILL");
4321
- resolve6(recorderAllowPatterns(buf));
4719
+ resolve7(recorderAllowPatterns(buf));
4322
4720
  }, 9e4).unref?.();
4323
4721
  });
4324
4722
  if (!recorderPatterns.length) {
@@ -4328,7 +4726,7 @@ async function runDashboard(argv) {
4328
4726
  }
4329
4727
  }
4330
4728
  const out = await prefillFromSource(
4331
- (prompt, tools) => new Promise((resolve6, reject) => {
4729
+ (prompt, tools) => new Promise((resolve7, reject) => {
4332
4730
  const argv2 = ["-p", prompt, "--max-turns", "25", "--disallowedTools", tools.deny];
4333
4731
  if (tools.allow) argv2.push("--allowedTools", tools.allow);
4334
4732
  else argv2.push("--allowedTools", "");
@@ -4337,7 +4735,7 @@ async function runDashboard(argv) {
4337
4735
  child.stdout.on("data", (d) => {
4338
4736
  out2 += d.toString();
4339
4737
  });
4340
- child.on("close", () => resolve6(out2));
4738
+ child.on("close", () => resolve7(out2));
4341
4739
  child.on("error", reject);
4342
4740
  setTimeout(() => child.kill("SIGKILL"), 5 * 60 * 1e3).unref?.();
4343
4741
  }),
@@ -4469,13 +4867,13 @@ async function runDashboard(argv) {
4469
4867
  }
4470
4868
  }
4471
4869
  const review = await generateReview(
4472
- (prompt) => new Promise((resolve6, reject) => {
4870
+ (prompt) => new Promise((resolve7, reject) => {
4473
4871
  const child = (0, import_child_process4.spawn)(claudeBin(), ["-p", prompt, "--allowedTools", "", "--disallowedTools", "Bash,Write,Edit,WebFetch,WebSearch,Task,Read,Glob,Grep", "--max-turns", "6"], { stdio: ["ignore", "pipe", "pipe"] });
4474
4872
  let out = "";
4475
4873
  child.stdout.on("data", (d) => {
4476
4874
  out += d.toString();
4477
4875
  });
4478
- child.on("close", () => resolve6(out));
4876
+ child.on("close", () => resolve7(out));
4479
4877
  child.on("error", reject);
4480
4878
  setTimeout(() => child.kill("SIGKILL"), 6 * 60 * 1e3).unref?.();
4481
4879
  }),
@@ -4648,9 +5046,9 @@ async function runDashboard(argv) {
4648
5046
  res.end();
4649
5047
  });
4650
5048
  try {
4651
- await new Promise((resolve6, reject) => {
5049
+ await new Promise((resolve7, reject) => {
4652
5050
  server2.once("error", reject);
4653
- server2.listen(port, "127.0.0.1", resolve6);
5051
+ server2.listen(port, "127.0.0.1", resolve7);
4654
5052
  });
4655
5053
  } catch (e) {
4656
5054
  const msg2 = e?.code === "EADDRINUSE" ? `Port ${port} is busy with another program. Close it, or start Command OS on a different port.` : `Command OS could not start: ${e instanceof Error ? e.message : String(e)}`;
@@ -4676,18 +5074,18 @@ async function runDashboard(argv) {
4676
5074
  if (adopted) process.stderr.write(` Synced ${adopted} client${adopted === 1 ? "" : "s"} from your GHL (teammate updates).
4677
5075
  `);
4678
5076
  });
4679
- return await new Promise((resolve6) => {
4680
- const stop = () => server2.close(() => resolve6(0));
5077
+ return await new Promise((resolve7) => {
5078
+ const stop = () => server2.close(() => resolve7(0));
4681
5079
  process.on("SIGINT", stop);
4682
5080
  process.on("SIGTERM", stop);
4683
5081
  });
4684
5082
  }
4685
- var fs12, path11, http, import_child_process3, import_child_process4, osmod, STAGES, ACCOUNT_TYPES, activeRun, UPGRADE_MSG;
5083
+ var fs13, path12, http, import_child_process3, import_child_process4, osmod, STAGES, ACCOUNT_TYPES, activeRun, UPGRADE_MSG;
4686
5084
  var init_dashboard = __esm({
4687
5085
  "src/dashboard.ts"() {
4688
5086
  "use strict";
4689
- fs12 = __toESM(require("fs"));
4690
- path11 = __toESM(require("path"));
5087
+ fs13 = __toESM(require("fs"));
5088
+ path12 = __toESM(require("path"));
4691
5089
  http = __toESM(require("http"));
4692
5090
  import_child_process3 = require("child_process");
4693
5091
  init_credentials_store();
@@ -4722,8 +5120,8 @@ var init_dashboard = __esm({
4722
5120
 
4723
5121
  // src/index.ts
4724
5122
  var dotenv2 = __toESM(require("dotenv"));
4725
- var path12 = __toESM(require("path"));
4726
- var fs13 = __toESM(require("fs"));
5123
+ var path13 = __toESM(require("path"));
5124
+ var fs14 = __toESM(require("fs"));
4727
5125
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
4728
5126
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
4729
5127
  init_ghl_client();
@@ -8758,16 +9156,16 @@ function registerEmailTools(server2, client) {
8758
9156
  function registerEmailBuilderInternalTools(server2, builderClient) {
8759
9157
  const client = builderClient;
8760
9158
  if (!client) return;
8761
- async function builderRequest(method, path13, body) {
9159
+ async function builderRequest(method, path14, body) {
8762
9160
  const headers = await client.buildHeaders();
8763
- const response = await fetch(`${EMAIL_BUILDER_BASE}${path13}`, {
9161
+ const response = await fetch(`${EMAIL_BUILDER_BASE}${path14}`, {
8764
9162
  method,
8765
9163
  headers,
8766
9164
  body: body ? JSON.stringify(body) : void 0
8767
9165
  });
8768
9166
  if (!response.ok) {
8769
9167
  const text2 = await response.text();
8770
- throw new Error(`Email Builder API Error ${response.status}: ${method} /emails/builder${path13}
9168
+ throw new Error(`Email Builder API Error ${response.status}: ${method} /emails/builder${path14}
8771
9169
  ${text2}`);
8772
9170
  }
8773
9171
  const text = await response.text();
@@ -10024,23 +10422,23 @@ var import_zod36 = require("zod");
10024
10422
  function registerFunnelBuilderTools(server2, builderClient) {
10025
10423
  const client = builderClient;
10026
10424
  if (!client) return;
10027
- async function internalGet(path13) {
10028
- return client.request("GET", path13);
10425
+ async function internalGet(path14) {
10426
+ return client.request("GET", path14);
10029
10427
  }
10030
- async function internalPost(path13, body) {
10031
- return client.request("POST", path13, body);
10428
+ async function internalPost(path14, body) {
10429
+ return client.request("POST", path14, body);
10032
10430
  }
10033
- async function internalPut(path13, body) {
10034
- return client.request("PUT", path13, body);
10431
+ async function internalPut(path14, body) {
10432
+ return client.request("PUT", path14, body);
10035
10433
  }
10036
- async function internalDelete(path13) {
10037
- return client.request("DELETE", path13);
10434
+ async function internalDelete(path14) {
10435
+ return client.request("DELETE", path14);
10038
10436
  }
10039
- async function funnelRequest(method, path13, body) {
10437
+ async function funnelRequest(method, path14, body) {
10040
10438
  const headers = await client.buildHeaders();
10041
10439
  headers.Origin = "https://app.gohighlevel.com";
10042
10440
  headers.Referer = "https://app.gohighlevel.com/";
10043
- const url = `https://backend.leadconnectorhq.com/funnels${path13}`;
10441
+ const url = `https://backend.leadconnectorhq.com/funnels${path14}`;
10044
10442
  const options = { method, headers };
10045
10443
  if (body && (method === "POST" || method === "PUT")) {
10046
10444
  options.body = JSON.stringify(body);
@@ -10048,7 +10446,7 @@ function registerFunnelBuilderTools(server2, builderClient) {
10048
10446
  const response = await fetch(url, options);
10049
10447
  if (!response.ok) {
10050
10448
  const text2 = await response.text();
10051
- throw new Error(`Funnel API Error ${response.status}: ${method} ${path13}
10449
+ throw new Error(`Funnel API Error ${response.status}: ${method} ${path14}
10052
10450
  ${text2}`);
10053
10451
  }
10054
10452
  const text = await response.text();
@@ -10993,12 +11391,12 @@ var valueCardSchema = import_zod37.z.object({
10993
11391
  function registerPageStudioTools(server2, builderClient) {
10994
11392
  const client = builderClient;
10995
11393
  if (!client) return;
10996
- async function funnelRequest(method, path13) {
11394
+ async function funnelRequest(method, path14) {
10997
11395
  const headers = await client.buildHeaders();
10998
11396
  headers.Origin = "https://app.gohighlevel.com";
10999
11397
  headers.Referer = "https://app.gohighlevel.com/";
11000
- const response = await fetch(`https://backend.leadconnectorhq.com/funnels${path13}`, { method, headers });
11001
- if (!response.ok) throw new Error(`Funnel API Error ${response.status}: ${method} ${path13}
11398
+ const response = await fetch(`https://backend.leadconnectorhq.com/funnels${path14}`, { method, headers });
11399
+ if (!response.ok) throw new Error(`Funnel API Error ${response.status}: ${method} ${path14}
11002
11400
  ${await response.text()}`);
11003
11401
  const text = await response.text();
11004
11402
  return text ? JSON.parse(text) : {};
@@ -11317,9 +11715,9 @@ function buildUpdateFormPath(formId, locationId2) {
11317
11715
  function buildUpdateFormBody(name, formData) {
11318
11716
  return { name, formData };
11319
11717
  }
11320
- async function formApiRequest(client, method, path13, body) {
11718
+ async function formApiRequest(client, method, path14, body) {
11321
11719
  const headers = await client.buildHeaders();
11322
- const url = `https://backend.leadconnectorhq.com/forms${path13}`;
11720
+ const url = `https://backend.leadconnectorhq.com/forms${path14}`;
11323
11721
  const options = { method, headers };
11324
11722
  if (body && (method === "POST" || method === "PUT")) {
11325
11723
  options.body = JSON.stringify(body);
@@ -11327,7 +11725,7 @@ async function formApiRequest(client, method, path13, body) {
11327
11725
  const response = await fetch(url, options);
11328
11726
  if (!response.ok) {
11329
11727
  const text2 = await response.text();
11330
- throw new Error(`Form API Error ${response.status}: ${method} ${path13}
11728
+ throw new Error(`Form API Error ${response.status}: ${method} ${path14}
11331
11729
  ${text2}`);
11332
11730
  }
11333
11731
  const text = await response.text();
@@ -11341,7 +11739,7 @@ ${text2}`);
11341
11739
  function registerFormBuilderTools(server2, builderClient, publicClient) {
11342
11740
  const client = builderClient;
11343
11741
  if (!client) return;
11344
- const formRequest = (method, path13, body) => formApiRequest(client, method, path13, body);
11742
+ const formRequest = (method, path14, body) => formApiRequest(client, method, path14, body);
11345
11743
  server2.tool(
11346
11744
  "get_form_full",
11347
11745
  "Get a form with full builder data: all fields (labels, types, IDs, validation), conditional logic, auto-responder config, email notification settings, styling, and version history. This is the internal API \u2014 it returns everything the form builder UI shows.",
@@ -11462,10 +11860,10 @@ function registerFormBuilderTools(server2, builderClient, publicClient) {
11462
11860
  },
11463
11861
  async ({ formId, limit, skip }) => {
11464
11862
  try {
11465
- let path13 = `/submissions?locationId=${client.locationId}&limit=${limit ?? 20}`;
11466
- if (formId) path13 += `&formId=${formId}`;
11467
- if (skip) path13 += `&skip=${skip}`;
11468
- const result = await formRequest("GET", path13);
11863
+ let path14 = `/submissions?locationId=${client.locationId}&limit=${limit ?? 20}`;
11864
+ if (formId) path14 += `&formId=${formId}`;
11865
+ if (skip) path14 += `&skip=${skip}`;
11866
+ const result = await formRequest("GET", path14);
11469
11867
  return {
11470
11868
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
11471
11869
  };
@@ -11858,9 +12256,9 @@ var import_zod40 = require("zod");
11858
12256
  function registerPipelineBuilderTools(server2, builderClient) {
11859
12257
  const client = builderClient;
11860
12258
  if (!client) return;
11861
- async function pipelineRequest(method, path13, body) {
12259
+ async function pipelineRequest(method, path14, body) {
11862
12260
  const headers = await client.buildHeaders();
11863
- const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${path13}`;
12261
+ const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${path14}`;
11864
12262
  const options = { method, headers };
11865
12263
  if (body && (method === "POST" || method === "PUT" || method === "PATCH")) {
11866
12264
  options.body = JSON.stringify(body);
@@ -11868,7 +12266,7 @@ function registerPipelineBuilderTools(server2, builderClient) {
11868
12266
  const response = await fetch(url, options);
11869
12267
  if (!response.ok) {
11870
12268
  const text2 = await response.text();
11871
- throw new Error(`Pipeline API Error ${response.status}: ${method} ${path13}
12269
+ throw new Error(`Pipeline API Error ${response.status}: ${method} ${path14}
11872
12270
  ${text2}`);
11873
12271
  }
11874
12272
  const text = await response.text();
@@ -12613,7 +13011,7 @@ ${lines.join("\n")}
12613
13011
  // src/tools/bulk-operations.ts
12614
13012
  var import_zod44 = require("zod");
12615
13013
  function delay(ms) {
12616
- return new Promise((resolve6) => setTimeout(resolve6, ms));
13014
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
12617
13015
  }
12618
13016
  function formatResults(op, results, total) {
12619
13017
  return `${op}: ${results.success} success, ${results.failed} failed out of ${total}.${results.errors.length ? "\nErrors:\n" + results.errors.join("\n") : ""}`;
@@ -12738,7 +13136,7 @@ function registerBulkOperationTools(server2, client) {
12738
13136
  // src/tools/account-export.ts
12739
13137
  var import_zod45 = require("zod");
12740
13138
  function delay2(ms) {
12741
- return new Promise((resolve6) => setTimeout(resolve6, ms));
13139
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
12742
13140
  }
12743
13141
  function registerAccountExportTools(server2, client) {
12744
13142
  const builderClient = WorkflowBuilderClient.fromEnv();
@@ -13059,9 +13457,9 @@ var OBJECT_KEYS = ["contacts", "opportunity"];
13059
13457
  function registerSmartListTools(server2, builderClient) {
13060
13458
  const client = builderClient;
13061
13459
  if (!client) return;
13062
- async function smartListRequest(method, path13, body) {
13460
+ async function smartListRequest(method, path14, body) {
13063
13461
  const headers = await client.buildHeaders();
13064
- const url = `${SMARTLIST_BASE}${path13}`;
13462
+ const url = `${SMARTLIST_BASE}${path14}`;
13065
13463
  const options = { method, headers };
13066
13464
  if (body && (method === "POST" || method === "PUT")) {
13067
13465
  options.body = JSON.stringify(body);
@@ -13069,7 +13467,7 @@ function registerSmartListTools(server2, builderClient) {
13069
13467
  const response = await fetch(url, options);
13070
13468
  if (!response.ok) {
13071
13469
  const text2 = await response.text();
13072
- throw new Error(`Smart Lists API Error ${response.status}: ${method} ${path13}
13470
+ throw new Error(`Smart Lists API Error ${response.status}: ${method} ${path14}
13073
13471
  ${text2}`);
13074
13472
  }
13075
13473
  const text = await response.text();
@@ -13201,12 +13599,12 @@ var REPUTATION_BASE = "https://backend.leadconnectorhq.com/reputation";
13201
13599
  function registerReputationTools(server2, builderClient) {
13202
13600
  const client = builderClient;
13203
13601
  if (!client) return;
13204
- async function reputationRequest(method, path13) {
13602
+ async function reputationRequest(method, path14) {
13205
13603
  const headers = await client.buildHeaders();
13206
- const response = await fetch(`${REPUTATION_BASE}${path13}`, { method, headers });
13604
+ const response = await fetch(`${REPUTATION_BASE}${path14}`, { method, headers });
13207
13605
  if (!response.ok) {
13208
13606
  const text2 = await response.text();
13209
- throw new Error(`Reputation API Error ${response.status}: ${method} ${path13}
13607
+ throw new Error(`Reputation API Error ${response.status}: ${method} ${path14}
13210
13608
  ${text2}`);
13211
13609
  }
13212
13610
  const text = await response.text();
@@ -13321,16 +13719,16 @@ var MEMBERSHIP_BASE = "https://backend.leadconnectorhq.com/membership";
13321
13719
  function registerMembershipTools(server2, builderClient) {
13322
13720
  const client = builderClient;
13323
13721
  if (!client) return;
13324
- async function membershipRequest(path13, method = "GET", body) {
13722
+ async function membershipRequest(path14, method = "GET", body) {
13325
13723
  const headers = await client.buildHeaders();
13326
- const response = await fetch(`${MEMBERSHIP_BASE}${path13}`, {
13724
+ const response = await fetch(`${MEMBERSHIP_BASE}${path14}`, {
13327
13725
  method,
13328
13726
  headers,
13329
13727
  body: body ? JSON.stringify(body) : void 0
13330
13728
  });
13331
13729
  if (!response.ok) {
13332
13730
  const text2 = await response.text();
13333
- throw new Error(`Membership API Error ${response.status}: ${method} ${path13}
13731
+ throw new Error(`Membership API Error ${response.status}: ${method} ${path14}
13334
13732
  ${text2}`);
13335
13733
  }
13336
13734
  const text = await response.text();
@@ -13499,7 +13897,7 @@ var import_zod51 = require("zod");
13499
13897
  var fs7 = __toESM(require("fs"));
13500
13898
  var path6 = __toESM(require("path"));
13501
13899
  function delay3(ms) {
13502
- return new Promise((resolve6) => setTimeout(resolve6, ms));
13900
+ return new Promise((resolve7) => setTimeout(resolve7, ms));
13503
13901
  }
13504
13902
  var TemplateSchema = import_zod51.z.object({
13505
13903
  templateName: import_zod51.z.string(),
@@ -13643,7 +14041,7 @@ function registerTemplateDeployerTools(server2, client) {
13643
14041
  const locId = client.resolveLocationId(locationId2);
13644
14042
  const safePath = validateTemplatePath(templateFile);
13645
14043
  const template = TemplateSchema.parse(JSON.parse(fs7.readFileSync(safePath, "utf-8")));
13646
- const resolve6 = (text) => {
14044
+ const resolve7 = (text) => {
13647
14045
  if (typeof text !== "string") return text;
13648
14046
  let result = text;
13649
14047
  for (const [key, value] of Object.entries(answers)) {
@@ -13656,7 +14054,7 @@ function registerTemplateDeployerTools(server2, client) {
13656
14054
  return result;
13657
14055
  };
13658
14056
  const resolveObj = (obj) => {
13659
- if (typeof obj === "string") return resolve6(obj);
14057
+ if (typeof obj === "string") return resolve7(obj);
13660
14058
  if (Array.isArray(obj)) return obj.map(resolveObj);
13661
14059
  if (obj && typeof obj === "object") {
13662
14060
  const result = {};
@@ -15317,9 +15715,9 @@ function presetForBusinessType(type) {
15317
15715
  return "generic";
15318
15716
  }
15319
15717
  }
15320
- function setPath(target, path13, value) {
15718
+ function setPath(target, path14, value) {
15321
15719
  if (value === void 0) return;
15322
- const parts = path13.split(".");
15720
+ const parts = path14.split(".");
15323
15721
  let node = target;
15324
15722
  for (let i = 0; i < parts.length - 1; i++) {
15325
15723
  const k = parts[i];
@@ -17405,15 +17803,15 @@ function extractFunnelId(result) {
17405
17803
  return void 0;
17406
17804
  }
17407
17805
  function makeExecuteDeps(client, builderClient, locationId2) {
17408
- const pipelineApi = async (method, path13, body) => {
17806
+ const pipelineApi = async (method, path14, body) => {
17409
17807
  const headers = await builderClient.buildHeaders();
17410
- const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${path13}`;
17808
+ const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${path14}`;
17411
17809
  const options = { method, headers };
17412
17810
  if (body && (method === "POST" || method === "PUT")) options.body = JSON.stringify(body);
17413
17811
  const response = await fetch(url, options);
17414
17812
  if (!response.ok) {
17415
17813
  const text2 = await response.text();
17416
- throw new Error(`Pipeline API ${response.status}: ${method} ${path13}
17814
+ throw new Error(`Pipeline API ${response.status}: ${method} ${path14}
17417
17815
  ${text2.slice(0, 300)}`);
17418
17816
  }
17419
17817
  const text = await response.text();
@@ -17424,17 +17822,17 @@ ${text2.slice(0, 300)}`);
17424
17822
  return JSON.parse(text.replace(/[\x00-\x1F\x7F]/g, ""));
17425
17823
  }
17426
17824
  };
17427
- const funnelApi = async (method, path13, body) => {
17825
+ const funnelApi = async (method, path14, body) => {
17428
17826
  const headers = await builderClient.buildHeaders();
17429
17827
  headers.Origin = "https://app.gohighlevel.com";
17430
17828
  headers.Referer = "https://app.gohighlevel.com/";
17431
- const url = `https://backend.leadconnectorhq.com/funnels${path13}`;
17829
+ const url = `https://backend.leadconnectorhq.com/funnels${path14}`;
17432
17830
  const options = { method, headers };
17433
17831
  if (body && (method === "POST" || method === "PUT")) options.body = JSON.stringify(body);
17434
17832
  const response = await fetch(url, options);
17435
17833
  if (!response.ok) {
17436
17834
  const text2 = await response.text();
17437
- throw new Error(`Funnel API ${response.status}: ${method} ${path13}
17835
+ throw new Error(`Funnel API ${response.status}: ${method} ${path14}
17438
17836
  ${text2.slice(0, 300)}`);
17439
17837
  }
17440
17838
  const text = await response.text();
@@ -18351,21 +18749,29 @@ function registerMetaTools(server2, installedVersion) {
18351
18749
  }
18352
18750
 
18353
18751
  // src/cli.ts
18354
- var import_node_util = require("node:util");
18355
- var fs9 = __toESM(require("fs"));
18356
- var path8 = __toESM(require("path"));
18752
+ var import_node_util2 = require("node:util");
18753
+ var fs10 = __toESM(require("fs"));
18754
+ var path9 = __toESM(require("path"));
18357
18755
  var import_crypto2 = require("crypto");
18358
18756
  init_ghl_client();
18359
18757
  init_token_registry();
18360
18758
  init_credentials_store();
18361
18759
  init_setup_tool();
18362
- var EXIT_OK = 0;
18363
- var EXIT_USAGE = 2;
18760
+ var EXIT_OK2 = 0;
18761
+ var EXIT_USAGE2 = 2;
18364
18762
  var EXIT_VALIDATION = 3;
18365
18763
  var EXIT_FS = 4;
18366
18764
  var USAGE = `Usage: ghl-mcp cli <subcommand> [options]
18367
18765
 
18368
18766
  Subcommands:
18767
+ install Add GHL Command to Claude Desktop's settings file
18768
+ (merges safely with existing MCP servers; backs up
18769
+ first; repairs unreadable configs where possible)
18770
+ --dry-run Show what would change without writing anything
18771
+ --print-only Print the merged JSON instead of writing it
18772
+ --path <file> Explicit claude_desktop_config.json location
18773
+ --force Replace a different existing "ghl" entry
18774
+
18369
18775
  register-location Add a sub-account's Private Integration key
18370
18776
  --location-id <id> GHL Location ID (required)
18371
18777
  --api-key <pit-...> The sub-account's Private Integration key (required)
@@ -18396,9 +18802,9 @@ function errLine(msg2) {
18396
18802
  function preflightWritable() {
18397
18803
  try {
18398
18804
  const dir = ensureAppDataDir();
18399
- const probe = path8.join(dir, `.write-probe.${process.pid}.${(0, import_crypto2.randomBytes)(4).toString("hex")}`);
18400
- fs9.writeFileSync(probe, "ok");
18401
- fs9.unlinkSync(probe);
18805
+ const probe = path9.join(dir, `.write-probe.${process.pid}.${(0, import_crypto2.randomBytes)(4).toString("hex")}`);
18806
+ fs10.writeFileSync(probe, "ok");
18807
+ fs10.unlinkSync(probe);
18402
18808
  return true;
18403
18809
  } catch (error) {
18404
18810
  errLine(`Config dir is not writable: ${error instanceof Error ? error.message : String(error)}`);
@@ -18422,7 +18828,7 @@ function confirmSaved(registry2) {
18422
18828
  function parse(argv, options, required) {
18423
18829
  let parsed;
18424
18830
  try {
18425
- parsed = (0, import_node_util.parseArgs)({ args: argv, options, strict: true, allowPositionals: false });
18831
+ parsed = (0, import_node_util2.parseArgs)({ args: argv, options, strict: true, allowPositionals: false });
18426
18832
  } catch (error) {
18427
18833
  return { usageError: error instanceof Error ? error.message : String(error) };
18428
18834
  }
@@ -18449,7 +18855,7 @@ async function cmdRegisterLocation(argv, registry2) {
18449
18855
  if ("usageError" in p) {
18450
18856
  errLine(p.usageError);
18451
18857
  errLine(USAGE);
18452
- return EXIT_USAGE;
18858
+ return EXIT_USAGE2;
18453
18859
  }
18454
18860
  const locationId2 = p.values["location-id"].trim();
18455
18861
  const apiKey2 = p.values["api-key"].trim();
@@ -18482,7 +18888,7 @@ async function cmdRegisterLocation(argv, registry2) {
18482
18888
  `
18483
18889
  );
18484
18890
  restartReminder();
18485
- return EXIT_OK;
18891
+ return EXIT_OK2;
18486
18892
  }
18487
18893
  async function cmdRegisterCompanyFirebase(argv, registry2) {
18488
18894
  const p = parse(
@@ -18500,7 +18906,7 @@ async function cmdRegisterCompanyFirebase(argv, registry2) {
18500
18906
  if ("usageError" in p) {
18501
18907
  errLine(p.usageError);
18502
18908
  errLine(USAGE);
18503
- return EXIT_USAGE;
18909
+ return EXIT_USAGE2;
18504
18910
  }
18505
18911
  const typedCompanyId = p.values["company-id"].trim();
18506
18912
  const refreshToken = p.values["refresh-token"].trim();
@@ -18510,7 +18916,7 @@ async function cmdRegisterCompanyFirebase(argv, registry2) {
18510
18916
  if (!apiKey2) {
18511
18917
  errLine("No Firebase API key available. Pass --api-key (starts with 'AIza'), or seed the home");
18512
18918
  errLine("Firebase first (the key is identical across GHL accounts).");
18513
- return EXIT_USAGE;
18919
+ return EXIT_USAGE2;
18514
18920
  }
18515
18921
  let canonicalCompanyId = typedCompanyId;
18516
18922
  if (!p.values["no-validate"]) {
@@ -18549,7 +18955,7 @@ async function cmdRegisterCompanyFirebase(argv, registry2) {
18549
18955
  `
18550
18956
  );
18551
18957
  restartReminder();
18552
- return EXIT_OK;
18958
+ return EXIT_OK2;
18553
18959
  }
18554
18960
  async function cmdRegisterAgencyKey(argv, registry2) {
18555
18961
  const p = parse(
@@ -18560,7 +18966,7 @@ async function cmdRegisterAgencyKey(argv, registry2) {
18560
18966
  if ("usageError" in p) {
18561
18967
  errLine(p.usageError);
18562
18968
  errLine(USAGE);
18563
- return EXIT_USAGE;
18969
+ return EXIT_USAGE2;
18564
18970
  }
18565
18971
  const apiKey2 = p.values["api-key"].trim();
18566
18972
  if (!p.values["no-validate"]) {
@@ -18589,7 +18995,7 @@ async function cmdRegisterAgencyKey(argv, registry2) {
18589
18995
  process.stdout.write(`Registered agency key: ${apiKey2.substring(0, 12)}... \u2192 ${tokenRegistryPath()}
18590
18996
  `);
18591
18997
  restartReminder();
18592
- return EXIT_OK;
18998
+ return EXIT_OK2;
18593
18999
  }
18594
19000
  function cmdListLocations(registry2) {
18595
19001
  const locs = registry2.listLocations().map((loc) => {
@@ -18606,15 +19012,19 @@ function cmdListLocations(registry2) {
18606
19012
  companyFirebases: companies.map(({ companyId, name }) => ({ companyId, ...name ? { name } : {} }))
18607
19013
  };
18608
19014
  process.stdout.write(JSON.stringify(out, null, 2) + "\n");
18609
- return EXIT_OK;
19015
+ return EXIT_OK2;
18610
19016
  }
18611
19017
  async function runCli(subcommand, argv) {
19018
+ if (subcommand === "install") {
19019
+ const { runInstall: runInstall2 } = await Promise.resolve().then(() => (init_config_installer(), config_installer_exports));
19020
+ return runInstall2(argv);
19021
+ }
18612
19022
  let registry2;
18613
19023
  try {
18614
19024
  registry2 = new TokenRegistry();
18615
19025
  } catch (error) {
18616
19026
  errLine(error instanceof Error ? error.message : String(error));
18617
- return EXIT_USAGE;
19027
+ return EXIT_USAGE2;
18618
19028
  }
18619
19029
  const loadFailure = registry2.getLoadFailure();
18620
19030
  if (loadFailure) {
@@ -18635,11 +19045,11 @@ async function runCli(subcommand, argv) {
18635
19045
  case "--help":
18636
19046
  case "-h":
18637
19047
  process.stdout.write(USAGE + "\n");
18638
- return subcommand === void 0 ? EXIT_USAGE : EXIT_OK;
19048
+ return subcommand === void 0 ? EXIT_USAGE2 : EXIT_OK2;
18639
19049
  default:
18640
19050
  errLine(`Unknown subcommand: ${subcommand}`);
18641
19051
  errLine(USAGE);
18642
- return EXIT_USAGE;
19052
+ return EXIT_USAGE2;
18643
19053
  }
18644
19054
  }
18645
19055
 
@@ -18648,7 +19058,7 @@ var bundledPkg = require_package();
18648
19058
  var pkg = (() => {
18649
19059
  try {
18650
19060
  const onDisk = JSON.parse(
18651
- fs13.readFileSync(path12.resolve(__dirname, "..", "package.json"), "utf8")
19061
+ fs14.readFileSync(path13.resolve(__dirname, "..", "package.json"), "utf8")
18652
19062
  );
18653
19063
  if (typeof onDisk.version === "string" && onDisk.version.length > 0) {
18654
19064
  return { version: onDisk.version };
@@ -18661,7 +19071,7 @@ dotenv2.config();
18661
19071
  setPkgVersion(pkg.version);
18662
19072
  {
18663
19073
  const configDirOverride = process.env.GHL_MCP_CONFIG_DIR?.trim();
18664
- if (configDirOverride && !path12.isAbsolute(configDirOverride)) {
19074
+ if (configDirOverride && !path13.isAbsolute(configDirOverride)) {
18665
19075
  process.stderr.write(
18666
19076
  `[ghl-mcp] GHL_MCP_CONFIG_DIR must be an absolute path (got "${configDirOverride}"). Use e.g. /data/ghl-mcp in a container, with a volume mounted at /data.
18667
19077
  `
@@ -18674,20 +19084,20 @@ process.on("unhandledRejection", (reason) => {
18674
19084
  `);
18675
19085
  });
18676
19086
  function hardenSecretFilePerms() {
18677
- const repoDir = path12.resolve(__dirname, "..");
19087
+ const repoDir = path13.resolve(__dirname, "..");
18678
19088
  const candidates = [
18679
- { file: path12.join(repoDir, "start-mcp.sh"), mode: 448 },
19089
+ { file: path13.join(repoDir, "start-mcp.sh"), mode: 448 },
18680
19090
  // Legacy registry location (pre-migration); new location lives in app-data.
18681
- { file: path12.join(repoDir, ".ghl-tokens.json"), mode: 384 },
19091
+ { file: path13.join(repoDir, ".ghl-tokens.json"), mode: 384 },
18682
19092
  { file: tokenRegistryPath(), mode: 384 }
18683
19093
  ];
18684
19094
  for (const { file, mode } of candidates) {
18685
19095
  let current;
18686
19096
  try {
18687
- if (!fs13.existsSync(file)) continue;
18688
- current = fs13.statSync(file).mode & 511;
19097
+ if (!fs14.existsSync(file)) continue;
19098
+ current = fs14.statSync(file).mode & 511;
18689
19099
  if (current !== mode) {
18690
- fs13.chmodSync(file, mode);
19100
+ fs14.chmodSync(file, mode);
18691
19101
  }
18692
19102
  } catch (error) {
18693
19103
  const message = error instanceof Error ? error.message : String(error);