@specific.dev/cli 0.1.92 → 0.1.93

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 (72) hide show
  1. package/dist/admin/404/index.html +1 -1
  2. package/dist/admin/404.html +1 -1
  3. package/dist/admin/__next.!KGRlZmF1bHQp.__PAGE__.txt +2 -2
  4. package/dist/admin/__next.!KGRlZmF1bHQp.txt +5 -5
  5. package/dist/admin/__next._full.txt +8 -8
  6. package/dist/admin/__next._head.txt +1 -1
  7. package/dist/admin/__next._index.txt +3 -3
  8. package/dist/admin/__next._tree.txt +1 -1
  9. package/dist/admin/_next/static/chunks/42de6b5004222d9f.js +1 -0
  10. package/dist/admin/_next/static/chunks/{091449dbc4d3eb97.js → df799a08b7ff15f9.js} +2 -2
  11. package/dist/admin/_not-found/__next._full.txt +3 -3
  12. package/dist/admin/_not-found/__next._head.txt +1 -1
  13. package/dist/admin/_not-found/__next._index.txt +3 -3
  14. package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
  15. package/dist/admin/_not-found/__next._not-found.txt +1 -1
  16. package/dist/admin/_not-found/__next._tree.txt +1 -1
  17. package/dist/admin/_not-found/index.html +1 -1
  18. package/dist/admin/_not-found/index.txt +3 -3
  19. package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.__PAGE__.txt +2 -2
  20. package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.txt +1 -1
  21. package/dist/admin/databases/__next.!KGRlZmF1bHQp.txt +5 -5
  22. package/dist/admin/databases/__next._full.txt +8 -8
  23. package/dist/admin/databases/__next._head.txt +1 -1
  24. package/dist/admin/databases/__next._index.txt +3 -3
  25. package/dist/admin/databases/__next._tree.txt +1 -1
  26. package/dist/admin/databases/index.html +1 -1
  27. package/dist/admin/databases/index.txt +8 -8
  28. package/dist/admin/fullscreen/__next._full.txt +4 -4
  29. package/dist/admin/fullscreen/__next._head.txt +1 -1
  30. package/dist/admin/fullscreen/__next._index.txt +3 -3
  31. package/dist/admin/fullscreen/__next._tree.txt +1 -1
  32. package/dist/admin/fullscreen/__next.fullscreen.__PAGE__.txt +2 -2
  33. package/dist/admin/fullscreen/__next.fullscreen.txt +1 -1
  34. package/dist/admin/fullscreen/databases/__next._full.txt +4 -4
  35. package/dist/admin/fullscreen/databases/__next._head.txt +1 -1
  36. package/dist/admin/fullscreen/databases/__next._index.txt +3 -3
  37. package/dist/admin/fullscreen/databases/__next._tree.txt +1 -1
  38. package/dist/admin/fullscreen/databases/__next.fullscreen.databases.__PAGE__.txt +2 -2
  39. package/dist/admin/fullscreen/databases/__next.fullscreen.databases.txt +1 -1
  40. package/dist/admin/fullscreen/databases/__next.fullscreen.txt +1 -1
  41. package/dist/admin/fullscreen/databases/index.html +1 -1
  42. package/dist/admin/fullscreen/databases/index.txt +4 -4
  43. package/dist/admin/fullscreen/index.html +1 -1
  44. package/dist/admin/fullscreen/index.txt +4 -4
  45. package/dist/admin/index.html +1 -1
  46. package/dist/admin/index.txt +8 -8
  47. package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.__PAGE__.txt +2 -2
  48. package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.txt +1 -1
  49. package/dist/admin/mail/__next.!KGRlZmF1bHQp.txt +5 -5
  50. package/dist/admin/mail/__next._full.txt +8 -8
  51. package/dist/admin/mail/__next._head.txt +1 -1
  52. package/dist/admin/mail/__next._index.txt +3 -3
  53. package/dist/admin/mail/__next._tree.txt +1 -1
  54. package/dist/admin/mail/index.html +1 -1
  55. package/dist/admin/mail/index.txt +8 -8
  56. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.txt +5 -5
  57. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.__PAGE__.txt +2 -2
  58. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.txt +1 -1
  59. package/dist/admin/workflows/__next._full.txt +8 -8
  60. package/dist/admin/workflows/__next._head.txt +1 -1
  61. package/dist/admin/workflows/__next._index.txt +3 -3
  62. package/dist/admin/workflows/__next._tree.txt +1 -1
  63. package/dist/admin/workflows/index.html +1 -1
  64. package/dist/admin/workflows/index.txt +8 -8
  65. package/dist/cli.js +489 -2062
  66. package/dist/docs/services.md +49 -5
  67. package/package.json +1 -2
  68. package/dist/admin/_next/static/chunks/153355cea359ee0f.js +0 -1
  69. package/dist/postinstall.js +0 -182912
  70. /package/dist/admin/_next/static/{b7lxofBl6HLcSJDobJJzv → bHtcCLWKGqkBj4NJ2_XcY}/_buildManifest.js +0 -0
  71. /package/dist/admin/_next/static/{b7lxofBl6HLcSJDobJJzv → bHtcCLWKGqkBj4NJ2_XcY}/_clientMiddlewareManifest.json +0 -0
  72. /package/dist/admin/_next/static/{b7lxofBl6HLcSJDobJJzv → bHtcCLWKGqkBj4NJ2_XcY}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -520,10 +520,10 @@ ${frame}`;
520
520
  });
521
521
 
522
522
  // node_modules/.pnpm/is-docker@3.0.0/node_modules/is-docker/index.js
523
- import fs18 from "node:fs";
523
+ import fs13 from "node:fs";
524
524
  function hasDockerEnv() {
525
525
  try {
526
- fs18.statSync("/.dockerenv");
526
+ fs13.statSync("/.dockerenv");
527
527
  return true;
528
528
  } catch {
529
529
  return false;
@@ -531,7 +531,7 @@ function hasDockerEnv() {
531
531
  }
532
532
  function hasDockerCGroup() {
533
533
  try {
534
- return fs18.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
534
+ return fs13.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
535
535
  } catch {
536
536
  return false;
537
537
  }
@@ -549,7 +549,7 @@ var init_is_docker = __esm({
549
549
  });
550
550
 
551
551
  // node_modules/.pnpm/is-inside-container@1.0.0/node_modules/is-inside-container/index.js
552
- import fs19 from "node:fs";
552
+ import fs14 from "node:fs";
553
553
  function isInsideContainer() {
554
554
  if (cachedResult === void 0) {
555
555
  cachedResult = hasContainerEnv() || isDocker();
@@ -562,7 +562,7 @@ var init_is_inside_container = __esm({
562
562
  init_is_docker();
563
563
  hasContainerEnv = () => {
564
564
  try {
565
- fs19.statSync("/run/.containerenv");
565
+ fs14.statSync("/run/.containerenv");
566
566
  return true;
567
567
  } catch {
568
568
  return false;
@@ -573,8 +573,8 @@ var init_is_inside_container = __esm({
573
573
 
574
574
  // node_modules/.pnpm/is-wsl@3.1.0/node_modules/is-wsl/index.js
575
575
  import process2 from "node:process";
576
- import os8 from "node:os";
577
- import fs20 from "node:fs";
576
+ import os3 from "node:os";
577
+ import fs15 from "node:fs";
578
578
  var isWsl, is_wsl_default;
579
579
  var init_is_wsl = __esm({
580
580
  "node_modules/.pnpm/is-wsl@3.1.0/node_modules/is-wsl/index.js"() {
@@ -583,14 +583,14 @@ var init_is_wsl = __esm({
583
583
  if (process2.platform !== "linux") {
584
584
  return false;
585
585
  }
586
- if (os8.release().toLowerCase().includes("microsoft")) {
586
+ if (os3.release().toLowerCase().includes("microsoft")) {
587
587
  if (isInsideContainer()) {
588
588
  return false;
589
589
  }
590
590
  return true;
591
591
  }
592
592
  try {
593
- return fs20.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isInsideContainer() : false;
593
+ return fs15.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isInsideContainer() : false;
594
594
  } catch {
595
595
  return false;
596
596
  }
@@ -660,7 +660,7 @@ var init_utilities = __esm({
660
660
  // node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/index.js
661
661
  import { promisify as promisify2 } from "node:util";
662
662
  import childProcess2 from "node:child_process";
663
- import fs21, { constants as fsConstants } from "node:fs/promises";
663
+ import fs16, { constants as fsConstants } from "node:fs/promises";
664
664
  var execFile2, wslDrivesMountPoint, powerShellPathFromWsl, powerShellPath2, canAccessPowerShellPromise, canAccessPowerShell, wslDefaultBrowser, convertWslPathToWindows;
665
665
  var init_wsl_utils = __esm({
666
666
  "node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/index.js"() {
@@ -679,14 +679,14 @@ var init_wsl_utils = __esm({
679
679
  const configFilePath = "/etc/wsl.conf";
680
680
  let isConfigFileExists = false;
681
681
  try {
682
- await fs21.access(configFilePath, fsConstants.F_OK);
682
+ await fs16.access(configFilePath, fsConstants.F_OK);
683
683
  isConfigFileExists = true;
684
684
  } catch {
685
685
  }
686
686
  if (!isConfigFileExists) {
687
687
  return defaultMountPoint;
688
688
  }
689
- const configContent = await fs21.readFile(configFilePath, { encoding: "utf8" });
689
+ const configContent = await fs16.readFile(configFilePath, { encoding: "utf8" });
690
690
  const parsedMountPoint = parseMountPointFromConfig(configContent);
691
691
  if (parsedMountPoint === void 0) {
692
692
  return defaultMountPoint;
@@ -705,7 +705,7 @@ var init_wsl_utils = __esm({
705
705
  canAccessPowerShellPromise ??= (async () => {
706
706
  try {
707
707
  const psPath = await powerShellPath2();
708
- await fs21.access(psPath, fsConstants.X_OK);
708
+ await fs16.access(psPath, fsConstants.X_OK);
709
709
  return true;
710
710
  } catch {
711
711
  return false;
@@ -719,15 +719,15 @@ var init_wsl_utils = __esm({
719
719
  const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
720
720
  return stdout.trim();
721
721
  };
722
- convertWslPathToWindows = async (path30) => {
723
- if (/^[a-z]+:\/\//i.test(path30)) {
724
- return path30;
722
+ convertWslPathToWindows = async (path26) => {
723
+ if (/^[a-z]+:\/\//i.test(path26)) {
724
+ return path26;
725
725
  }
726
726
  try {
727
- const { stdout } = await execFile2("wslpath", ["-aw", path30], { encoding: "utf8" });
727
+ const { stdout } = await execFile2("wslpath", ["-aw", path26], { encoding: "utf8" });
728
728
  return stdout.trim();
729
729
  } catch {
730
- return path30;
730
+ return path26;
731
731
  }
732
732
  };
733
733
  }
@@ -913,10 +913,10 @@ __export(open_exports, {
913
913
  openApp: () => openApp
914
914
  });
915
915
  import process8 from "node:process";
916
- import path19 from "node:path";
916
+ import path15 from "node:path";
917
917
  import { fileURLToPath as fileURLToPath2 } from "node:url";
918
918
  import childProcess3 from "node:child_process";
919
- import fs22, { constants as fsConstants2 } from "node:fs/promises";
919
+ import fs17, { constants as fsConstants2 } from "node:fs/promises";
920
920
  function detectArchBinary(binary) {
921
921
  if (typeof binary === "string" || Array.isArray(binary)) {
922
922
  return binary;
@@ -927,16 +927,16 @@ function detectArchBinary(binary) {
927
927
  }
928
928
  return archBinary;
929
929
  }
930
- function detectPlatformBinary({ [platform6]: platformBinary }, { wsl } = {}) {
930
+ function detectPlatformBinary({ [platform2]: platformBinary }, { wsl } = {}) {
931
931
  if (wsl && is_wsl_default) {
932
932
  return detectArchBinary(wsl);
933
933
  }
934
934
  if (!platformBinary) {
935
- throw new Error(`${platform6} is not supported`);
935
+ throw new Error(`${platform2} is not supported`);
936
936
  }
937
937
  return detectArchBinary(platformBinary);
938
938
  }
939
- var fallbackAttemptSymbol, __dirname2, localXdgOpenPath, platform6, arch2, tryEachApp, baseOpen, open2, openApp, apps, open_default;
939
+ var fallbackAttemptSymbol, __dirname2, localXdgOpenPath, platform2, arch2, tryEachApp, baseOpen, open2, openApp, apps, open_default;
940
940
  var init_open = __esm({
941
941
  "node_modules/.pnpm/open@11.0.0/node_modules/open/index.js"() {
942
942
  init_wsl_utils();
@@ -946,9 +946,9 @@ var init_open = __esm({
946
946
  init_is_inside_container();
947
947
  init_is_in_ssh();
948
948
  fallbackAttemptSymbol = Symbol("fallbackAttempt");
949
- __dirname2 = import.meta.url ? path19.dirname(fileURLToPath2(import.meta.url)) : "";
950
- localXdgOpenPath = path19.join(__dirname2, "xdg-open");
951
- ({ platform: platform6, arch: arch2 } = process8);
949
+ __dirname2 = import.meta.url ? path15.dirname(fileURLToPath2(import.meta.url)) : "";
950
+ localXdgOpenPath = path15.join(__dirname2, "xdg-open");
951
+ ({ platform: platform2, arch: arch2 } = process8);
952
952
  tryEachApp = async (apps2, opener) => {
953
953
  if (apps2.length === 0) {
954
954
  return;
@@ -1045,7 +1045,7 @@ var init_open = __esm({
1045
1045
  if (is_wsl_default && !isInsideContainer() && !is_in_ssh_default && !app) {
1046
1046
  shouldUseWindowsInWsl = await canAccessPowerShell();
1047
1047
  }
1048
- if (platform6 === "darwin") {
1048
+ if (platform2 === "darwin") {
1049
1049
  command = "open";
1050
1050
  if (options2.wait) {
1051
1051
  cliArguments.push("--wait-apps");
@@ -1059,7 +1059,7 @@ var init_open = __esm({
1059
1059
  if (app) {
1060
1060
  cliArguments.push("-a", app);
1061
1061
  }
1062
- } else if (platform6 === "win32" || shouldUseWindowsInWsl) {
1062
+ } else if (platform2 === "win32" || shouldUseWindowsInWsl) {
1063
1063
  command = await powerShellPath2();
1064
1064
  cliArguments.push(...executePowerShell.argumentsPrefix);
1065
1065
  if (!is_wsl_default) {
@@ -1095,11 +1095,11 @@ var init_open = __esm({
1095
1095
  const isBundled = !__dirname2 || __dirname2 === "/";
1096
1096
  let exeLocalXdgOpen = false;
1097
1097
  try {
1098
- await fs22.access(localXdgOpenPath, fsConstants2.X_OK);
1098
+ await fs17.access(localXdgOpenPath, fsConstants2.X_OK);
1099
1099
  exeLocalXdgOpen = true;
1100
1100
  } catch {
1101
1101
  }
1102
- const useSystemXdgOpen = process8.versions.electron ?? (platform6 === "android" || isBundled || !exeLocalXdgOpen);
1102
+ const useSystemXdgOpen = process8.versions.electron ?? (platform2 === "android" || isBundled || !exeLocalXdgOpen);
1103
1103
  command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
1104
1104
  }
1105
1105
  if (appArguments.length > 0) {
@@ -1110,7 +1110,7 @@ var init_open = __esm({
1110
1110
  childProcessOptions.detached = true;
1111
1111
  }
1112
1112
  }
1113
- if (platform6 === "darwin" && appArguments.length > 0) {
1113
+ if (platform2 === "darwin" && appArguments.length > 0) {
1114
1114
  cliArguments.push("--args", ...appArguments);
1115
1115
  }
1116
1116
  if (options2.target) {
@@ -1235,8 +1235,8 @@ var require_dist2 = __commonJS({
1235
1235
  var $global, $module, $NaN = NaN;
1236
1236
  if ("undefined" != typeof window ? $global = window : "undefined" != typeof self ? $global = self : "undefined" != typeof global ? ($global = global).require = __require : $global = this, void 0 === $global || void 0 === $global.Array) throw new Error("no global object found");
1237
1237
  if ("undefined" != typeof module && ($module = module), !$global.fs && $global.require) try {
1238
- var fs33 = $global.require("fs");
1239
- "object" == typeof fs33 && null !== fs33 && 0 !== Object.keys(fs33).length && ($global.fs = fs33);
1238
+ var fs28 = $global.require("fs");
1239
+ "object" == typeof fs28 && null !== fs28 && 0 !== Object.keys(fs28).length && ($global.fs = fs28);
1240
1240
  } catch (e) {
1241
1241
  }
1242
1242
  if (!$global.fs) {
@@ -183429,10 +183429,8 @@ import { Command } from "commander";
183429
183429
  // src/commands/init.tsx
183430
183430
  import React2, { useState, useEffect } from "react";
183431
183431
  import { render as render2, Text as Text2, Box as Box2, useInput, useApp } from "ink";
183432
- import "ink-spinner";
183433
- import * as fs24 from "fs";
183434
- import * as path21 from "path";
183435
- import * as os11 from "os";
183432
+ import * as fs19 from "fs";
183433
+ import * as path17 from "path";
183436
183434
 
183437
183435
  // node_modules/.pnpm/@specific+sdk@file+..+sdk/node_modules/@specific/sdk/dist/index.js
183438
183436
  import * as fs from "fs";
@@ -183442,9 +183440,8 @@ import { execSync } from "child_process";
183442
183440
  import * as fs2 from "fs";
183443
183441
  import * as path2 from "path";
183444
183442
  import { createTarPacker, createEntryItemGenerator } from "tar-vern";
183445
- import * as fs16 from "fs";
183446
- import * as os7 from "os";
183447
- import * as path16 from "path";
183443
+ import * as fs12 from "fs";
183444
+ import * as path13 from "path";
183448
183445
 
183449
183446
  // node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
183450
183447
  import { EventEmitter } from "node:events";
@@ -183536,7 +183533,7 @@ var ReaddirpStream = class extends Readable {
183536
183533
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
183537
183534
  const statMethod = opts.lstat ? lstat : stat;
183538
183535
  if (wantBigintFsStats) {
183539
- this._stat = (path30) => statMethod(path30, { bigint: true });
183536
+ this._stat = (path26) => statMethod(path26, { bigint: true });
183540
183537
  } else {
183541
183538
  this._stat = statMethod;
183542
183539
  }
@@ -183561,8 +183558,8 @@ var ReaddirpStream = class extends Readable {
183561
183558
  const par = this.parent;
183562
183559
  const fil = par && par.files;
183563
183560
  if (fil && fil.length > 0) {
183564
- const { path: path30, depth } = par;
183565
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path30));
183561
+ const { path: path26, depth } = par;
183562
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path26));
183566
183563
  const awaited = await Promise.all(slice);
183567
183564
  for (const entry of awaited) {
183568
183565
  if (!entry)
@@ -183602,20 +183599,20 @@ var ReaddirpStream = class extends Readable {
183602
183599
  this.reading = false;
183603
183600
  }
183604
183601
  }
183605
- async _exploreDir(path30, depth) {
183602
+ async _exploreDir(path26, depth) {
183606
183603
  let files;
183607
183604
  try {
183608
- files = await readdir(path30, this._rdOptions);
183605
+ files = await readdir(path26, this._rdOptions);
183609
183606
  } catch (error) {
183610
183607
  this._onError(error);
183611
183608
  }
183612
- return { files, depth, path: path30 };
183609
+ return { files, depth, path: path26 };
183613
183610
  }
183614
- async _formatEntry(dirent, path30) {
183611
+ async _formatEntry(dirent, path26) {
183615
183612
  let entry;
183616
183613
  const basename6 = this._isDirent ? dirent.name : dirent;
183617
183614
  try {
183618
- const fullPath = presolve(pjoin(path30, basename6));
183615
+ const fullPath = presolve(pjoin(path26, basename6));
183619
183616
  entry = { path: prelative(this._root, fullPath), fullPath, basename: basename6 };
183620
183617
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
183621
183618
  } catch (err) {
@@ -184015,16 +184012,16 @@ var delFromSet = (main, prop, item) => {
184015
184012
  };
184016
184013
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
184017
184014
  var FsWatchInstances = /* @__PURE__ */ new Map();
184018
- function createFsWatchInstance(path30, options2, listener, errHandler, emitRaw) {
184015
+ function createFsWatchInstance(path26, options2, listener, errHandler, emitRaw) {
184019
184016
  const handleEvent = (rawEvent, evPath) => {
184020
- listener(path30);
184021
- emitRaw(rawEvent, evPath, { watchedPath: path30 });
184022
- if (evPath && path30 !== evPath) {
184023
- fsWatchBroadcast(sp.resolve(path30, evPath), KEY_LISTENERS, sp.join(path30, evPath));
184017
+ listener(path26);
184018
+ emitRaw(rawEvent, evPath, { watchedPath: path26 });
184019
+ if (evPath && path26 !== evPath) {
184020
+ fsWatchBroadcast(sp.resolve(path26, evPath), KEY_LISTENERS, sp.join(path26, evPath));
184024
184021
  }
184025
184022
  };
184026
184023
  try {
184027
- return fs_watch(path30, {
184024
+ return fs_watch(path26, {
184028
184025
  persistent: options2.persistent
184029
184026
  }, handleEvent);
184030
184027
  } catch (error) {
@@ -184040,12 +184037,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
184040
184037
  listener(val1, val2, val3);
184041
184038
  });
184042
184039
  };
184043
- var setFsWatchListener = (path30, fullPath, options2, handlers) => {
184040
+ var setFsWatchListener = (path26, fullPath, options2, handlers) => {
184044
184041
  const { listener, errHandler, rawEmitter } = handlers;
184045
184042
  let cont = FsWatchInstances.get(fullPath);
184046
184043
  let watcher;
184047
184044
  if (!options2.persistent) {
184048
- watcher = createFsWatchInstance(path30, options2, listener, errHandler, rawEmitter);
184045
+ watcher = createFsWatchInstance(path26, options2, listener, errHandler, rawEmitter);
184049
184046
  if (!watcher)
184050
184047
  return;
184051
184048
  return watcher.close.bind(watcher);
@@ -184056,7 +184053,7 @@ var setFsWatchListener = (path30, fullPath, options2, handlers) => {
184056
184053
  addAndConvert(cont, KEY_RAW, rawEmitter);
184057
184054
  } else {
184058
184055
  watcher = createFsWatchInstance(
184059
- path30,
184056
+ path26,
184060
184057
  options2,
184061
184058
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
184062
184059
  errHandler,
@@ -184071,7 +184068,7 @@ var setFsWatchListener = (path30, fullPath, options2, handlers) => {
184071
184068
  cont.watcherUnusable = true;
184072
184069
  if (isWindows && error.code === "EPERM") {
184073
184070
  try {
184074
- const fd = await open(path30, "r");
184071
+ const fd = await open(path26, "r");
184075
184072
  await fd.close();
184076
184073
  broadcastErr(error);
184077
184074
  } catch (err) {
@@ -184102,7 +184099,7 @@ var setFsWatchListener = (path30, fullPath, options2, handlers) => {
184102
184099
  };
184103
184100
  };
184104
184101
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
184105
- var setFsWatchFileListener = (path30, fullPath, options2, handlers) => {
184102
+ var setFsWatchFileListener = (path26, fullPath, options2, handlers) => {
184106
184103
  const { listener, rawEmitter } = handlers;
184107
184104
  let cont = FsWatchFileInstances.get(fullPath);
184108
184105
  const copts = cont && cont.options;
@@ -184124,7 +184121,7 @@ var setFsWatchFileListener = (path30, fullPath, options2, handlers) => {
184124
184121
  });
184125
184122
  const currmtime = curr.mtimeMs;
184126
184123
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
184127
- foreach(cont.listeners, (listener2) => listener2(path30, curr));
184124
+ foreach(cont.listeners, (listener2) => listener2(path26, curr));
184128
184125
  }
184129
184126
  })
184130
184127
  };
@@ -184154,13 +184151,13 @@ var NodeFsHandler = class {
184154
184151
  * @param listener on fs change
184155
184152
  * @returns closer for the watcher instance
184156
184153
  */
184157
- _watchWithNodeFs(path30, listener) {
184154
+ _watchWithNodeFs(path26, listener) {
184158
184155
  const opts = this.fsw.options;
184159
- const directory = sp.dirname(path30);
184160
- const basename6 = sp.basename(path30);
184156
+ const directory = sp.dirname(path26);
184157
+ const basename6 = sp.basename(path26);
184161
184158
  const parent = this.fsw._getWatchedDir(directory);
184162
184159
  parent.add(basename6);
184163
- const absolutePath = sp.resolve(path30);
184160
+ const absolutePath = sp.resolve(path26);
184164
184161
  const options2 = {
184165
184162
  persistent: opts.persistent
184166
184163
  };
@@ -184170,12 +184167,12 @@ var NodeFsHandler = class {
184170
184167
  if (opts.usePolling) {
184171
184168
  const enableBin = opts.interval !== opts.binaryInterval;
184172
184169
  options2.interval = enableBin && isBinaryPath(basename6) ? opts.binaryInterval : opts.interval;
184173
- closer = setFsWatchFileListener(path30, absolutePath, options2, {
184170
+ closer = setFsWatchFileListener(path26, absolutePath, options2, {
184174
184171
  listener,
184175
184172
  rawEmitter: this.fsw._emitRaw
184176
184173
  });
184177
184174
  } else {
184178
- closer = setFsWatchListener(path30, absolutePath, options2, {
184175
+ closer = setFsWatchListener(path26, absolutePath, options2, {
184179
184176
  listener,
184180
184177
  errHandler: this._boundHandleError,
184181
184178
  rawEmitter: this.fsw._emitRaw
@@ -184197,7 +184194,7 @@ var NodeFsHandler = class {
184197
184194
  let prevStats = stats;
184198
184195
  if (parent.has(basename6))
184199
184196
  return;
184200
- const listener = async (path30, newStats) => {
184197
+ const listener = async (path26, newStats) => {
184201
184198
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
184202
184199
  return;
184203
184200
  if (!newStats || newStats.mtimeMs === 0) {
@@ -184211,11 +184208,11 @@ var NodeFsHandler = class {
184211
184208
  this.fsw._emit(EV.CHANGE, file, newStats2);
184212
184209
  }
184213
184210
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
184214
- this.fsw._closeFile(path30);
184211
+ this.fsw._closeFile(path26);
184215
184212
  prevStats = newStats2;
184216
184213
  const closer2 = this._watchWithNodeFs(file, listener);
184217
184214
  if (closer2)
184218
- this.fsw._addPathCloser(path30, closer2);
184215
+ this.fsw._addPathCloser(path26, closer2);
184219
184216
  } else {
184220
184217
  prevStats = newStats2;
184221
184218
  }
@@ -184247,7 +184244,7 @@ var NodeFsHandler = class {
184247
184244
  * @param item basename of this item
184248
184245
  * @returns true if no more processing is needed for this entry.
184249
184246
  */
184250
- async _handleSymlink(entry, directory, path30, item) {
184247
+ async _handleSymlink(entry, directory, path26, item) {
184251
184248
  if (this.fsw.closed) {
184252
184249
  return;
184253
184250
  }
@@ -184257,7 +184254,7 @@ var NodeFsHandler = class {
184257
184254
  this.fsw._incrReadyCount();
184258
184255
  let linkPath;
184259
184256
  try {
184260
- linkPath = await fsrealpath(path30);
184257
+ linkPath = await fsrealpath(path26);
184261
184258
  } catch (e) {
184262
184259
  this.fsw._emitReady();
184263
184260
  return true;
@@ -184267,12 +184264,12 @@ var NodeFsHandler = class {
184267
184264
  if (dir.has(item)) {
184268
184265
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
184269
184266
  this.fsw._symlinkPaths.set(full, linkPath);
184270
- this.fsw._emit(EV.CHANGE, path30, entry.stats);
184267
+ this.fsw._emit(EV.CHANGE, path26, entry.stats);
184271
184268
  }
184272
184269
  } else {
184273
184270
  dir.add(item);
184274
184271
  this.fsw._symlinkPaths.set(full, linkPath);
184275
- this.fsw._emit(EV.ADD, path30, entry.stats);
184272
+ this.fsw._emit(EV.ADD, path26, entry.stats);
184276
184273
  }
184277
184274
  this.fsw._emitReady();
184278
184275
  return true;
@@ -184302,9 +184299,9 @@ var NodeFsHandler = class {
184302
184299
  return;
184303
184300
  }
184304
184301
  const item = entry.path;
184305
- let path30 = sp.join(directory, item);
184302
+ let path26 = sp.join(directory, item);
184306
184303
  current.add(item);
184307
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path30, item)) {
184304
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path26, item)) {
184308
184305
  return;
184309
184306
  }
184310
184307
  if (this.fsw.closed) {
@@ -184313,8 +184310,8 @@ var NodeFsHandler = class {
184313
184310
  }
184314
184311
  if (item === target || !target && !previous.has(item)) {
184315
184312
  this.fsw._incrReadyCount();
184316
- path30 = sp.join(dir, sp.relative(dir, path30));
184317
- this._addToNodeFs(path30, initialAdd, wh, depth + 1);
184313
+ path26 = sp.join(dir, sp.relative(dir, path26));
184314
+ this._addToNodeFs(path26, initialAdd, wh, depth + 1);
184318
184315
  }
184319
184316
  }).on(EV.ERROR, this._boundHandleError);
184320
184317
  return new Promise((resolve9, reject) => {
@@ -184383,13 +184380,13 @@ var NodeFsHandler = class {
184383
184380
  * @param depth Child path actually targeted for watch
184384
184381
  * @param target Child path actually targeted for watch
184385
184382
  */
184386
- async _addToNodeFs(path30, initialAdd, priorWh, depth, target) {
184383
+ async _addToNodeFs(path26, initialAdd, priorWh, depth, target) {
184387
184384
  const ready = this.fsw._emitReady;
184388
- if (this.fsw._isIgnored(path30) || this.fsw.closed) {
184385
+ if (this.fsw._isIgnored(path26) || this.fsw.closed) {
184389
184386
  ready();
184390
184387
  return false;
184391
184388
  }
184392
- const wh = this.fsw._getWatchHelpers(path30);
184389
+ const wh = this.fsw._getWatchHelpers(path26);
184393
184390
  if (priorWh) {
184394
184391
  wh.filterPath = (entry) => priorWh.filterPath(entry);
184395
184392
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -184405,8 +184402,8 @@ var NodeFsHandler = class {
184405
184402
  const follow = this.fsw.options.followSymlinks;
184406
184403
  let closer;
184407
184404
  if (stats.isDirectory()) {
184408
- const absPath = sp.resolve(path30);
184409
- const targetPath = follow ? await fsrealpath(path30) : path30;
184405
+ const absPath = sp.resolve(path26);
184406
+ const targetPath = follow ? await fsrealpath(path26) : path26;
184410
184407
  if (this.fsw.closed)
184411
184408
  return;
184412
184409
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -184416,29 +184413,29 @@ var NodeFsHandler = class {
184416
184413
  this.fsw._symlinkPaths.set(absPath, targetPath);
184417
184414
  }
184418
184415
  } else if (stats.isSymbolicLink()) {
184419
- const targetPath = follow ? await fsrealpath(path30) : path30;
184416
+ const targetPath = follow ? await fsrealpath(path26) : path26;
184420
184417
  if (this.fsw.closed)
184421
184418
  return;
184422
184419
  const parent = sp.dirname(wh.watchPath);
184423
184420
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
184424
184421
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
184425
- closer = await this._handleDir(parent, stats, initialAdd, depth, path30, wh, targetPath);
184422
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path26, wh, targetPath);
184426
184423
  if (this.fsw.closed)
184427
184424
  return;
184428
184425
  if (targetPath !== void 0) {
184429
- this.fsw._symlinkPaths.set(sp.resolve(path30), targetPath);
184426
+ this.fsw._symlinkPaths.set(sp.resolve(path26), targetPath);
184430
184427
  }
184431
184428
  } else {
184432
184429
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
184433
184430
  }
184434
184431
  ready();
184435
184432
  if (closer)
184436
- this.fsw._addPathCloser(path30, closer);
184433
+ this.fsw._addPathCloser(path26, closer);
184437
184434
  return false;
184438
184435
  } catch (error) {
184439
184436
  if (this.fsw._handleError(error)) {
184440
184437
  ready();
184441
- return path30;
184438
+ return path26;
184442
184439
  }
184443
184440
  }
184444
184441
  }
@@ -184481,24 +184478,24 @@ function createPattern(matcher) {
184481
184478
  }
184482
184479
  return () => false;
184483
184480
  }
184484
- function normalizePath(path30) {
184485
- if (typeof path30 !== "string")
184481
+ function normalizePath(path26) {
184482
+ if (typeof path26 !== "string")
184486
184483
  throw new Error("string expected");
184487
- path30 = sp2.normalize(path30);
184488
- path30 = path30.replace(/\\/g, "/");
184484
+ path26 = sp2.normalize(path26);
184485
+ path26 = path26.replace(/\\/g, "/");
184489
184486
  let prepend = false;
184490
- if (path30.startsWith("//"))
184487
+ if (path26.startsWith("//"))
184491
184488
  prepend = true;
184492
- path30 = path30.replace(DOUBLE_SLASH_RE, "/");
184489
+ path26 = path26.replace(DOUBLE_SLASH_RE, "/");
184493
184490
  if (prepend)
184494
- path30 = "/" + path30;
184495
- return path30;
184491
+ path26 = "/" + path26;
184492
+ return path26;
184496
184493
  }
184497
184494
  function matchPatterns(patterns, testString, stats) {
184498
- const path30 = normalizePath(testString);
184495
+ const path26 = normalizePath(testString);
184499
184496
  for (let index = 0; index < patterns.length; index++) {
184500
184497
  const pattern = patterns[index];
184501
- if (pattern(path30, stats)) {
184498
+ if (pattern(path26, stats)) {
184502
184499
  return true;
184503
184500
  }
184504
184501
  }
@@ -184536,19 +184533,19 @@ var toUnix = (string) => {
184536
184533
  }
184537
184534
  return str;
184538
184535
  };
184539
- var normalizePathToUnix = (path30) => toUnix(sp2.normalize(toUnix(path30)));
184540
- var normalizeIgnored = (cwd = "") => (path30) => {
184541
- if (typeof path30 === "string") {
184542
- return normalizePathToUnix(sp2.isAbsolute(path30) ? path30 : sp2.join(cwd, path30));
184536
+ var normalizePathToUnix = (path26) => toUnix(sp2.normalize(toUnix(path26)));
184537
+ var normalizeIgnored = (cwd = "") => (path26) => {
184538
+ if (typeof path26 === "string") {
184539
+ return normalizePathToUnix(sp2.isAbsolute(path26) ? path26 : sp2.join(cwd, path26));
184543
184540
  } else {
184544
- return path30;
184541
+ return path26;
184545
184542
  }
184546
184543
  };
184547
- var getAbsolutePath = (path30, cwd) => {
184548
- if (sp2.isAbsolute(path30)) {
184549
- return path30;
184544
+ var getAbsolutePath = (path26, cwd) => {
184545
+ if (sp2.isAbsolute(path26)) {
184546
+ return path26;
184550
184547
  }
184551
- return sp2.join(cwd, path30);
184548
+ return sp2.join(cwd, path26);
184552
184549
  };
184553
184550
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
184554
184551
  var DirEntry = class {
@@ -184613,10 +184610,10 @@ var WatchHelper = class {
184613
184610
  dirParts;
184614
184611
  followSymlinks;
184615
184612
  statMethod;
184616
- constructor(path30, follow, fsw) {
184613
+ constructor(path26, follow, fsw) {
184617
184614
  this.fsw = fsw;
184618
- const watchPath = path30;
184619
- this.path = path30 = path30.replace(REPLACER_RE, "");
184615
+ const watchPath = path26;
184616
+ this.path = path26 = path26.replace(REPLACER_RE, "");
184620
184617
  this.watchPath = watchPath;
184621
184618
  this.fullWatchPath = sp2.resolve(watchPath);
184622
184619
  this.dirParts = [];
@@ -184756,20 +184753,20 @@ var FSWatcher = class extends EventEmitter {
184756
184753
  this._closePromise = void 0;
184757
184754
  let paths = unifyPaths(paths_);
184758
184755
  if (cwd) {
184759
- paths = paths.map((path30) => {
184760
- const absPath = getAbsolutePath(path30, cwd);
184756
+ paths = paths.map((path26) => {
184757
+ const absPath = getAbsolutePath(path26, cwd);
184761
184758
  return absPath;
184762
184759
  });
184763
184760
  }
184764
- paths.forEach((path30) => {
184765
- this._removeIgnoredPath(path30);
184761
+ paths.forEach((path26) => {
184762
+ this._removeIgnoredPath(path26);
184766
184763
  });
184767
184764
  this._userIgnored = void 0;
184768
184765
  if (!this._readyCount)
184769
184766
  this._readyCount = 0;
184770
184767
  this._readyCount += paths.length;
184771
- Promise.all(paths.map(async (path30) => {
184772
- const res = await this._nodeFsHandler._addToNodeFs(path30, !_internal, void 0, 0, _origAdd);
184768
+ Promise.all(paths.map(async (path26) => {
184769
+ const res = await this._nodeFsHandler._addToNodeFs(path26, !_internal, void 0, 0, _origAdd);
184773
184770
  if (res)
184774
184771
  this._emitReady();
184775
184772
  return res;
@@ -184791,17 +184788,17 @@ var FSWatcher = class extends EventEmitter {
184791
184788
  return this;
184792
184789
  const paths = unifyPaths(paths_);
184793
184790
  const { cwd } = this.options;
184794
- paths.forEach((path30) => {
184795
- if (!sp2.isAbsolute(path30) && !this._closers.has(path30)) {
184791
+ paths.forEach((path26) => {
184792
+ if (!sp2.isAbsolute(path26) && !this._closers.has(path26)) {
184796
184793
  if (cwd)
184797
- path30 = sp2.join(cwd, path30);
184798
- path30 = sp2.resolve(path30);
184794
+ path26 = sp2.join(cwd, path26);
184795
+ path26 = sp2.resolve(path26);
184799
184796
  }
184800
- this._closePath(path30);
184801
- this._addIgnoredPath(path30);
184802
- if (this._watched.has(path30)) {
184797
+ this._closePath(path26);
184798
+ this._addIgnoredPath(path26);
184799
+ if (this._watched.has(path26)) {
184803
184800
  this._addIgnoredPath({
184804
- path: path30,
184801
+ path: path26,
184805
184802
  recursive: true
184806
184803
  });
184807
184804
  }
@@ -184865,38 +184862,38 @@ var FSWatcher = class extends EventEmitter {
184865
184862
  * @param stats arguments to be passed with event
184866
184863
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
184867
184864
  */
184868
- async _emit(event, path30, stats) {
184865
+ async _emit(event, path26, stats) {
184869
184866
  if (this.closed)
184870
184867
  return;
184871
184868
  const opts = this.options;
184872
184869
  if (isWindows)
184873
- path30 = sp2.normalize(path30);
184870
+ path26 = sp2.normalize(path26);
184874
184871
  if (opts.cwd)
184875
- path30 = sp2.relative(opts.cwd, path30);
184876
- const args = [path30];
184872
+ path26 = sp2.relative(opts.cwd, path26);
184873
+ const args = [path26];
184877
184874
  if (stats != null)
184878
184875
  args.push(stats);
184879
184876
  const awf = opts.awaitWriteFinish;
184880
184877
  let pw;
184881
- if (awf && (pw = this._pendingWrites.get(path30))) {
184878
+ if (awf && (pw = this._pendingWrites.get(path26))) {
184882
184879
  pw.lastChange = /* @__PURE__ */ new Date();
184883
184880
  return this;
184884
184881
  }
184885
184882
  if (opts.atomic) {
184886
184883
  if (event === EVENTS.UNLINK) {
184887
- this._pendingUnlinks.set(path30, [event, ...args]);
184884
+ this._pendingUnlinks.set(path26, [event, ...args]);
184888
184885
  setTimeout(() => {
184889
- this._pendingUnlinks.forEach((entry, path31) => {
184886
+ this._pendingUnlinks.forEach((entry, path27) => {
184890
184887
  this.emit(...entry);
184891
184888
  this.emit(EVENTS.ALL, ...entry);
184892
- this._pendingUnlinks.delete(path31);
184889
+ this._pendingUnlinks.delete(path27);
184893
184890
  });
184894
184891
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
184895
184892
  return this;
184896
184893
  }
184897
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path30)) {
184894
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path26)) {
184898
184895
  event = EVENTS.CHANGE;
184899
- this._pendingUnlinks.delete(path30);
184896
+ this._pendingUnlinks.delete(path26);
184900
184897
  }
184901
184898
  }
184902
184899
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -184914,16 +184911,16 @@ var FSWatcher = class extends EventEmitter {
184914
184911
  this.emitWithAll(event, args);
184915
184912
  }
184916
184913
  };
184917
- this._awaitWriteFinish(path30, awf.stabilityThreshold, event, awfEmit);
184914
+ this._awaitWriteFinish(path26, awf.stabilityThreshold, event, awfEmit);
184918
184915
  return this;
184919
184916
  }
184920
184917
  if (event === EVENTS.CHANGE) {
184921
- const isThrottled = !this._throttle(EVENTS.CHANGE, path30, 50);
184918
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path26, 50);
184922
184919
  if (isThrottled)
184923
184920
  return this;
184924
184921
  }
184925
184922
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
184926
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path30) : path30;
184923
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path26) : path26;
184927
184924
  let stats2;
184928
184925
  try {
184929
184926
  stats2 = await stat3(fullPath);
@@ -184954,23 +184951,23 @@ var FSWatcher = class extends EventEmitter {
184954
184951
  * @param timeout duration of time to suppress duplicate actions
184955
184952
  * @returns tracking object or false if action should be suppressed
184956
184953
  */
184957
- _throttle(actionType, path30, timeout) {
184954
+ _throttle(actionType, path26, timeout) {
184958
184955
  if (!this._throttled.has(actionType)) {
184959
184956
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
184960
184957
  }
184961
184958
  const action = this._throttled.get(actionType);
184962
184959
  if (!action)
184963
184960
  throw new Error("invalid throttle");
184964
- const actionPath = action.get(path30);
184961
+ const actionPath = action.get(path26);
184965
184962
  if (actionPath) {
184966
184963
  actionPath.count++;
184967
184964
  return false;
184968
184965
  }
184969
184966
  let timeoutObject;
184970
184967
  const clear = () => {
184971
- const item = action.get(path30);
184968
+ const item = action.get(path26);
184972
184969
  const count = item ? item.count : 0;
184973
- action.delete(path30);
184970
+ action.delete(path26);
184974
184971
  clearTimeout(timeoutObject);
184975
184972
  if (item)
184976
184973
  clearTimeout(item.timeoutObject);
@@ -184978,7 +184975,7 @@ var FSWatcher = class extends EventEmitter {
184978
184975
  };
184979
184976
  timeoutObject = setTimeout(clear, timeout);
184980
184977
  const thr = { timeoutObject, clear, count: 0 };
184981
- action.set(path30, thr);
184978
+ action.set(path26, thr);
184982
184979
  return thr;
184983
184980
  }
184984
184981
  _incrReadyCount() {
@@ -184992,44 +184989,44 @@ var FSWatcher = class extends EventEmitter {
184992
184989
  * @param event
184993
184990
  * @param awfEmit Callback to be called when ready for event to be emitted.
184994
184991
  */
184995
- _awaitWriteFinish(path30, threshold, event, awfEmit) {
184992
+ _awaitWriteFinish(path26, threshold, event, awfEmit) {
184996
184993
  const awf = this.options.awaitWriteFinish;
184997
184994
  if (typeof awf !== "object")
184998
184995
  return;
184999
184996
  const pollInterval = awf.pollInterval;
185000
184997
  let timeoutHandler;
185001
- let fullPath = path30;
185002
- if (this.options.cwd && !sp2.isAbsolute(path30)) {
185003
- fullPath = sp2.join(this.options.cwd, path30);
184998
+ let fullPath = path26;
184999
+ if (this.options.cwd && !sp2.isAbsolute(path26)) {
185000
+ fullPath = sp2.join(this.options.cwd, path26);
185004
185001
  }
185005
185002
  const now = /* @__PURE__ */ new Date();
185006
185003
  const writes = this._pendingWrites;
185007
185004
  function awaitWriteFinishFn(prevStat) {
185008
185005
  statcb(fullPath, (err, curStat) => {
185009
- if (err || !writes.has(path30)) {
185006
+ if (err || !writes.has(path26)) {
185010
185007
  if (err && err.code !== "ENOENT")
185011
185008
  awfEmit(err);
185012
185009
  return;
185013
185010
  }
185014
185011
  const now2 = Number(/* @__PURE__ */ new Date());
185015
185012
  if (prevStat && curStat.size !== prevStat.size) {
185016
- writes.get(path30).lastChange = now2;
185013
+ writes.get(path26).lastChange = now2;
185017
185014
  }
185018
- const pw = writes.get(path30);
185015
+ const pw = writes.get(path26);
185019
185016
  const df = now2 - pw.lastChange;
185020
185017
  if (df >= threshold) {
185021
- writes.delete(path30);
185018
+ writes.delete(path26);
185022
185019
  awfEmit(void 0, curStat);
185023
185020
  } else {
185024
185021
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
185025
185022
  }
185026
185023
  });
185027
185024
  }
185028
- if (!writes.has(path30)) {
185029
- writes.set(path30, {
185025
+ if (!writes.has(path26)) {
185026
+ writes.set(path26, {
185030
185027
  lastChange: now,
185031
185028
  cancelWait: () => {
185032
- writes.delete(path30);
185029
+ writes.delete(path26);
185033
185030
  clearTimeout(timeoutHandler);
185034
185031
  return event;
185035
185032
  }
@@ -185040,8 +185037,8 @@ var FSWatcher = class extends EventEmitter {
185040
185037
  /**
185041
185038
  * Determines whether user has asked to ignore this path.
185042
185039
  */
185043
- _isIgnored(path30, stats) {
185044
- if (this.options.atomic && DOT_RE.test(path30))
185040
+ _isIgnored(path26, stats) {
185041
+ if (this.options.atomic && DOT_RE.test(path26))
185045
185042
  return true;
185046
185043
  if (!this._userIgnored) {
185047
185044
  const { cwd } = this.options;
@@ -185051,17 +185048,17 @@ var FSWatcher = class extends EventEmitter {
185051
185048
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
185052
185049
  this._userIgnored = anymatch(list, void 0);
185053
185050
  }
185054
- return this._userIgnored(path30, stats);
185051
+ return this._userIgnored(path26, stats);
185055
185052
  }
185056
- _isntIgnored(path30, stat4) {
185057
- return !this._isIgnored(path30, stat4);
185053
+ _isntIgnored(path26, stat4) {
185054
+ return !this._isIgnored(path26, stat4);
185058
185055
  }
185059
185056
  /**
185060
185057
  * Provides a set of common helpers and properties relating to symlink handling.
185061
185058
  * @param path file or directory pattern being watched
185062
185059
  */
185063
- _getWatchHelpers(path30) {
185064
- return new WatchHelper(path30, this.options.followSymlinks, this);
185060
+ _getWatchHelpers(path26) {
185061
+ return new WatchHelper(path26, this.options.followSymlinks, this);
185065
185062
  }
185066
185063
  // Directory helpers
185067
185064
  // -----------------
@@ -185093,63 +185090,63 @@ var FSWatcher = class extends EventEmitter {
185093
185090
  * @param item base path of item/directory
185094
185091
  */
185095
185092
  _remove(directory, item, isDirectory) {
185096
- const path30 = sp2.join(directory, item);
185097
- const fullPath = sp2.resolve(path30);
185098
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path30) || this._watched.has(fullPath);
185099
- if (!this._throttle("remove", path30, 100))
185093
+ const path26 = sp2.join(directory, item);
185094
+ const fullPath = sp2.resolve(path26);
185095
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path26) || this._watched.has(fullPath);
185096
+ if (!this._throttle("remove", path26, 100))
185100
185097
  return;
185101
185098
  if (!isDirectory && this._watched.size === 1) {
185102
185099
  this.add(directory, item, true);
185103
185100
  }
185104
- const wp = this._getWatchedDir(path30);
185101
+ const wp = this._getWatchedDir(path26);
185105
185102
  const nestedDirectoryChildren = wp.getChildren();
185106
- nestedDirectoryChildren.forEach((nested) => this._remove(path30, nested));
185103
+ nestedDirectoryChildren.forEach((nested) => this._remove(path26, nested));
185107
185104
  const parent = this._getWatchedDir(directory);
185108
185105
  const wasTracked = parent.has(item);
185109
185106
  parent.remove(item);
185110
185107
  if (this._symlinkPaths.has(fullPath)) {
185111
185108
  this._symlinkPaths.delete(fullPath);
185112
185109
  }
185113
- let relPath = path30;
185110
+ let relPath = path26;
185114
185111
  if (this.options.cwd)
185115
- relPath = sp2.relative(this.options.cwd, path30);
185112
+ relPath = sp2.relative(this.options.cwd, path26);
185116
185113
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
185117
185114
  const event = this._pendingWrites.get(relPath).cancelWait();
185118
185115
  if (event === EVENTS.ADD)
185119
185116
  return;
185120
185117
  }
185121
- this._watched.delete(path30);
185118
+ this._watched.delete(path26);
185122
185119
  this._watched.delete(fullPath);
185123
185120
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
185124
- if (wasTracked && !this._isIgnored(path30))
185125
- this._emit(eventName, path30);
185126
- this._closePath(path30);
185121
+ if (wasTracked && !this._isIgnored(path26))
185122
+ this._emit(eventName, path26);
185123
+ this._closePath(path26);
185127
185124
  }
185128
185125
  /**
185129
185126
  * Closes all watchers for a path
185130
185127
  */
185131
- _closePath(path30) {
185132
- this._closeFile(path30);
185133
- const dir = sp2.dirname(path30);
185134
- this._getWatchedDir(dir).remove(sp2.basename(path30));
185128
+ _closePath(path26) {
185129
+ this._closeFile(path26);
185130
+ const dir = sp2.dirname(path26);
185131
+ this._getWatchedDir(dir).remove(sp2.basename(path26));
185135
185132
  }
185136
185133
  /**
185137
185134
  * Closes only file-specific watchers
185138
185135
  */
185139
- _closeFile(path30) {
185140
- const closers = this._closers.get(path30);
185136
+ _closeFile(path26) {
185137
+ const closers = this._closers.get(path26);
185141
185138
  if (!closers)
185142
185139
  return;
185143
185140
  closers.forEach((closer) => closer());
185144
- this._closers.delete(path30);
185141
+ this._closers.delete(path26);
185145
185142
  }
185146
- _addPathCloser(path30, closer) {
185143
+ _addPathCloser(path26, closer) {
185147
185144
  if (!closer)
185148
185145
  return;
185149
- let list = this._closers.get(path30);
185146
+ let list = this._closers.get(path26);
185150
185147
  if (!list) {
185151
185148
  list = [];
185152
- this._closers.set(path30, list);
185149
+ this._closers.set(path26, list);
185153
185150
  }
185154
185151
  list.push(closer);
185155
185152
  }
@@ -185180,8 +185177,6 @@ var chokidar_default = { watch, FSWatcher };
185180
185177
 
185181
185178
  // node_modules/.pnpm/@specific+sdk@file+..+sdk/node_modules/@specific/sdk/dist/index.js
185182
185179
  var import_code_frame = __toESM(require_lib2(), 1);
185183
- import * as fs3 from "fs";
185184
- import * as path3 from "path";
185185
185180
  import * as fs5 from "fs";
185186
185181
  import * as path5 from "path";
185187
185182
  import * as net from "net";
@@ -185193,17 +185188,14 @@ import { createReadStream } from "fs";
185193
185188
  import { createTarExtractor, extractTo } from "tar-vern";
185194
185189
  import { spawn as spawn2 } from "child_process";
185195
185190
  import { readFile, writeFile } from "fs/promises";
185196
- import { existsSync as existsSync52 } from "fs";
185191
+ import { existsSync as existsSync5 } from "fs";
185197
185192
  import * as fs6 from "fs";
185198
185193
  import * as path6 from "path";
185199
185194
  import * as http from "http";
185200
- import * as https from "https";
185201
185195
  import * as fs7 from "fs";
185202
185196
  import * as path7 from "path";
185203
185197
  import { fileURLToPath } from "url";
185204
- import httpProxy from "http-proxy";
185205
185198
  import * as net2 from "net";
185206
- import * as net3 from "net";
185207
185199
  import { spawn as spawn3 } from "child_process";
185208
185200
  import { readFile as readFile2, writeFile as writeFile2, mkdir } from "fs/promises";
185209
185201
  import { existsSync as existsSync8 } from "fs";
@@ -185211,7 +185203,7 @@ import * as path8 from "path";
185211
185203
  import * as crypto from "crypto";
185212
185204
  import * as http2 from "http";
185213
185205
  import * as crypto2 from "crypto";
185214
- import * as net4 from "net";
185206
+ import * as net3 from "net";
185215
185207
  import * as fs8 from "fs";
185216
185208
  import * as path9 from "path";
185217
185209
  import { spawn as spawn4 } from "child_process";
@@ -185221,32 +185213,17 @@ import * as path10 from "path";
185221
185213
  import { spawnSync } from "child_process";
185222
185214
  import * as fs10 from "fs";
185223
185215
  import * as path11 from "path";
185224
- import * as net5 from "net";
185216
+ import * as net4 from "net";
185225
185217
  import { spawn as spawn5 } from "child_process";
185218
+ import * as net5 from "net";
185226
185219
  import * as fs11 from "fs";
185227
185220
  import * as path12 from "path";
185228
- import * as os3 from "os";
185229
- import forge from "node-forge";
185230
- import { execSync as execSync2 } from "child_process";
185231
- import DNS from "dns2";
185232
- import { execSync as execSync3 } from "child_process";
185233
- import * as fs13 from "fs";
185234
- import * as os5 from "os";
185235
- import * as path13 from "path";
185236
- import * as fs12 from "fs";
185237
- import * as os4 from "os";
185238
- import * as fs14 from "fs";
185239
- import * as path14 from "path";
185240
185221
  import { generateSlug } from "random-word-slugs";
185241
185222
  import { EventEmitter as EventEmitter2 } from "node:events";
185242
185223
  import * as net6 from "node:net";
185243
- import * as fs15 from "fs";
185244
- import * as path15 from "path";
185245
- import * as os6 from "os";
185246
- import * as net7 from "net";
185247
185224
  import { EventEmitter as EventEmitter22 } from "events";
185248
- import { execSync as execSync4 } from "child_process";
185249
- import * as path17 from "path";
185225
+ import { execSync as execSync2 } from "child_process";
185226
+ import * as path14 from "path";
185250
185227
  var __create2 = Object.create;
185251
185228
  var __defProp2 = Object.defineProperty;
185252
185229
  var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor;
@@ -185287,8 +185264,8 @@ var require_dist = __commonJS2({
185287
185264
  var $global, $module, $NaN = NaN;
185288
185265
  if ("undefined" != typeof window ? $global = window : "undefined" != typeof self ? $global = self : "undefined" != typeof global ? ($global = global).require = __require2 : $global = this, void 0 === $global || void 0 === $global.Array) throw new Error("no global object found");
185289
185266
  if ("undefined" != typeof module && ($module = module), !$global.fs && $global.require) try {
185290
- var fs172 = $global.require("fs");
185291
- "object" == typeof fs172 && null !== fs172 && 0 !== Object.keys(fs172).length && ($global.fs = fs172);
185267
+ var fs132 = $global.require("fs");
185268
+ "object" == typeof fs132 && null !== fs132 && 0 !== Object.keys(fs132).length && ($global.fs = fs132);
185292
185269
  } catch (e) {
185293
185270
  }
185294
185271
  if (!$global.fs) {
@@ -368888,68 +368865,6 @@ var PortAllocator = class {
368888
368865
  return port;
368889
368866
  }
368890
368867
  };
368891
- var PORT_RANGE_START2 = 4e4;
368892
- var PORT_RANGE_END2 = 49999;
368893
- var StablePortAllocator = class {
368894
- portsDir;
368895
- portsFilePath;
368896
- savedPorts = {};
368897
- usedPorts = /* @__PURE__ */ new Set();
368898
- constructor(projectRoot, key = "default") {
368899
- this.portsDir = path3.join(projectRoot, ".specific", "keys", key);
368900
- this.portsFilePath = path3.join(this.portsDir, "ports.json");
368901
- this.loadPorts();
368902
- }
368903
- loadPorts() {
368904
- if (!fs3.existsSync(this.portsFilePath)) {
368905
- return;
368906
- }
368907
- try {
368908
- const content = fs3.readFileSync(this.portsFilePath, "utf-8");
368909
- const data = JSON.parse(content);
368910
- if (data.version === 1 && data.ports) {
368911
- this.savedPorts = data.ports;
368912
- for (const port of Object.values(this.savedPorts)) {
368913
- this.usedPorts.add(port);
368914
- }
368915
- }
368916
- } catch {
368917
- this.savedPorts = {};
368918
- }
368919
- }
368920
- savePorts() {
368921
- if (!fs3.existsSync(this.portsDir)) {
368922
- fs3.mkdirSync(this.portsDir, { recursive: true });
368923
- }
368924
- const data = {
368925
- version: 1,
368926
- ports: this.savedPorts
368927
- };
368928
- fs3.writeFileSync(this.portsFilePath, JSON.stringify(data, null, 2));
368929
- }
368930
- allocateRandom() {
368931
- const rangeSize = PORT_RANGE_END2 - PORT_RANGE_START2 + 1;
368932
- if (this.usedPorts.size >= rangeSize) {
368933
- throw new Error("No available ports in range");
368934
- }
368935
- let port;
368936
- do {
368937
- port = PORT_RANGE_START2 + Math.floor(Math.random() * rangeSize);
368938
- } while (this.usedPorts.has(port));
368939
- this.usedPorts.add(port);
368940
- return port;
368941
- }
368942
- allocate(key) {
368943
- const savedPort = this.savedPorts[key];
368944
- if (savedPort !== void 0) {
368945
- return savedPort;
368946
- }
368947
- const port = this.allocateRandom();
368948
- this.savedPorts[key] = port;
368949
- this.savePorts();
368950
- return port;
368951
- }
368952
- };
368953
368868
  var BinaryNotFoundError = class extends Error {
368954
368869
  constructor(binaryName, version, platformInfo) {
368955
368870
  super(
@@ -368986,10 +368901,10 @@ function getLibraryEnv(binary) {
368986
368901
  if (!binary.libraryPath) {
368987
368902
  return {};
368988
368903
  }
368989
- const platform62 = os2.platform();
368990
- if (platform62 === "darwin") {
368904
+ const platform22 = os2.platform();
368905
+ if (platform22 === "darwin") {
368991
368906
  return { DYLD_LIBRARY_PATH: binary.libraryPath };
368992
- } else if (platform62 === "linux") {
368907
+ } else if (platform22 === "linux") {
368993
368908
  return { LD_LIBRARY_PATH: binary.libraryPath };
368994
368909
  }
368995
368910
  return {};
@@ -368998,11 +368913,11 @@ function getBinBaseDir() {
368998
368913
  return path4.join(os2.homedir(), ".specific", "bin");
368999
368914
  }
369000
368915
  function getPlatformInfo() {
369001
- const platform62 = os2.platform();
368916
+ const platform22 = os2.platform();
369002
368917
  const arch22 = os2.arch();
369003
- if (platform62 !== "darwin" && platform62 !== "linux") {
368918
+ if (platform22 !== "darwin" && platform22 !== "linux") {
369004
368919
  throw new Error(
369005
- `Unsupported platform: ${platform62}. Only macOS and Linux are supported.`
368920
+ `Unsupported platform: ${platform22}. Only macOS and Linux are supported.`
369006
368921
  );
369007
368922
  }
369008
368923
  const archStr = arch22;
@@ -369016,7 +368931,7 @@ function getPlatformInfo() {
369016
368931
  `Unsupported architecture: ${arch22}. Only x64 and arm64 are supported.`
369017
368932
  );
369018
368933
  }
369019
- return { platform: platform62, arch: mappedArch };
368934
+ return { platform: platform22, arch: mappedArch };
369020
368935
  }
369021
368936
  function getBinaryDir(definition, version, platformInfo) {
369022
368937
  return path4.join(
@@ -369547,7 +369462,7 @@ async function parseLocalFile(content) {
369547
369462
  return { secrets, configs };
369548
369463
  }
369549
369464
  async function loadLocal() {
369550
- if (!existsSync52(LOCAL_FILE)) {
369465
+ if (!existsSync5(LOCAL_FILE)) {
369551
369466
  return { secrets: /* @__PURE__ */ new Map(), configs: /* @__PURE__ */ new Map() };
369552
369467
  }
369553
369468
  const content = await readFile(LOCAL_FILE, "utf-8");
@@ -369587,7 +369502,7 @@ ${newLine}
369587
369502
  }
369588
369503
  async function saveLocalSecret(name, value) {
369589
369504
  let content = "";
369590
- if (existsSync52(LOCAL_FILE)) {
369505
+ if (existsSync5(LOCAL_FILE)) {
369591
369506
  content = await readFile(LOCAL_FILE, "utf-8");
369592
369507
  } else {
369593
369508
  content = HEADER_COMMENT;
@@ -369597,7 +369512,7 @@ async function saveLocalSecret(name, value) {
369597
369512
  }
369598
369513
  async function saveLocalConfig(name, value) {
369599
369514
  let content = "";
369600
- if (existsSync52(LOCAL_FILE)) {
369515
+ if (existsSync5(LOCAL_FILE)) {
369601
369516
  content = await readFile(LOCAL_FILE, "utf-8");
369602
369517
  } else {
369603
369518
  content = HEADER_COMMENT;
@@ -370227,13 +370142,6 @@ var InstanceStateManager = class {
370227
370142
  var __dirname = path7.dirname(fileURLToPath(import.meta.url));
370228
370143
  var adminDir = path7.join(__dirname, "admin");
370229
370144
  var _embeddedAdmin = null;
370230
- var HTTP_PORT = 80;
370231
- var HTTPS_PORT = 443;
370232
- var FALLBACK_HTTPS_PORT = 3443;
370233
- var DOMAIN_SUFFIX = ".spcf.localhost";
370234
- var ADMIN_DOMAIN = "spcf.localhost";
370235
- var DRIZZLE_GATEWAY_PREFIX = "__drizzle_gateway";
370236
- var TEMPORAL_UI_PREFIX = "__temporal";
370237
370145
  var MIME_TYPES = {
370238
370146
  ".html": "text/html",
370239
370147
  ".css": "text/css",
@@ -370248,399 +370156,6 @@ var MIME_TYPES = {
370248
370156
  ".woff2": "font/woff2",
370249
370157
  ".txt": "text/plain"
370250
370158
  };
370251
- function isPortAvailable(port) {
370252
- return new Promise((resolve52) => {
370253
- const server = net2.createServer();
370254
- server.once("error", () => resolve52(false));
370255
- server.once("listening", () => {
370256
- server.close(() => resolve52(true));
370257
- });
370258
- server.listen(port, "0.0.0.0");
370259
- });
370260
- }
370261
- async function probeHttpsPort() {
370262
- for (const port of [HTTPS_PORT, FALLBACK_HTTPS_PORT]) {
370263
- if (await isPortAvailable(port)) return port;
370264
- }
370265
- return 0;
370266
- }
370267
- async function startHttpProxy(services, certificate, getState, instanceKey = "default", preferredHttpsPort) {
370268
- const serviceMap = /* @__PURE__ */ new Map();
370269
- const adminPortMap = /* @__PURE__ */ new Map();
370270
- const drizzleGatewayPortMap = /* @__PURE__ */ new Map();
370271
- const temporalUiPortMap = /* @__PURE__ */ new Map();
370272
- const buildMapKey = (serviceName, key) => {
370273
- return key === "default" ? serviceName : `${serviceName}.${key}`;
370274
- };
370275
- for (const svc of services) {
370276
- serviceMap.set(buildMapKey(svc.name, instanceKey), svc.port);
370277
- }
370278
- const updateServices = (registryServices, keys) => {
370279
- serviceMap.clear();
370280
- adminPortMap.clear();
370281
- drizzleGatewayPortMap.clear();
370282
- temporalUiPortMap.clear();
370283
- for (const svc of registryServices) {
370284
- serviceMap.set(buildMapKey(svc.serviceName, svc.key), svc.port);
370285
- }
370286
- for (const [key, registration] of Object.entries(keys)) {
370287
- adminPortMap.set(key, registration.adminPort);
370288
- if (registration.drizzleGatewayPort !== void 0) {
370289
- drizzleGatewayPortMap.set(key, registration.drizzleGatewayPort);
370290
- }
370291
- if (registration.temporalUiPort !== void 0) {
370292
- temporalUiPortMap.set(key, registration.temporalUiPort);
370293
- }
370294
- }
370295
- writeLog("proxy", `Updated service map: ${serviceMap.size} services, ${adminPortMap.size} admin instances, ${drizzleGatewayPortMap.size} drizzle gateways`);
370296
- };
370297
- const { key: defaultKey, cert: defaultCert } = certificate;
370298
- const proxy = httpProxy.createProxyServer({});
370299
- proxy.on("error", (err, req, res) => {
370300
- const host = req.headers?.host || "unknown";
370301
- writeLog("proxy:error", `Bad Gateway for ${host}: ${err.message}`);
370302
- if (res instanceof http.ServerResponse && !res.headersSent) {
370303
- res.writeHead(502, { "Content-Type": "text/html" });
370304
- res.end(`<h1>Bad Gateway</h1><p>Service unavailable: ${err.message}</p>`);
370305
- }
370306
- });
370307
- proxy.on("proxyRes", (proxyRes, req) => {
370308
- const host = req.headers?.host || "";
370309
- if (extractTemporalUiKey(host) !== null || extractDrizzleGatewayKey(host) !== null) {
370310
- delete proxyRes.headers["x-frame-options"];
370311
- const csp = proxyRes.headers["content-security-policy"];
370312
- if (typeof csp === "string" && csp.includes("frame-ancestors")) {
370313
- proxyRes.headers["content-security-policy"] = csp.split(";").filter((d) => !d.trim().startsWith("frame-ancestors")).join(";");
370314
- }
370315
- }
370316
- });
370317
- const handleRequest = (req, res) => {
370318
- const host = req.headers.host || "";
370319
- const hostname2 = host.split(":")[0];
370320
- const temporalKey = extractTemporalUiKey(host);
370321
- if (temporalKey !== null) {
370322
- const temporalPort = temporalUiPortMap.get(temporalKey);
370323
- if (temporalPort) {
370324
- proxy.web(req, res, {
370325
- target: `http://127.0.0.1:${temporalPort}`
370326
- });
370327
- return;
370328
- }
370329
- res.writeHead(503, { "Content-Type": "text/html" });
370330
- res.end("<h1>Workflow Engine</h1><p>No workflow engine running for this instance.</p>");
370331
- return;
370332
- }
370333
- const drizzleKey = extractDrizzleGatewayKey(host);
370334
- if (drizzleKey !== null) {
370335
- const drizzlePort = drizzleGatewayPortMap.get(drizzleKey);
370336
- if (drizzlePort) {
370337
- proxy.web(req, res, {
370338
- target: `http://127.0.0.1:${drizzlePort}`
370339
- });
370340
- return;
370341
- }
370342
- res.writeHead(503, { "Content-Type": "text/html" });
370343
- res.end("<h1>Database Viewer</h1><p>No database viewer running for this instance.</p>");
370344
- return;
370345
- }
370346
- const adminKey = extractAdminKey(host);
370347
- if (adminKey !== null) {
370348
- const adminPort = adminPortMap.get(adminKey);
370349
- if (adminPort) {
370350
- const url = new URL(req.url || "/", "http://localhost");
370351
- if (url.pathname.startsWith("/api/")) {
370352
- proxy.web(req, res, { target: `http://127.0.0.1:${adminPort}` });
370353
- return;
370354
- }
370355
- serveStaticFile(res, url.pathname);
370356
- return;
370357
- }
370358
- if (adminKey === "default" && hostname2 === ADMIN_DOMAIN) {
370359
- res.writeHead(503, { "Content-Type": "text/html" });
370360
- res.end("<h1>Admin UI</h1><p>No dev instance running. Start <code>specific dev</code> first.</p>");
370361
- return;
370362
- }
370363
- }
370364
- const route = extractServiceAndKey(host);
370365
- if (!route) {
370366
- return sendNotFound(res, null, serviceMap);
370367
- }
370368
- const mapKey = buildMapKey(route.serviceName, route.key);
370369
- const targetPort2 = serviceMap.get(mapKey);
370370
- if (!targetPort2) {
370371
- return sendNotFound(res, route.serviceName, serviceMap);
370372
- }
370373
- proxy.web(req, res, { target: `http://127.0.0.1:${targetPort2}` });
370374
- };
370375
- const handleUpgrade = (req, socket, head) => {
370376
- const host = req.headers.host || "";
370377
- const temporalKey = extractTemporalUiKey(host);
370378
- if (temporalKey !== null) {
370379
- const temporalPort = temporalUiPortMap.get(temporalKey);
370380
- if (temporalPort) {
370381
- proxy.ws(req, socket, head, { target: `http://127.0.0.1:${temporalPort}` });
370382
- return;
370383
- }
370384
- socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
370385
- return;
370386
- }
370387
- const drizzleKey = extractDrizzleGatewayKey(host);
370388
- if (drizzleKey !== null) {
370389
- const drizzlePort = drizzleGatewayPortMap.get(drizzleKey);
370390
- if (drizzlePort) {
370391
- proxy.ws(req, socket, head, { target: `http://127.0.0.1:${drizzlePort}` });
370392
- return;
370393
- }
370394
- socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
370395
- return;
370396
- }
370397
- const adminKey = extractAdminKey(host);
370398
- if (adminKey !== null) {
370399
- if (adminPortMap.has(adminKey)) {
370400
- socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
370401
- return;
370402
- }
370403
- }
370404
- const route = extractServiceAndKey(host);
370405
- if (!route) {
370406
- socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
370407
- return;
370408
- }
370409
- const mapKey = buildMapKey(route.serviceName, route.key);
370410
- const targetPort2 = serviceMap.get(mapKey);
370411
- if (!targetPort2) {
370412
- socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
370413
- return;
370414
- }
370415
- proxy.ws(req, socket, head, { target: `http://127.0.0.1:${targetPort2}` });
370416
- };
370417
- const createHttpsServer = (key, cert) => {
370418
- const server = https.createServer({ key, cert }, handleRequest);
370419
- server.on("upgrade", handleUpgrade);
370420
- return server;
370421
- };
370422
- const makeInstance = (httpsServer, actualHttpsPort, httpServer, actualHttpPort) => {
370423
- let currentHttpsServer = httpsServer;
370424
- return {
370425
- httpPort: actualHttpPort ?? 0,
370426
- httpsPort: actualHttpsPort,
370427
- updateServices,
370428
- updateCertificate(newCert) {
370429
- if (typeof currentHttpsServer.setSecureContext === "function") {
370430
- currentHttpsServer.setSecureContext({
370431
- key: newCert.key,
370432
- cert: newCert.cert
370433
- });
370434
- writeLog("proxy", "Certificate updated");
370435
- } else {
370436
- const oldServer = currentHttpsServer;
370437
- currentHttpsServer = createHttpsServer(newCert.key, newCert.cert);
370438
- currentHttpsServer.listen(actualHttpsPort, "0.0.0.0", () => {
370439
- writeLog("proxy", "Certificate updated (server restarted)");
370440
- });
370441
- oldServer.closeAllConnections?.();
370442
- oldServer.close();
370443
- }
370444
- },
370445
- async stop() {
370446
- return new Promise((resolve52) => {
370447
- const serverCount = httpServer ? 2 : 1;
370448
- let closed = 0;
370449
- const onClose = () => {
370450
- closed++;
370451
- if (closed === serverCount) {
370452
- clearTimeout(forceCloseTimeout);
370453
- resolve52();
370454
- }
370455
- };
370456
- currentHttpsServer.close(onClose);
370457
- httpServer?.close(onClose);
370458
- const forceCloseTimeout = setTimeout(() => {
370459
- currentHttpsServer.closeAllConnections?.();
370460
- httpServer?.closeAllConnections?.();
370461
- resolve52();
370462
- }, 2e3);
370463
- });
370464
- }
370465
- };
370466
- };
370467
- const useStandardPorts = !preferredHttpsPort || preferredHttpsPort === HTTPS_PORT;
370468
- if (useStandardPorts) {
370469
- try {
370470
- return await startStandardProxy();
370471
- } catch (err) {
370472
- const isPortError = err instanceof Error && (err.message.includes("already in use") || err.message.includes("Cannot bind to port"));
370473
- if (!isPortError) throw err;
370474
- writeLog("proxy", `Standard ports unavailable (${err instanceof Error ? err.message : err}), falling back to HTTPS-only`);
370475
- }
370476
- }
370477
- const targetPort = preferredHttpsPort && preferredHttpsPort !== HTTPS_PORT ? preferredHttpsPort : FALLBACK_HTTPS_PORT;
370478
- return startFallbackProxy(targetPort);
370479
- function startStandardProxy() {
370480
- const httpServer = http.createServer(handleRequest);
370481
- httpServer.on("upgrade", handleUpgrade);
370482
- const httpsServer = https.createServer(
370483
- { key: defaultKey, cert: defaultCert },
370484
- handleRequest
370485
- );
370486
- httpsServer.on("upgrade", handleUpgrade);
370487
- return new Promise((resolve52, reject) => {
370488
- let httpStarted = false;
370489
- let httpsStarted = false;
370490
- let failed = false;
370491
- const checkReady = () => {
370492
- if (failed) return;
370493
- if (httpStarted && httpsStarted) {
370494
- writeLog("proxy", `HTTP/HTTPS proxy started on ports ${HTTP_PORT}/${HTTPS_PORT}`);
370495
- resolve52(makeInstance(httpsServer, HTTPS_PORT, httpServer, HTTP_PORT));
370496
- }
370497
- };
370498
- const handleError = (port) => (err) => {
370499
- if (failed) return;
370500
- failed = true;
370501
- httpServer.close(() => {
370502
- });
370503
- httpsServer.close(() => {
370504
- });
370505
- if (err.code === "EACCES" || err.code === "EADDRINUSE") {
370506
- reject(new Error(`Port ${port} is already in use`));
370507
- } else {
370508
- reject(err);
370509
- }
370510
- };
370511
- httpServer.on("error", handleError(HTTP_PORT));
370512
- httpsServer.on("error", handleError(HTTPS_PORT));
370513
- httpServer.listen(HTTP_PORT, "0.0.0.0", () => {
370514
- httpStarted = true;
370515
- checkReady();
370516
- });
370517
- httpsServer.listen(HTTPS_PORT, "0.0.0.0", () => {
370518
- httpsStarted = true;
370519
- checkReady();
370520
- });
370521
- });
370522
- }
370523
- function startFallbackProxy(port) {
370524
- const httpsServer = https.createServer(
370525
- { key: defaultKey, cert: defaultCert },
370526
- handleRequest
370527
- );
370528
- httpsServer.on("upgrade", handleUpgrade);
370529
- return new Promise((resolve52, reject) => {
370530
- const tryPort = (p) => {
370531
- httpsServer.removeAllListeners("error");
370532
- httpsServer.on("error", (err) => {
370533
- if ((err.code === "EADDRINUSE" || err.code === "EACCES") && p !== 0) {
370534
- tryPort(0);
370535
- } else {
370536
- reject(err);
370537
- }
370538
- });
370539
- httpsServer.listen(p, "0.0.0.0", () => {
370540
- const addr = httpsServer.address();
370541
- const actualPort = typeof addr === "object" && addr ? addr.port : p;
370542
- writeLog("proxy", `HTTPS proxy started on port ${actualPort} (fallback mode)`);
370543
- resolve52(makeInstance(httpsServer, actualPort));
370544
- });
370545
- };
370546
- tryPort(port);
370547
- });
370548
- }
370549
- }
370550
- function extractServiceAndKey(host) {
370551
- const hostname2 = host.split(":")[0];
370552
- if (!hostname2 || !hostname2.endsWith(DOMAIN_SUFFIX)) {
370553
- return null;
370554
- }
370555
- const prefix = hostname2.slice(0, -DOMAIN_SUFFIX.length);
370556
- if (!prefix) {
370557
- return null;
370558
- }
370559
- const parts = prefix.split(".");
370560
- if (parts.length === 1 && parts[0]) {
370561
- return { serviceName: parts[0], key: "default" };
370562
- } else if (parts.length === 2 && parts[0] && parts[1]) {
370563
- return { serviceName: parts[0], key: parts[1] };
370564
- }
370565
- return null;
370566
- }
370567
- function extractDrizzleGatewayKey(host) {
370568
- const hostname2 = host.split(":")[0];
370569
- if (!hostname2 || !hostname2.endsWith(DOMAIN_SUFFIX)) {
370570
- return null;
370571
- }
370572
- const prefix = hostname2.slice(0, -DOMAIN_SUFFIX.length);
370573
- if (!prefix) {
370574
- return null;
370575
- }
370576
- const parts = prefix.split(".");
370577
- if (parts.length === 1 && parts[0] === DRIZZLE_GATEWAY_PREFIX) {
370578
- return "default";
370579
- } else if (parts.length === 2 && parts[0] === DRIZZLE_GATEWAY_PREFIX && parts[1]) {
370580
- return parts[1];
370581
- }
370582
- return null;
370583
- }
370584
- function extractTemporalUiKey(host) {
370585
- const hostname2 = host.split(":")[0];
370586
- if (!hostname2 || !hostname2.endsWith(DOMAIN_SUFFIX)) {
370587
- return null;
370588
- }
370589
- const prefix = hostname2.slice(0, -DOMAIN_SUFFIX.length);
370590
- if (!prefix) {
370591
- return null;
370592
- }
370593
- const parts = prefix.split(".");
370594
- if (parts.length === 1 && parts[0] === TEMPORAL_UI_PREFIX) {
370595
- return "default";
370596
- } else if (parts.length === 2 && parts[0] === TEMPORAL_UI_PREFIX && parts[1]) {
370597
- return parts[1];
370598
- }
370599
- return null;
370600
- }
370601
- function extractAdminKey(host) {
370602
- const hostname2 = host.split(":")[0];
370603
- if (!hostname2) {
370604
- return null;
370605
- }
370606
- if (hostname2 === ADMIN_DOMAIN) {
370607
- return "default";
370608
- }
370609
- if (!hostname2.endsWith(DOMAIN_SUFFIX)) {
370610
- return null;
370611
- }
370612
- const prefix = hostname2.slice(0, -DOMAIN_SUFFIX.length);
370613
- if (!prefix) {
370614
- return null;
370615
- }
370616
- const parts = prefix.split(".");
370617
- if (parts.length === 1 && parts[0]) {
370618
- return parts[0];
370619
- }
370620
- return null;
370621
- }
370622
- function sendNotFound(res, requestedService, serviceMap) {
370623
- const serviceList = Array.from(serviceMap.keys()).map((key) => {
370624
- const parts = key.split(".");
370625
- if (parts.length === 1) {
370626
- return `<li>${parts[0]}.spcf.localhost</li>`;
370627
- } else {
370628
- return `<li>${key}.spcf.localhost</li>`;
370629
- }
370630
- }).join("\n");
370631
- const message = requestedService ? `No service named "${requestedService}" is running.` : "Invalid host header.";
370632
- res.writeHead(404, { "Content-Type": "text/html" });
370633
- res.end(`<!DOCTYPE html>
370634
- <html>
370635
- <head><title>Service Not Found</title></head>
370636
- <body>
370637
- <h1>Service Not Found</h1>
370638
- <p>${message}</p>
370639
- ${serviceMap.size > 0 ? `<p>Available services:</p>
370640
- <ul>${serviceList}</ul>` : "<p>No services are currently exposed.</p>"}
370641
- </body>
370642
- </html>`);
370643
- }
370644
370159
  function serveStaticFile(res, pathname) {
370645
370160
  if (_embeddedAdmin) {
370646
370161
  return serveEmbeddedFile(res, pathname);
@@ -370740,7 +370255,7 @@ function serveFileContent(res, filePath, contentType, statusCode = 200) {
370740
370255
  res.end("<h1>Internal Server Error</h1>");
370741
370256
  }
370742
370257
  }
370743
- async function startAdminServer(getState) {
370258
+ async function startAdminServer(getState, listenPort = 0) {
370744
370259
  return new Promise((resolve52, reject) => {
370745
370260
  const server = http.createServer((req, res) => {
370746
370261
  const url = new URL(req.url || "/", "http://localhost");
@@ -370763,14 +370278,14 @@ async function startAdminServer(getState) {
370763
370278
  serveStaticFile(res, pathname);
370764
370279
  });
370765
370280
  server.on("error", reject);
370766
- server.listen(0, "127.0.0.1", () => {
370281
+ server.listen(listenPort, "127.0.0.1", () => {
370767
370282
  const addr = server.address();
370768
370283
  if (!addr || typeof addr === "string") {
370769
370284
  reject(new Error("Failed to get server address"));
370770
370285
  return;
370771
370286
  }
370772
370287
  const port = addr.port;
370773
- writeLog("admin", `Admin API server started on port ${port}`);
370288
+ writeLog("admin", `Admin server started on port ${port}`);
370774
370289
  resolve52({
370775
370290
  port,
370776
370291
  stop: () => new Promise((res, rej) => {
@@ -370921,7 +370436,7 @@ async function waitForTcpPort2(host, port, timeoutMs = 3e4) {
370921
370436
  }
370922
370437
  function checkTcpPort2(host, port) {
370923
370438
  return new Promise((resolve52) => {
370924
- const socket = new net3.Socket();
370439
+ const socket = new net2.Socket();
370925
370440
  socket.setTimeout(1e3);
370926
370441
  socket.on("connect", () => {
370927
370442
  socket.destroy();
@@ -371159,7 +370674,7 @@ async function waitForTcpPort3(host, port, timeoutMs = 3e4) {
371159
370674
  }
371160
370675
  function checkTcpPort3(host, port) {
371161
370676
  return new Promise((resolve52) => {
371162
- const socket = new net4.Socket();
370677
+ const socket = new net3.Socket();
371163
370678
  socket.setTimeout(1e3);
371164
370679
  socket.on("connect", () => {
371165
370680
  socket.destroy();
@@ -371553,7 +371068,7 @@ async function waitForTcpPort4(host, port, timeoutMs = 3e4) {
371553
371068
  }
371554
371069
  function checkTcpPort4(host, port) {
371555
371070
  return new Promise((resolve52) => {
371556
- const socket = new net5.Socket();
371071
+ const socket = new net4.Socket();
371557
371072
  socket.setTimeout(1e3);
371558
371073
  socket.on("connect", () => {
371559
371074
  socket.destroy();
@@ -371821,418 +371336,86 @@ function watchConfigFile(configPath, debounceMs, onChange) {
371821
371336
  }
371822
371337
  };
371823
371338
  }
371824
- function getCADir() {
371825
- return path12.join(os3.homedir(), ".specific", "root_ca");
371826
- }
371827
- var EXPECTED_DOMAIN = "spcf.localhost";
371828
- function caFilesExist() {
371829
- const caDir = getCADir();
371830
- const keyPath = path12.join(caDir, "ca.key");
371831
- const certPath = path12.join(caDir, "ca.crt");
371832
- if (!fs11.existsSync(keyPath) || !fs11.existsSync(certPath)) {
371833
- return false;
371834
- }
371835
- try {
371836
- const certPem = fs11.readFileSync(certPath, "utf-8");
371837
- const cert = forge.pki.certificateFromPem(certPem);
371838
- const nameConstraints = cert.getExtension("nameConstraints");
371839
- if (!nameConstraints) {
371840
- return false;
371841
- }
371842
- return nameConstraints.value.includes(EXPECTED_DOMAIN);
371843
- } catch {
371844
- return false;
371845
- }
371846
- }
371847
- function caInstalledInTrustStore() {
371848
- if (!caFilesExist()) {
371849
- return false;
371339
+ var BLOCK_SIZE = 100;
371340
+ var FIRST_BLOCK_START = 3e3;
371341
+ var MAX_BLOCKS = 100;
371342
+ var BlockPortAllocator = class _BlockPortAllocator {
371343
+ basePort;
371344
+ nextServiceOffset = 1;
371345
+ nextResourceOffset = BLOCK_SIZE - 1;
371346
+ // starts at 99
371347
+ constructor(basePort) {
371348
+ this.basePort = basePort;
371850
371349
  }
371851
- const platform62 = os3.platform();
371852
- const certPath = path12.join(getCADir(), "ca.crt");
371853
- const diskCert = fs11.readFileSync(certPath, "utf-8").replace(/\r\n/g, "\n").trim();
371854
- try {
371855
- if (platform62 === "darwin") {
371856
- const keychainCert = execSync2(
371857
- `security find-certificate -c "Specific Local Development CA" -p "${getLoginKeychainPath()}"`,
371858
- { encoding: "utf-8" }
371859
- ).replace(/\r\n/g, "\n").trim();
371860
- if (keychainCert !== diskCert) {
371861
- return false;
371862
- }
371863
- const trustOutput = execSync2(
371864
- `security dump-trust-settings 2>&1`,
371865
- { encoding: "utf-8" }
371866
- );
371867
- const caSection = trustOutput.split("Specific Local Development CA");
371868
- if (caSection.length < 2) {
371869
- return false;
371870
- }
371871
- const afterCA = caSection[1].split(/^Cert \d+:/m)[0];
371872
- return afterCA.includes("Policy OID");
371873
- } else if (platform62 === "linux") {
371874
- const trustPaths = [
371875
- "/usr/local/share/ca-certificates/specific-local-ca.crt",
371876
- // Debian/Ubuntu
371877
- "/etc/pki/ca-trust/source/anchors/specific-local-ca.crt",
371878
- // RHEL/Fedora
371879
- "/etc/ca-certificates/trust-source/anchors/specific-local-ca.crt"
371880
- // Arch
371881
- ];
371882
- for (const trustPath of trustPaths) {
371883
- if (fs11.existsSync(trustPath)) {
371884
- const trustedCert = fs11.readFileSync(trustPath, "utf-8").replace(/\r\n/g, "\n").trim();
371885
- return trustedCert === diskCert;
371886
- }
371350
+ /**
371351
+ * Create a BlockPortAllocator by probing for an available port block.
371352
+ */
371353
+ static async create() {
371354
+ for (let i = 0; i < MAX_BLOCKS; i++) {
371355
+ const base = FIRST_BLOCK_START + i * BLOCK_SIZE;
371356
+ if (await isPortAvailable(base)) {
371357
+ return new _BlockPortAllocator(base);
371887
371358
  }
371888
- return false;
371889
371359
  }
371890
- } catch {
371891
- return false;
371892
- }
371893
- return false;
371894
- }
371895
- function createNameConstraintsExtension(permittedDNSNames) {
371896
- const nameConstraintsOID = "2.5.29.30";
371897
- const permittedSubtrees = permittedDNSNames.map((dnsName) => {
371898
- return forge.asn1.create(
371899
- forge.asn1.Class.UNIVERSAL,
371900
- forge.asn1.Type.SEQUENCE,
371901
- true,
371902
- [
371903
- // dNSName [2] IA5String
371904
- forge.asn1.create(
371905
- forge.asn1.Class.CONTEXT_SPECIFIC,
371906
- 2,
371907
- false,
371908
- dnsName
371909
- )
371910
- ]
371360
+ throw new Error(
371361
+ `No available port block found (tried ${FIRST_BLOCK_START}\u2013${FIRST_BLOCK_START + MAX_BLOCKS * BLOCK_SIZE - 1})`
371911
371362
  );
371912
- });
371913
- const nameConstraints = forge.asn1.create(
371914
- forge.asn1.Class.UNIVERSAL,
371915
- forge.asn1.Type.SEQUENCE,
371916
- true,
371917
- [
371918
- // permittedSubtrees [0] IMPLICIT GeneralSubtrees
371919
- forge.asn1.create(
371920
- forge.asn1.Class.CONTEXT_SPECIFIC,
371921
- 0,
371922
- true,
371923
- permittedSubtrees
371924
- )
371925
- ]
371926
- );
371927
- return {
371928
- id: nameConstraintsOID,
371929
- critical: true,
371930
- value: forge.asn1.toDer(nameConstraints).getBytes()
371931
- };
371932
- }
371933
- function generateRootCA() {
371934
- const keys = forge.pki.rsa.generateKeyPair(2048);
371935
- const cert = forge.pki.createCertificate();
371936
- cert.publicKey = keys.publicKey;
371937
- cert.serialNumber = "01";
371938
- cert.validity.notBefore = /* @__PURE__ */ new Date();
371939
- cert.validity.notAfter = /* @__PURE__ */ new Date();
371940
- cert.validity.notAfter.setFullYear(
371941
- cert.validity.notBefore.getFullYear() + 10
371942
- );
371943
- const attrs = [
371944
- { name: "commonName", value: "Specific Local Development CA" },
371945
- { name: "organizationName", value: "Specific" }
371946
- ];
371947
- cert.setSubject(attrs);
371948
- cert.setIssuer(attrs);
371949
- const nameConstraintsExt = createNameConstraintsExtension([
371950
- ".spcf.localhost",
371951
- "spcf.localhost"
371952
- ]);
371953
- cert.setExtensions([
371954
- { name: "basicConstraints", cA: true, critical: true },
371955
- { name: "keyUsage", keyCertSign: true, cRLSign: true, critical: true },
371956
- nameConstraintsExt
371957
- ]);
371958
- cert.sign(keys.privateKey, forge.md.sha256.create());
371959
- return {
371960
- key: forge.pki.privateKeyToPem(keys.privateKey),
371961
- cert: forge.pki.certificateToPem(cert)
371962
- };
371963
- }
371964
- function saveCA(key, cert) {
371965
- const caDir = getCADir();
371966
- fs11.mkdirSync(caDir, { recursive: true, mode: 448 });
371967
- const keyPath = path12.join(caDir, "ca.key");
371968
- const certPath = path12.join(caDir, "ca.crt");
371969
- fs11.writeFileSync(keyPath, key, { mode: 384 });
371970
- fs11.writeFileSync(certPath, cert, { mode: 420 });
371971
- return certPath;
371972
- }
371973
- function removeCA() {
371974
- const caDir = getCADir();
371975
- const keyPath = path12.join(caDir, "ca.key");
371976
- const certPath = path12.join(caDir, "ca.crt");
371977
- if (fs11.existsSync(keyPath)) {
371978
- fs11.unlinkSync(keyPath);
371979
371363
  }
371980
- if (fs11.existsSync(certPath)) {
371981
- fs11.unlinkSync(certPath);
371364
+ getBasePort() {
371365
+ return this.basePort;
371982
371366
  }
371983
- }
371984
- function getLoginKeychainPath() {
371985
- return path12.join(os3.homedir(), "Library", "Keychains", "login.keychain-db");
371986
- }
371987
- function getCADeleteCommand() {
371988
- return `security delete-certificate -c "Specific Local Development CA" "${getLoginKeychainPath()}"`;
371989
- }
371990
- function getCATrustCommand(certPath) {
371991
- return `security add-trusted-cert -r trustRoot -p ssl -p basic -k "${getLoginKeychainPath()}" "${certPath}"`;
371992
- }
371993
- function getCAInstallCommands(certPath) {
371994
- const platform62 = os3.platform();
371995
- if (platform62 === "darwin") {
371996
- return [
371997
- `${getCADeleteCommand()} 2>/dev/null || true`,
371998
- getCATrustCommand(certPath)
371999
- ];
372000
- } else if (platform62 === "linux") {
372001
- if (fs11.existsSync("/usr/local/share/ca-certificates")) {
372002
- return [
372003
- `cp "${certPath}" /usr/local/share/ca-certificates/specific-local-ca.crt`,
372004
- "update-ca-certificates"
372005
- ];
372006
- } else if (fs11.existsSync("/etc/pki/ca-trust/source/anchors")) {
372007
- return [
372008
- `cp "${certPath}" /etc/pki/ca-trust/source/anchors/specific-local-ca.crt`,
372009
- "update-ca-trust extract"
372010
- ];
372011
- } else if (fs11.existsSync("/etc/ca-certificates/trust-source/anchors")) {
372012
- return [
372013
- `cp "${certPath}" /etc/ca-certificates/trust-source/anchors/specific-local-ca.crt`,
372014
- "trust extract-compat"
372015
- ];
372016
- }
372017
- throw new Error("Could not detect Linux certificate trust mechanism");
371367
+ getAdminPort() {
371368
+ return this.basePort;
372018
371369
  }
372019
- throw new Error(`Unsupported platform: ${platform62}`);
372020
- }
372021
- function generateCertificate(domain, keys = []) {
372022
- const caDir = getCADir();
372023
- const caCertPem = fs11.readFileSync(path12.join(caDir, "ca.crt"), "utf-8");
372024
- const caKeyPem = fs11.readFileSync(path12.join(caDir, "ca.key"), "utf-8");
372025
- const caCert = forge.pki.certificateFromPem(caCertPem);
372026
- const caKey = forge.pki.privateKeyFromPem(caKeyPem);
372027
- const certKeys = forge.pki.rsa.generateKeyPair(2048);
372028
- const cert = forge.pki.createCertificate();
372029
- cert.publicKey = certKeys.publicKey;
372030
- cert.serialNumber = forge.util.bytesToHex(forge.random.getBytesSync(16));
372031
- cert.validity.notBefore = /* @__PURE__ */ new Date();
372032
- cert.validity.notAfter = /* @__PURE__ */ new Date();
372033
- cert.validity.notAfter.setDate(cert.validity.notBefore.getDate() + 7);
372034
- cert.setSubject([{ name: "commonName", value: domain }]);
372035
- cert.setIssuer(caCert.subject.attributes);
372036
- const altNames = [
372037
- { type: 2, value: domain },
372038
- { type: 2, value: `*.${domain}` }
372039
- ];
372040
- for (const key of keys) {
372041
- if (key !== "default") {
372042
- altNames.push({ type: 2, value: `*.${key}.${domain}` });
372043
- }
372044
- }
372045
- cert.setExtensions([
372046
- { name: "basicConstraints", cA: false },
372047
- { name: "keyUsage", digitalSignature: true, keyEncipherment: true },
372048
- { name: "extKeyUsage", serverAuth: true },
372049
- { name: "subjectAltName", altNames }
372050
- ]);
372051
- cert.sign(caKey, forge.md.sha256.create());
372052
- return {
372053
- key: Buffer.from(forge.pki.privateKeyToPem(certKeys.privateKey)),
372054
- cert: Buffer.from(forge.pki.certificateToPem(cert))
372055
- };
372056
- }
372057
- var DNS_PORT = 15353;
372058
- async function startDnsServer(port = DNS_PORT) {
372059
- const server = DNS.createServer({
372060
- udp: true,
372061
- handle: (request, send) => {
372062
- const response = DNS.Packet.createResponseFromRequest(request);
372063
- for (const question of request.questions) {
372064
- if (question.type === DNS.Packet.TYPE.A) {
372065
- response.answers.push({
372066
- name: question.name,
372067
- type: DNS.Packet.TYPE.A,
372068
- class: DNS.Packet.CLASS.IN,
372069
- ttl: 300,
372070
- address: "127.0.0.1"
372071
- });
372072
- }
372073
- }
372074
- send(response);
372075
- }
372076
- });
372077
- await server.listen({
372078
- udp: { port, address: "127.0.0.1" }
372079
- });
372080
- writeLog("dns", `DNS server started on 127.0.0.1:${port}`);
372081
- return {
372082
- port,
372083
- async stop() {
372084
- await server.close();
372085
- writeLog("dns", "DNS server stopped");
372086
- }
372087
- };
372088
- }
372089
- var RESOLVER_FILE_MACOS = "/etc/resolver/spcf.localhost";
372090
- var OLD_RESOLVER_FILE_MACOS = "/etc/resolver/local.spcf.app";
372091
- var RESOLVER_FILE_LINUX = "/etc/systemd/resolved.conf.d/specific-local.conf";
372092
- function resolverConfigExists() {
372093
- const platform62 = os4.platform();
372094
- if (platform62 === "darwin") {
372095
- return fs12.existsSync(RESOLVER_FILE_MACOS);
372096
- } else if (platform62 === "linux") {
372097
- return fs12.existsSync(RESOLVER_FILE_LINUX);
372098
- }
372099
- return false;
372100
- }
372101
- function getResolverInstallCommands(port) {
372102
- const platform62 = os4.platform();
372103
- if (platform62 === "darwin") {
372104
- return [
372105
- "mkdir -p /etc/resolver",
372106
- `rm -f ${OLD_RESOLVER_FILE_MACOS}`,
372107
- `printf "nameserver 127.0.0.1\\nport ${port}\\n" > ${RESOLVER_FILE_MACOS}`
372108
- ];
372109
- } else if (platform62 === "linux") {
372110
- if (fs12.existsSync("/etc/systemd/resolved.conf.d") || fs12.existsSync("/etc/systemd")) {
372111
- return [
372112
- "mkdir -p /etc/systemd/resolved.conf.d",
372113
- `printf "[Resolve]\\nDNS=127.0.0.1:${port}\\nDomains=~spcf.localhost\\n" > ${RESOLVER_FILE_LINUX}`,
372114
- "systemctl restart systemd-resolved"
372115
- ];
371370
+ allocateServicePort() {
371371
+ const port = this.basePort + this.nextServiceOffset;
371372
+ this.nextServiceOffset++;
371373
+ if (this.nextServiceOffset >= this.nextResourceOffset) {
371374
+ throw new Error("Port block exhausted: too many services and resources");
372116
371375
  }
372117
- return [];
372118
- }
372119
- return [];
372120
- }
372121
- function systemSetupNeeded() {
372122
- return dnsSetupNeeded() || tlsSetupNeeded();
372123
- }
372124
- function dnsSetupNeeded() {
372125
- return !resolverConfigExists();
372126
- }
372127
- function tlsSetupNeeded() {
372128
- return !caInstalledInTrustStore();
372129
- }
372130
- function ensureCAGenerated() {
372131
- if (caFilesExist()) {
372132
- return false;
372133
- }
372134
- const { key, cert } = generateRootCA();
372135
- saveCA(key, cert);
372136
- return true;
372137
- }
372138
- function performDnsSetup() {
372139
- const commands = getResolverInstallCommands(DNS_PORT);
372140
- if (commands.length === 0) return;
372141
- if (os5.platform() === "darwin") {
372142
- execPrivilegedMacOS(commands);
372143
- } else {
372144
- execSync3(`sudo sh -c '${commands.join(" && ")}'`, { stdio: "inherit" });
372145
- }
372146
- }
372147
- function performTlsTrust() {
372148
- const wasGenerated = ensureCAGenerated();
372149
- const certPath = path13.join(getCADir(), "ca.crt");
372150
- try {
372151
- if (os5.platform() === "darwin") {
372152
- execSync3(`${getCADeleteCommand()} 2>/dev/null || true`, {
372153
- stdio: "inherit"
372154
- });
372155
- execSync3(getCATrustCommand(certPath), { stdio: "inherit" });
372156
- } else {
372157
- execSync3(
372158
- `sudo sh -c '${getCAInstallCommands(certPath).join(" && ")}'`,
372159
- { stdio: "inherit" }
372160
- );
372161
- }
372162
- } catch (err) {
372163
- if (wasGenerated) {
372164
- removeCA();
372165
- }
372166
- throw err;
372167
- }
372168
- }
372169
- function performSystemSetup() {
372170
- const wasGenerated = ensureCAGenerated();
372171
- const needsInstallCA = !caInstalledInTrustStore();
372172
- const needsResolver = !resolverConfigExists();
372173
- if (!needsInstallCA && !needsResolver) {
372174
- return;
372175
- }
372176
- const certPath = path13.join(getCADir(), "ca.crt");
372177
- const commands = [];
372178
- if (needsInstallCA) {
372179
- commands.push(...getCAInstallCommands(certPath));
372180
- }
372181
- if (needsResolver) {
372182
- commands.push(...getResolverInstallCommands(DNS_PORT));
372183
- }
372184
- if (commands.length === 0) {
372185
- return;
371376
+ return port;
372186
371377
  }
372187
- try {
372188
- execSync3(`sudo sh -c '${commands.join(" && ")}'`, { stdio: "inherit" });
372189
- } catch (err) {
372190
- if (wasGenerated) {
372191
- removeCA();
371378
+ allocateResourcePort() {
371379
+ const port = this.basePort + this.nextResourceOffset;
371380
+ this.nextResourceOffset--;
371381
+ if (this.nextResourceOffset <= this.nextServiceOffset) {
371382
+ throw new Error("Port block exhausted: too many services and resources");
372192
371383
  }
372193
- throw err;
371384
+ return port;
372194
371385
  }
372195
- }
372196
- function execPrivilegedMacOS(commands) {
372197
- if (commands.length === 0) return;
372198
- const scriptPath = path13.join(os5.tmpdir(), "specific-setup.sh");
372199
- const appPath = path13.join(os5.tmpdir(), "Specific.app");
372200
- try {
372201
- fs13.writeFileSync(
372202
- scriptPath,
372203
- "#!/bin/sh\nset -e\n" + commands.join("\n") + "\n",
372204
- { mode: 448 }
372205
- );
372206
- execSync3(
372207
- `osacompile -o '${appPath}' -e 'do shell script "sh ${scriptPath}" with administrator privileges'`
372208
- );
372209
- execSync3(`'${appPath}/Contents/MacOS/applet'`, { stdio: "pipe" });
372210
- } finally {
372211
- try {
372212
- fs13.unlinkSync(scriptPath);
372213
- } catch {
372214
- }
372215
- try {
372216
- fs13.rmSync(appPath, { recursive: true });
372217
- } catch {
372218
- }
371386
+ /**
371387
+ * Implements PortAllocatorInterface for compatibility with resource-starter.
371388
+ * Delegates to allocateResourcePort().
371389
+ */
371390
+ allocate(_key) {
371391
+ return this.allocateResourcePort();
372219
371392
  }
371393
+ };
371394
+ function isPortAvailable(port) {
371395
+ return new Promise((resolve52) => {
371396
+ const server = net5.createServer();
371397
+ server.once("error", () => resolve52(false));
371398
+ server.once("listening", () => {
371399
+ server.close(() => resolve52(true));
371400
+ });
371401
+ server.listen(port, "127.0.0.1");
371402
+ });
372220
371403
  }
372221
371404
  var StableSubdomainAllocator = class {
372222
371405
  tunnelsDir;
372223
371406
  tunnelsFilePath;
372224
371407
  baseSlug = null;
372225
371408
  constructor(projectRoot, key = "default") {
372226
- this.tunnelsDir = path14.join(projectRoot, ".specific", "keys", key);
372227
- this.tunnelsFilePath = path14.join(this.tunnelsDir, "tunnels.json");
371409
+ this.tunnelsDir = path12.join(projectRoot, ".specific", "keys", key);
371410
+ this.tunnelsFilePath = path12.join(this.tunnelsDir, "tunnels.json");
372228
371411
  this.loadTunnels();
372229
371412
  }
372230
371413
  loadTunnels() {
372231
- if (!fs14.existsSync(this.tunnelsFilePath)) {
371414
+ if (!fs11.existsSync(this.tunnelsFilePath)) {
372232
371415
  return;
372233
371416
  }
372234
371417
  try {
372235
- const content = fs14.readFileSync(this.tunnelsFilePath, "utf-8");
371418
+ const content = fs11.readFileSync(this.tunnelsFilePath, "utf-8");
372236
371419
  const data = JSON.parse(content);
372237
371420
  if (data.version === 1 && data.baseSlug) {
372238
371421
  this.baseSlug = data.baseSlug;
@@ -372242,14 +371425,14 @@ var StableSubdomainAllocator = class {
372242
371425
  }
372243
371426
  }
372244
371427
  saveTunnels() {
372245
- if (!fs14.existsSync(this.tunnelsDir)) {
372246
- fs14.mkdirSync(this.tunnelsDir, { recursive: true });
371428
+ if (!fs11.existsSync(this.tunnelsDir)) {
371429
+ fs11.mkdirSync(this.tunnelsDir, { recursive: true });
372247
371430
  }
372248
371431
  const data = {
372249
371432
  version: 1,
372250
371433
  baseSlug: this.baseSlug
372251
371434
  };
372252
- fs14.writeFileSync(this.tunnelsFilePath, JSON.stringify(data, null, 2));
371435
+ fs11.writeFileSync(this.tunnelsFilePath, JSON.stringify(data, null, 2));
372253
371436
  }
372254
371437
  generateBaseSlug() {
372255
371438
  return generateSlug(2, {
@@ -372397,382 +371580,6 @@ async function startTunnel(serviceName, endpointName, port, subdomain, callbacks
372397
371580
  stop: async () => tunnel.close()
372398
371581
  };
372399
371582
  }
372400
- var ProxyRegistryManager = class {
372401
- proxyDir;
372402
- ownerPath;
372403
- registryPath;
372404
- lockPath;
372405
- isOwner = false;
372406
- registryWatcher = null;
372407
- constructor() {
372408
- this.proxyDir = path15.join(os6.homedir(), ".specific", "proxy");
372409
- this.ownerPath = path15.join(this.proxyDir, "owner.json");
372410
- this.registryPath = path15.join(this.proxyDir, "registry.json");
372411
- this.lockPath = path15.join(this.proxyDir, "registry.lock");
372412
- }
372413
- ensureProxyDir() {
372414
- if (!fs15.existsSync(this.proxyDir)) {
372415
- fs15.mkdirSync(this.proxyDir, { recursive: true });
372416
- }
372417
- }
372418
- isProcessRunning(pid) {
372419
- try {
372420
- process.kill(pid, 0);
372421
- return true;
372422
- } catch (e) {
372423
- const err = e;
372424
- return err.code !== "ESRCH";
372425
- }
372426
- }
372427
- /**
372428
- * Check if the proxy is actually listening on its expected port.
372429
- * This catches cases where the owner process is alive but the proxy has crashed.
372430
- */
372431
- isProxyListening(port, timeoutMs = 1e3) {
372432
- return new Promise((resolve52) => {
372433
- const socket = new net7.Socket();
372434
- let resolved = false;
372435
- const cleanup = () => {
372436
- if (!resolved) {
372437
- resolved = true;
372438
- socket.destroy();
372439
- }
372440
- };
372441
- socket.setTimeout(timeoutMs);
372442
- socket.on("connect", () => {
372443
- cleanup();
372444
- resolve52(true);
372445
- });
372446
- socket.on("timeout", () => {
372447
- cleanup();
372448
- resolve52(false);
372449
- });
372450
- socket.on("error", () => {
372451
- cleanup();
372452
- resolve52(false);
372453
- });
372454
- socket.connect(port, "127.0.0.1");
372455
- });
372456
- }
372457
- /**
372458
- * Check if the proxy owner is healthy (process running AND proxy listening).
372459
- */
372460
- async isProxyOwnerHealthy(owner) {
372461
- if (!this.isProcessRunning(owner.pid)) {
372462
- return false;
372463
- }
372464
- const port = owner.httpsPort ?? 443;
372465
- const isListening = await this.isProxyListening(port);
372466
- return isListening;
372467
- }
372468
- async acquireLock(timeoutMs = 5e3) {
372469
- this.ensureProxyDir();
372470
- const startTime = Date.now();
372471
- while (Date.now() - startTime < timeoutMs) {
372472
- try {
372473
- const fd = fs15.openSync(
372474
- this.lockPath,
372475
- fs15.constants.O_CREAT | fs15.constants.O_EXCL | fs15.constants.O_WRONLY
372476
- );
372477
- fs15.writeSync(fd, String(process.pid));
372478
- fs15.closeSync(fd);
372479
- return () => {
372480
- try {
372481
- fs15.unlinkSync(this.lockPath);
372482
- } catch {
372483
- }
372484
- };
372485
- } catch (e) {
372486
- const err = e;
372487
- if (err.code === "EEXIST") {
372488
- try {
372489
- const lockPid = parseInt(
372490
- fs15.readFileSync(this.lockPath, "utf-8").trim(),
372491
- 10
372492
- );
372493
- if (!this.isProcessRunning(lockPid)) {
372494
- fs15.unlinkSync(this.lockPath);
372495
- continue;
372496
- }
372497
- } catch {
372498
- try {
372499
- fs15.unlinkSync(this.lockPath);
372500
- } catch {
372501
- }
372502
- continue;
372503
- }
372504
- await new Promise((resolve52) => setTimeout(resolve52, 100));
372505
- } else {
372506
- throw e;
372507
- }
372508
- }
372509
- }
372510
- throw new Error("Failed to acquire proxy registry lock (timeout)");
372511
- }
372512
- /**
372513
- * Try to claim ownership of the proxy.
372514
- * Returns true if this process is now the owner.
372515
- */
372516
- async claimProxyOwnership(key, httpsPort) {
372517
- const releaseLock = await this.acquireLock();
372518
- try {
372519
- if (fs15.existsSync(this.ownerPath)) {
372520
- const content = fs15.readFileSync(this.ownerPath, "utf-8");
372521
- const ownerFile2 = JSON.parse(content);
372522
- if (await this.isProxyOwnerHealthy(ownerFile2.owner)) {
372523
- return false;
372524
- }
372525
- }
372526
- const ownerFile = {
372527
- version: 1,
372528
- owner: {
372529
- pid: process.pid,
372530
- key,
372531
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
372532
- httpsPort
372533
- }
372534
- };
372535
- this.writeFileAtomic(this.ownerPath, ownerFile);
372536
- this.isOwner = true;
372537
- return true;
372538
- } finally {
372539
- releaseLock();
372540
- }
372541
- }
372542
- /**
372543
- * Release ownership of the proxy.
372544
- */
372545
- async releaseProxyOwnership() {
372546
- if (!this.isOwner) {
372547
- return;
372548
- }
372549
- const releaseLock = await this.acquireLock();
372550
- try {
372551
- if (fs15.existsSync(this.ownerPath)) {
372552
- const content = fs15.readFileSync(this.ownerPath, "utf-8");
372553
- const ownerFile = JSON.parse(content);
372554
- if (ownerFile.owner.pid === process.pid) {
372555
- fs15.unlinkSync(this.ownerPath);
372556
- }
372557
- }
372558
- this.isOwner = false;
372559
- } finally {
372560
- releaseLock();
372561
- }
372562
- }
372563
- /**
372564
- * Get the current proxy owner.
372565
- */
372566
- async getProxyOwner() {
372567
- if (!fs15.existsSync(this.ownerPath)) {
372568
- return null;
372569
- }
372570
- const releaseLock = await this.acquireLock();
372571
- try {
372572
- const content = fs15.readFileSync(this.ownerPath, "utf-8");
372573
- const ownerFile = JSON.parse(content);
372574
- if (!await this.isProxyOwnerHealthy(ownerFile.owner)) {
372575
- return null;
372576
- }
372577
- return ownerFile.owner;
372578
- } catch {
372579
- return null;
372580
- } finally {
372581
- releaseLock();
372582
- }
372583
- }
372584
- /**
372585
- * Check if this process is the proxy owner.
372586
- */
372587
- isProxyOwner() {
372588
- return this.isOwner;
372589
- }
372590
- /**
372591
- * Get the HTTPS port used by the current proxy owner.
372592
- * Returns 443 if unknown.
372593
- */
372594
- getProxyHttpsPort() {
372595
- try {
372596
- if (!fs15.existsSync(this.ownerPath)) return 443;
372597
- const content = fs15.readFileSync(this.ownerPath, "utf-8");
372598
- const ownerFile = JSON.parse(content);
372599
- return ownerFile.owner.httpsPort ?? 443;
372600
- } catch {
372601
- return 443;
372602
- }
372603
- }
372604
- /**
372605
- * Register services for a key.
372606
- */
372607
- async registerServices(key, adminPort, services, drizzleGatewayPort, temporalUiPort) {
372608
- const releaseLock = await this.acquireLock();
372609
- try {
372610
- const registry = this.readRegistry();
372611
- const registration = {
372612
- adminPort,
372613
- services
372614
- };
372615
- if (drizzleGatewayPort !== void 0) {
372616
- registration.drizzleGatewayPort = drizzleGatewayPort;
372617
- }
372618
- if (temporalUiPort !== void 0) {
372619
- registration.temporalUiPort = temporalUiPort;
372620
- }
372621
- registry.keys[key] = registration;
372622
- registry.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
372623
- this.writeFileAtomic(this.registryPath, registry);
372624
- } finally {
372625
- releaseLock();
372626
- }
372627
- }
372628
- /**
372629
- * Unregister all services for a key.
372630
- */
372631
- async unregisterServices(key) {
372632
- const releaseLock = await this.acquireLock();
372633
- try {
372634
- const registry = this.readRegistry();
372635
- delete registry.keys[key];
372636
- registry.lastUpdated = (/* @__PURE__ */ new Date()).toISOString();
372637
- this.writeFileAtomic(this.registryPath, registry);
372638
- } finally {
372639
- releaseLock();
372640
- }
372641
- }
372642
- /**
372643
- * Get all registered services (flattened view for proxy routing).
372644
- */
372645
- async getAllServices() {
372646
- const releaseLock = await this.acquireLock();
372647
- try {
372648
- const registry = this.readRegistry();
372649
- const services = [];
372650
- for (const [key, registration] of Object.entries(registry.keys)) {
372651
- for (const svc of registration.services) {
372652
- services.push({
372653
- key,
372654
- serviceName: svc.serviceName,
372655
- port: svc.port
372656
- });
372657
- }
372658
- }
372659
- return services;
372660
- } finally {
372661
- releaseLock();
372662
- }
372663
- }
372664
- /**
372665
- * Get all registered keys and their admin ports.
372666
- */
372667
- async getAllKeyRegistrations() {
372668
- const releaseLock = await this.acquireLock();
372669
- try {
372670
- const registry = this.readRegistry();
372671
- return { ...registry.keys };
372672
- } finally {
372673
- releaseLock();
372674
- }
372675
- }
372676
- /**
372677
- * Watch the registry for changes. Returns a cleanup function.
372678
- * Callback receives both the flattened services list and all key registrations (for admin ports).
372679
- * Uses chokidar with stat-polling for reliable detection of atomic file writes (rename) on macOS.
372680
- */
372681
- watchRegistry(onChange) {
372682
- this.ensureProxyDir();
372683
- if (!fs15.existsSync(this.registryPath)) {
372684
- const emptyRegistry = {
372685
- version: 1,
372686
- keys: {},
372687
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
372688
- };
372689
- this.writeFileAtomic(this.registryPath, emptyRegistry);
372690
- }
372691
- let debounceTimer = null;
372692
- const triggerUpdate = () => {
372693
- if (debounceTimer) {
372694
- clearTimeout(debounceTimer);
372695
- }
372696
- debounceTimer = setTimeout(async () => {
372697
- try {
372698
- const services = await this.getAllServices();
372699
- const keys = await this.getAllKeyRegistrations();
372700
- onChange(services, keys);
372701
- } catch {
372702
- }
372703
- }, 50);
372704
- };
372705
- this.registryWatcher = chokidar_default.watch(this.registryPath, {
372706
- persistent: true,
372707
- usePolling: true,
372708
- interval: 500
372709
- });
372710
- this.registryWatcher.on("change", triggerUpdate);
372711
- return () => {
372712
- if (debounceTimer) {
372713
- clearTimeout(debounceTimer);
372714
- }
372715
- this.registryWatcher?.close();
372716
- this.registryWatcher = null;
372717
- };
372718
- }
372719
- /**
372720
- * Attempt to become the proxy owner (election).
372721
- * This is called when the current owner is detected as dead.
372722
- */
372723
- async attemptElection(key, httpsPort) {
372724
- const releaseLock = await this.acquireLock();
372725
- try {
372726
- if (fs15.existsSync(this.ownerPath)) {
372727
- const content = fs15.readFileSync(this.ownerPath, "utf-8");
372728
- const ownerFile2 = JSON.parse(content);
372729
- if (await this.isProxyOwnerHealthy(ownerFile2.owner)) {
372730
- return false;
372731
- }
372732
- fs15.unlinkSync(this.ownerPath);
372733
- }
372734
- const ownerFile = {
372735
- version: 1,
372736
- owner: {
372737
- pid: process.pid,
372738
- key,
372739
- startedAt: (/* @__PURE__ */ new Date()).toISOString(),
372740
- httpsPort
372741
- }
372742
- };
372743
- this.writeFileAtomic(this.ownerPath, ownerFile);
372744
- this.isOwner = true;
372745
- return true;
372746
- } finally {
372747
- releaseLock();
372748
- }
372749
- }
372750
- readRegistry() {
372751
- if (!fs15.existsSync(this.registryPath)) {
372752
- return {
372753
- version: 1,
372754
- keys: {},
372755
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
372756
- };
372757
- }
372758
- try {
372759
- const content = fs15.readFileSync(this.registryPath, "utf-8");
372760
- return JSON.parse(content);
372761
- } catch {
372762
- return {
372763
- version: 1,
372764
- keys: {},
372765
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
372766
- };
372767
- }
372768
- }
372769
- writeFileAtomic(filePath, data) {
372770
- this.ensureProxyDir();
372771
- const tmpPath = filePath + ".tmp";
372772
- fs15.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
372773
- fs15.renameSync(tmpPath, filePath);
372774
- }
372775
- };
372776
371583
  async function loadConfigs() {
372777
371584
  const { configs } = await loadLocal();
372778
371585
  return configs;
@@ -372872,19 +371679,14 @@ var DevEnvironment = class extends TypedEventEmitter {
372872
371679
  startTime = null;
372873
371680
  // Core managers
372874
371681
  stateManager = null;
372875
- proxyRegistry = null;
372876
- // Running instances (class fields replacing useRef)
372877
- proxy = null;
371682
+ // Running instances
372878
371683
  adminServer = null;
372879
371684
  services = [];
372880
371685
  resources = /* @__PURE__ */ new Map();
372881
371686
  electricInstances = [];
372882
371687
  drizzleGateway = null;
372883
371688
  reshapeWatchers = [];
372884
- registryWatcherCleanup = null;
372885
- electionInterval = null;
372886
371689
  tunnels = [];
372887
- dnsServer = null;
372888
371690
  mailServers = /* @__PURE__ */ new Map();
372889
371691
  restartServices = null;
372890
371692
  // Config file watcher
@@ -372940,31 +371742,6 @@ var DevEnvironment = class extends TypedEventEmitter {
372940
371742
  this.started = true;
372941
371743
  this.shuttingDown = false;
372942
371744
  this.cancelled = false;
372943
- const setupNeeded = !this.tunnelEnabled && systemSetupNeeded();
372944
- if (setupNeeded) {
372945
- this.setStatus("setup");
372946
- this.systemLog("Setting up TLS certificates and DNS resolution");
372947
- try {
372948
- if (os7.platform() === "darwin") {
372949
- if (dnsSetupNeeded()) {
372950
- ensureCAGenerated();
372951
- performDnsSetup();
372952
- }
372953
- if (tlsSetupNeeded()) {
372954
- ensureCAGenerated();
372955
- performTlsTrust();
372956
- }
372957
- } else {
372958
- performSystemSetup();
372959
- }
372960
- this.systemLog("System setup complete");
372961
- } catch (err) {
372962
- const msg = err instanceof Error ? err.message : String(err);
372963
- this.setStatus("error");
372964
- this.emit("error", new Error(`System setup failed: ${msg}`));
372965
- return;
372966
- }
372967
- }
372968
371745
  return new Promise((resolve52) => {
372969
371746
  this.startResolve = resolve52;
372970
371747
  this.startInternal().then(() => {
@@ -372989,8 +371766,6 @@ var DevEnvironment = class extends TypedEventEmitter {
372989
371766
  if (this.shuttingDown) {
372990
371767
  this.systemLog("Force shutting down");
372991
371768
  const allProcesses = [
372992
- this.proxy,
372993
- this.dnsServer,
372994
371769
  ...this.services,
372995
371770
  ...this.electricInstances,
372996
371771
  this.drizzleGateway,
@@ -373022,17 +371797,7 @@ var DevEnvironment = class extends TypedEventEmitter {
373022
371797
  this.configWatcher.close();
373023
371798
  this.configWatcher = null;
373024
371799
  }
373025
- if (this.electionInterval) {
373026
- clearInterval(this.electionInterval);
373027
- this.electionInterval = null;
373028
- }
373029
- if (this.registryWatcherCleanup) {
373030
- this.registryWatcherCleanup();
373031
- this.registryWatcherCleanup = null;
373032
- }
373033
371800
  await Promise.all([
373034
- this.proxy?.stop(),
373035
- this.dnsServer?.stop(),
373036
371801
  this.adminServer?.stop(),
373037
371802
  ...this.services.map((service) => service.stop()),
373038
371803
  ...this.electricInstances.map((electric) => electric.stop()),
@@ -373045,16 +371810,10 @@ var DevEnvironment = class extends TypedEventEmitter {
373045
371810
  this.reshapeWatchers = [];
373046
371811
  this.restartServices = null;
373047
371812
  this.drizzleGateway = null;
373048
- this.proxy = null;
373049
- this.dnsServer = null;
373050
371813
  this.adminServer = null;
373051
371814
  this.services = [];
373052
371815
  this.resources = /* @__PURE__ */ new Map();
373053
371816
  this.tunnels = [];
373054
- if (this.proxyRegistry) {
373055
- await this.proxyRegistry.unregisterServices(this.instanceKey);
373056
- await this.proxyRegistry.releaseProxyOwnership();
373057
- }
373058
371817
  if (this.stateManager) {
373059
371818
  await this.stateManager.releaseOwnership();
373060
371819
  }
@@ -373132,17 +371891,7 @@ var DevEnvironment = class extends TypedEventEmitter {
373132
371891
  await this.fileAppearanceWatcher.close();
373133
371892
  this.fileAppearanceWatcher = null;
373134
371893
  }
373135
- if (this.electionInterval) {
373136
- clearInterval(this.electionInterval);
373137
- this.electionInterval = null;
373138
- }
373139
- if (this.registryWatcherCleanup) {
373140
- this.registryWatcherCleanup();
373141
- this.registryWatcherCleanup = null;
373142
- }
373143
371894
  await Promise.all([
373144
- this.proxy?.stop(),
373145
- this.dnsServer?.stop(),
373146
371895
  this.adminServer?.stop(),
373147
371896
  ...this.services.map((service) => service.stop()),
373148
371897
  ...this.electricInstances.map((electric) => electric.stop()),
@@ -373153,10 +371902,6 @@ var DevEnvironment = class extends TypedEventEmitter {
373153
371902
  ]);
373154
371903
  this.reshapeWatchers = [];
373155
371904
  this.restartServices = null;
373156
- if (this.proxyRegistry) {
373157
- await this.proxyRegistry.unregisterServices(this.instanceKey);
373158
- await this.proxyRegistry.releaseProxyOwnership();
373159
- }
373160
371905
  if (this.stateManager) {
373161
371906
  await this.stateManager.releaseOwnership();
373162
371907
  }
@@ -373171,10 +371916,10 @@ var DevEnvironment = class extends TypedEventEmitter {
373171
371916
  }
373172
371917
  // ── Private: config file watcher ───────────────────────────────────────────
373173
371918
  startConfigWatcher() {
373174
- const configPath = path16.join(this.projectDir, "specific.hcl");
371919
+ const configPath = path13.join(this.projectDir, "specific.hcl");
373175
371920
  this.configWatcher = watchConfigFile(configPath, 1e3, () => {
373176
371921
  try {
373177
- const hcl = fs16.readFileSync(configPath, "utf-8");
371922
+ const hcl = fs12.readFileSync(configPath, "utf-8");
373178
371923
  parseConfig(hcl).then(() => {
373179
371924
  this.reload();
373180
371925
  }).catch((err) => {
@@ -373195,8 +371940,6 @@ var DevEnvironment = class extends TypedEventEmitter {
373195
371940
  this.instanceKey
373196
371941
  );
373197
371942
  this.stateManager = stateManager;
373198
- const proxyRegistry = new ProxyRegistryManager();
373199
- this.proxyRegistry = proxyRegistry;
373200
371943
  this.systemLog("Starting dev server");
373201
371944
  this.setStatus("loading");
373202
371945
  await stateManager.cleanStaleState();
@@ -373215,8 +371958,8 @@ var DevEnvironment = class extends TypedEventEmitter {
373215
371958
  this.emit("error", new Error(msg));
373216
371959
  return;
373217
371960
  }
373218
- const configPath = path16.join(this.projectDir, "specific.hcl");
373219
- if (!fs16.existsSync(configPath)) {
371961
+ const configPath = path13.join(this.projectDir, "specific.hcl");
371962
+ if (!fs12.existsSync(configPath)) {
373220
371963
  this.systemLog("Waiting for specific.hcl to appear");
373221
371964
  this.setStatus("waiting");
373222
371965
  const dirWatcher = chokidar_default.watch(configPath, {
@@ -373237,7 +371980,7 @@ var DevEnvironment = class extends TypedEventEmitter {
373237
371980
  return;
373238
371981
  }
373239
371982
  let config;
373240
- const hcl = fs16.readFileSync(configPath, "utf-8");
371983
+ const hcl = fs12.readFileSync(configPath, "utf-8");
373241
371984
  try {
373242
371985
  config = await parseConfig(hcl);
373243
371986
  } catch (err) {
@@ -373247,9 +371990,9 @@ var DevEnvironment = class extends TypedEventEmitter {
373247
371990
  return;
373248
371991
  }
373249
371992
  if (this.cancelled) return;
373250
- const localFilePath = path16.join(this.projectDir, "specific.local");
373251
- if (fs16.existsSync(localFilePath)) {
373252
- const localContent = fs16.readFileSync(localFilePath, "utf-8");
371993
+ const localFilePath = path13.join(this.projectDir, "specific.local");
371994
+ if (fs12.existsSync(localFilePath)) {
371995
+ const localContent = fs12.readFileSync(localFilePath, "utf-8");
373253
371996
  try {
373254
371997
  await parseLocalFile(localContent);
373255
371998
  } catch (err) {
@@ -373296,10 +372039,8 @@ var DevEnvironment = class extends TypedEventEmitter {
373296
372039
  colorIndex++;
373297
372040
  }
373298
372041
  this.setStatus("starting");
373299
- const portAllocator = new StablePortAllocator(
373300
- this.projectDir,
373301
- this.instanceKey
373302
- );
372042
+ const portAllocator = await BlockPortAllocator.create();
372043
+ this.systemLog(`Using port block ${portAllocator.getBasePort()}\u2013${portAllocator.getBasePort() + 99}`);
373303
372044
  const syncDatabases = detectSyncDatabases(config);
373304
372045
  let resources;
373305
372046
  const startedElectric = [];
@@ -373368,7 +372109,7 @@ var DevEnvironment = class extends TypedEventEmitter {
373368
372109
  const drizzleGateway = await startDrizzleGateway(
373369
372110
  postgresResources,
373370
372111
  drizzlePort,
373371
- path16.join(this.projectDir, ".specific", "keys", this.instanceKey)
372112
+ path13.join(this.projectDir, ".specific", "keys", this.instanceKey)
373372
372113
  );
373373
372114
  this.drizzleGateway = drizzleGateway;
373374
372115
  this.systemLog(`Database viewer ready at ${drizzleGateway.url}`);
@@ -373388,7 +372129,7 @@ var DevEnvironment = class extends TypedEventEmitter {
373388
372129
  if (pg.reshape?.enabled) {
373389
372130
  const resource = resources.get(pg.name);
373390
372131
  if (!resource) continue;
373391
- const migrationsDir = path16.resolve(
372132
+ const migrationsDir = path13.resolve(
373392
372133
  this.projectDir,
373393
372134
  pg.reshape.migrations_dir ?? "migrations"
373394
372135
  );
@@ -373536,8 +372277,7 @@ Add them to the config block in specific.local`
373536
372277
  const endpointInfos = [];
373537
372278
  if (service.endpoints.length > 0) {
373538
372279
  for (const endpoint of service.endpoints) {
373539
- const portKey = endpoint.name === "default" ? `service:${service.name}` : `service:${service.name}:${endpoint.name}`;
373540
- const port = portAllocator.allocate(portKey);
372280
+ const port = portAllocator.allocateServicePort();
373541
372281
  endpointPorts.set(endpoint.name, port);
373542
372282
  endpointInfos.push({
373543
372283
  serviceName: service.name,
@@ -373546,7 +372286,7 @@ Add them to the config block in specific.local`
373546
372286
  });
373547
372287
  }
373548
372288
  } else if (hasPortReference(service.env)) {
373549
- const port = portAllocator.allocate(`service:${service.name}`);
372289
+ const port = portAllocator.allocateServicePort();
373550
372290
  endpointPorts.set("default", port);
373551
372291
  endpointInfos.push({
373552
372292
  serviceName: service.name,
@@ -373557,7 +372297,6 @@ Add them to the config block in specific.local`
373557
372297
  serviceEndpointPorts.set(service.name, endpointPorts);
373558
372298
  serviceEndpoints.set(service.name, endpointInfos);
373559
372299
  }
373560
- const serviceInfos = [];
373561
372300
  const exposedServices = [];
373562
372301
  for (const service of config.services) {
373563
372302
  const endpointInfos = serviceEndpoints.get(service.name) || [];
@@ -373568,16 +372307,9 @@ Add them to the config block in specific.local`
373568
372307
  if (endpointConfig?.public) {
373569
372308
  const proxyName = info.endpointName === "default" ? service.name : `${service.name}-${info.endpointName}`;
373570
372309
  exposedServices.push({ name: proxyName, port: info.port });
373571
- serviceInfos.push({
373572
- serviceName: proxyName,
373573
- port: info.port
373574
- });
373575
372310
  }
373576
372311
  }
373577
372312
  }
373578
- const existingOwner = await proxyRegistry.getProxyOwner();
373579
- const expectedHttpsPort = existingOwner ? existingOwner.httpsPort ?? 443 : await probeHttpsPort();
373580
- const portSuffix = expectedHttpsPort !== 443 && expectedHttpsPort !== 0 ? `:${expectedHttpsPort}` : "";
373581
372313
  const publicUrls = /* @__PURE__ */ new Map();
373582
372314
  let subdomainAllocator;
373583
372315
  if (this.tunnelEnabled && exposedServices.length > 0) {
@@ -373594,21 +372326,20 @@ Add them to the config block in specific.local`
373594
372326
  publicUrls.set(svc.name, `${subdomain}.tunnel.spcf.app`);
373595
372327
  }
373596
372328
  } else {
373597
- const keySuffix = this.instanceKey === "default" ? "" : `.${this.instanceKey}`;
373598
372329
  for (const svc of exposedServices) {
373599
- publicUrls.set(svc.name, `${svc.name}${keySuffix}.spcf.localhost${portSuffix}`);
372330
+ publicUrls.set(svc.name, `localhost:${svc.port}`);
373600
372331
  }
373601
372332
  }
373602
372333
  const runningServices = [];
373603
372334
  const resolveServiceCwd = (service) => {
373604
372335
  if (service.root)
373605
- return path16.resolve(this.projectDir, service.root);
372336
+ return path13.resolve(this.projectDir, service.root);
373606
372337
  if (service.build) {
373607
372338
  const build = config.builds.find(
373608
372339
  (b) => b.name === service.build.name
373609
372340
  );
373610
372341
  if (build?.root)
373611
- return path16.resolve(this.projectDir, build.root);
372342
+ return path13.resolve(this.projectDir, build.root);
373612
372343
  }
373613
372344
  return this.projectDir;
373614
372345
  };
@@ -373806,175 +372537,13 @@ Add them to the config block in specific.local`
373806
372537
  drizzleGatewayPort: this.drizzleGateway?.port,
373807
372538
  temporalUiPort
373808
372539
  });
373809
- const adminServer = await startAdminServer(getState);
372540
+ const adminPort = portAllocator.getAdminPort();
372541
+ const adminServer = await startAdminServer(getState, adminPort);
373810
372542
  this.adminServer = adminServer;
373811
372543
  this.systemLog(
373812
- `Admin API server started on port ${adminServer.port}`
372544
+ `Admin server started on port ${adminServer.port}`
373813
372545
  );
373814
- if (this.tunnelEnabled) {
373815
- this.emit("adminReady", `http://localhost:${adminServer.port}`);
373816
- } else {
373817
- const adminHost = this.instanceKey === "default" ? "" : `${this.instanceKey}.`;
373818
- this.emit("adminReady", `https://${adminHost}spcf.localhost${portSuffix}`);
373819
- await proxyRegistry.registerServices(
373820
- this.instanceKey,
373821
- adminServer.port,
373822
- serviceInfos,
373823
- this.drizzleGateway?.port,
373824
- temporalUiPort
373825
- );
373826
- this.systemLog(
373827
- `Registered ${serviceInfos.length} services with proxy registry`
373828
- );
373829
- const becameProxyOwner = await proxyRegistry.claimProxyOwnership(
373830
- this.instanceKey,
373831
- expectedHttpsPort
373832
- );
373833
- if (becameProxyOwner) {
373834
- this.systemLog(
373835
- "Claimed proxy ownership, starting HTTP proxy and DNS server"
373836
- );
373837
- try {
373838
- const dnsServer = await startDnsServer();
373839
- this.dnsServer = dnsServer;
373840
- const currentServices = await proxyRegistry.getAllServices();
373841
- const registeredKeys = [
373842
- ...new Set(currentServices.map((s) => s.key))
373843
- ];
373844
- const certificate = generateCertificate(
373845
- "spcf.localhost",
373846
- registeredKeys
373847
- );
373848
- const proxy = await startHttpProxy(
373849
- exposedServices,
373850
- certificate,
373851
- getState,
373852
- this.instanceKey,
373853
- expectedHttpsPort
373854
- );
373855
- this.proxy = proxy;
373856
- const knownKeys = new Set(registeredKeys);
373857
- this.registryWatcherCleanup = proxyRegistry.watchRegistry(
373858
- async (updatedServices, updatedKeys) => {
373859
- this.systemLog(
373860
- `Registry updated: ${updatedServices.length} services`
373861
- );
373862
- proxy.updateServices(updatedServices, updatedKeys);
373863
- const newKeyNames = Object.keys(updatedKeys).filter(
373864
- (k) => !knownKeys.has(k)
373865
- );
373866
- if (newKeyNames.length > 0) {
373867
- this.systemLog(
373868
- `New keys detected: ${newKeyNames.join(", ")} - regenerating certificate`
373869
- );
373870
- for (const key of newKeyNames) {
373871
- knownKeys.add(key);
373872
- }
373873
- const allKeys = [...knownKeys];
373874
- const newCertificate = generateCertificate(
373875
- "spcf.localhost",
373876
- allKeys
373877
- );
373878
- proxy.updateCertificate(newCertificate);
373879
- }
373880
- }
373881
- );
373882
- const currentKeys = await proxyRegistry.getAllKeyRegistrations();
373883
- proxy.updateServices(currentServices, currentKeys);
373884
- this.systemLog(
373885
- `Loaded ${currentServices.length} services from registry`
373886
- );
373887
- } catch (err) {
373888
- const errorMsg = `Failed to start HTTP proxy: ${err instanceof Error ? err.message : String(err)}`;
373889
- writeLog("system:error", errorMsg);
373890
- this.setStatus("error");
373891
- this.emit("error", new Error(errorMsg));
373892
- return;
373893
- }
373894
- } else {
373895
- this.systemLog(
373896
- "Another instance owns the proxy, starting election watcher"
373897
- );
373898
- const isProcessRunning = (pid) => {
373899
- try {
373900
- process.kill(pid, 0);
373901
- return true;
373902
- } catch {
373903
- return false;
373904
- }
373905
- };
373906
- this.electionInterval = setInterval(async () => {
373907
- if (this.cancelled || this.shuttingDown) {
373908
- if (this.electionInterval) {
373909
- clearInterval(this.electionInterval);
373910
- this.electionInterval = null;
373911
- }
373912
- return;
373913
- }
373914
- const owner = await proxyRegistry.getProxyOwner();
373915
- if (!owner || !isProcessRunning(owner.pid)) {
373916
- this.systemLog(
373917
- "Proxy owner died, attempting election"
373918
- );
373919
- const previousPort = proxyRegistry.getProxyHttpsPort();
373920
- const won = await proxyRegistry.attemptElection(
373921
- this.instanceKey,
373922
- previousPort
373923
- );
373924
- if (won) {
373925
- this.systemLog(
373926
- "Won election, starting HTTP proxy and DNS server"
373927
- );
373928
- if (this.electionInterval) {
373929
- clearInterval(this.electionInterval);
373930
- this.electionInterval = null;
373931
- }
373932
- try {
373933
- const dnsServer = await startDnsServer();
373934
- this.dnsServer = dnsServer;
373935
- const electionServices = await proxyRegistry.getAllServices();
373936
- const electionKeyRegistrations = await proxyRegistry.getAllKeyRegistrations();
373937
- const electionKeyNames = Object.keys(
373938
- electionKeyRegistrations
373939
- );
373940
- const certificate = generateCertificate(
373941
- "spcf.localhost",
373942
- electionKeyNames
373943
- );
373944
- const proxy = await startHttpProxy(
373945
- exposedServices,
373946
- certificate,
373947
- getState,
373948
- this.instanceKey,
373949
- previousPort
373950
- );
373951
- this.proxy = proxy;
373952
- this.registryWatcherCleanup = proxyRegistry.watchRegistry(
373953
- (updatedServices, updatedKeys) => {
373954
- this.systemLog(
373955
- `Registry updated: ${updatedServices.length} services`
373956
- );
373957
- proxy.updateServices(
373958
- updatedServices,
373959
- updatedKeys
373960
- );
373961
- }
373962
- );
373963
- proxy.updateServices(
373964
- electionServices,
373965
- electionKeyRegistrations
373966
- );
373967
- } catch (err) {
373968
- writeLog(
373969
- "system:error",
373970
- `Failed to start proxy after election: ${err}`
373971
- );
373972
- }
373973
- }
373974
- }
373975
- }, 1e3);
373976
- }
373977
- }
372546
+ this.emit("adminReady", `http://localhost:${adminServer.port}`);
373978
372547
  if (this.cancelled) return;
373979
372548
  this.systemLog("Dev server running");
373980
372549
  this.setStatus("running");
@@ -373984,16 +372553,16 @@ Add them to the config block in specific.local`
373984
372553
  };
373985
372554
  function isInWorktree() {
373986
372555
  try {
373987
- const commonDir = execSync4("git rev-parse --git-common-dir", {
372556
+ const commonDir = execSync2("git rev-parse --git-common-dir", {
373988
372557
  encoding: "utf-8",
373989
372558
  stdio: ["pipe", "pipe", "pipe"]
373990
372559
  }).trim();
373991
- const gitDir = execSync4("git rev-parse --git-dir", {
372560
+ const gitDir = execSync2("git rev-parse --git-dir", {
373992
372561
  encoding: "utf-8",
373993
372562
  stdio: ["pipe", "pipe", "pipe"]
373994
372563
  }).trim();
373995
- const resolvedCommonDir = path17.resolve(commonDir);
373996
- const resolvedGitDir = path17.resolve(gitDir);
372564
+ const resolvedCommonDir = path14.resolve(commonDir);
372565
+ const resolvedGitDir = path14.resolve(gitDir);
373997
372566
  return resolvedCommonDir !== resolvedGitDir;
373998
372567
  } catch {
373999
372568
  return false;
@@ -374004,11 +372573,11 @@ function getWorktreeName() {
374004
372573
  return null;
374005
372574
  }
374006
372575
  try {
374007
- const gitDir = execSync4("git rev-parse --git-dir", {
372576
+ const gitDir = execSync2("git rev-parse --git-dir", {
374008
372577
  encoding: "utf-8",
374009
372578
  stdio: ["pipe", "pipe", "pipe"]
374010
372579
  }).trim();
374011
- return path17.basename(gitDir);
372580
+ return path14.basename(gitDir);
374012
372581
  } catch {
374013
372582
  return null;
374014
372583
  }
@@ -374020,12 +372589,12 @@ function getDefaultKey() {
374020
372589
 
374021
372590
  // src/lib/analytics/index.ts
374022
372591
  import { PostHog } from "posthog-node";
374023
- import * as os10 from "os";
372592
+ import * as os5 from "os";
374024
372593
  import * as crypto3 from "crypto";
374025
372594
 
374026
372595
  // src/lib/project/config.ts
374027
- import * as fs17 from "fs";
374028
- import * as path18 from "path";
372596
+ import * as fs3 from "fs";
372597
+ import * as path3 from "path";
374029
372598
  var PROJECT_ID_FILE = ".specific/project_id";
374030
372599
  var ProjectNotLinkedError = class extends Error {
374031
372600
  constructor() {
@@ -374040,32 +372609,32 @@ Run: specific deploy`
374040
372609
  }
374041
372610
  };
374042
372611
  function readProjectId(projectDir = process.cwd()) {
374043
- const projectIdPath = path18.join(projectDir, PROJECT_ID_FILE);
374044
- if (!fs17.existsSync(projectIdPath)) {
372612
+ const projectIdPath = path3.join(projectDir, PROJECT_ID_FILE);
372613
+ if (!fs3.existsSync(projectIdPath)) {
374045
372614
  throw new ProjectNotLinkedError();
374046
372615
  }
374047
- const projectId = fs17.readFileSync(projectIdPath, "utf-8").trim();
372616
+ const projectId = fs3.readFileSync(projectIdPath, "utf-8").trim();
374048
372617
  if (!projectId) {
374049
372618
  throw new Error(`${PROJECT_ID_FILE} is empty`);
374050
372619
  }
374051
372620
  return projectId;
374052
372621
  }
374053
372622
  function hasProjectId(projectDir = process.cwd()) {
374054
- const projectIdPath = path18.join(projectDir, PROJECT_ID_FILE);
374055
- return fs17.existsSync(projectIdPath);
372623
+ const projectIdPath = path3.join(projectDir, PROJECT_ID_FILE);
372624
+ return fs3.existsSync(projectIdPath);
374056
372625
  }
374057
372626
  function writeProjectId(projectId, projectDir = process.cwd()) {
374058
- const specificDir = path18.join(projectDir, ".specific");
374059
- if (!fs17.existsSync(specificDir)) {
374060
- fs17.mkdirSync(specificDir, { recursive: true });
372627
+ const specificDir = path3.join(projectDir, ".specific");
372628
+ if (!fs3.existsSync(specificDir)) {
372629
+ fs3.mkdirSync(specificDir, { recursive: true });
374061
372630
  }
374062
- fs17.writeFileSync(path18.join(specificDir, "project_id"), projectId + "\n");
372631
+ fs3.writeFileSync(path3.join(specificDir, "project_id"), projectId + "\n");
374063
372632
  }
374064
372633
 
374065
372634
  // src/lib/auth/credentials.ts
374066
- import * as fs23 from "fs";
374067
- import * as path20 from "path";
374068
- import * as os9 from "os";
372635
+ import * as fs18 from "fs";
372636
+ import * as path16 from "path";
372637
+ import * as os4 from "os";
374069
372638
 
374070
372639
  // src/lib/auth/errors.ts
374071
372640
  var RefreshTokenExpiredError = class extends Error {
@@ -374307,18 +372876,18 @@ function performLogin(options2 = {}) {
374307
372876
 
374308
372877
  // src/lib/auth/credentials.ts
374309
372878
  function getUserCredentialsDir() {
374310
- return path20.join(os9.homedir(), ".specific");
372879
+ return path16.join(os4.homedir(), ".specific");
374311
372880
  }
374312
372881
  function getCredentialsPath() {
374313
- return path20.join(getUserCredentialsDir(), "credentials.json");
372882
+ return path16.join(getUserCredentialsDir(), "credentials.json");
374314
372883
  }
374315
372884
  function readUserCredentials() {
374316
372885
  const credentialsPath = getCredentialsPath();
374317
- if (!fs23.existsSync(credentialsPath)) {
372886
+ if (!fs18.existsSync(credentialsPath)) {
374318
372887
  return null;
374319
372888
  }
374320
372889
  try {
374321
- const content = fs23.readFileSync(credentialsPath, "utf-8");
372890
+ const content = fs18.readFileSync(credentialsPath, "utf-8");
374322
372891
  return JSON.parse(content);
374323
372892
  } catch {
374324
372893
  return null;
@@ -374326,18 +372895,18 @@ function readUserCredentials() {
374326
372895
  }
374327
372896
  function writeUserCredentials(credentials) {
374328
372897
  const dir = getUserCredentialsDir();
374329
- if (!fs23.existsSync(dir)) {
374330
- fs23.mkdirSync(dir, { recursive: true, mode: 448 });
372898
+ if (!fs18.existsSync(dir)) {
372899
+ fs18.mkdirSync(dir, { recursive: true, mode: 448 });
374331
372900
  }
374332
372901
  const credentialsPath = getCredentialsPath();
374333
- fs23.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {
372902
+ fs18.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {
374334
372903
  mode: 384
374335
372904
  });
374336
372905
  }
374337
372906
  function clearUserCredentials() {
374338
372907
  const credentialsPath = getCredentialsPath();
374339
- if (fs23.existsSync(credentialsPath)) {
374340
- fs23.unlinkSync(credentialsPath);
372908
+ if (fs18.existsSync(credentialsPath)) {
372909
+ fs18.unlinkSync(credentialsPath);
374341
372910
  }
374342
372911
  }
374343
372912
  function isLoggedIn() {
@@ -374416,7 +372985,7 @@ function isEnabled() {
374416
372985
  }
374417
372986
  function getAnonymousId() {
374418
372987
  if (anonymousId) return anonymousId;
374419
- const machineId = `${os10.hostname()}-${os10.userInfo().username}`;
372988
+ const machineId = `${os5.hostname()}-${os5.userInfo().username}`;
374420
372989
  anonymousId = crypto3.createHash("sha256").update(machineId).digest("hex").slice(0, 16);
374421
372990
  return anonymousId;
374422
372991
  }
@@ -374463,7 +373032,7 @@ function trackEvent(event, properties) {
374463
373032
  event,
374464
373033
  properties: {
374465
373034
  ...properties,
374466
- cli_version: "0.1.92",
373035
+ cli_version: "0.1.93",
374467
373036
  platform: process.platform,
374468
373037
  node_version: process.version,
374469
373038
  project_id: getProjectId()
@@ -374494,67 +373063,67 @@ var options = [
374494
373063
  { id: "other", label: "Other / Manual" }
374495
373064
  ];
374496
373065
  function isGitProject() {
374497
- const gitPath = path21.join(process.cwd(), ".git");
374498
- return fs24.existsSync(gitPath);
373066
+ const gitPath = path17.join(process.cwd(), ".git");
373067
+ return fs19.existsSync(gitPath);
374499
373068
  }
374500
373069
  function detectExistingAgents() {
374501
373070
  const detected = {};
374502
- const cursorDir = path21.join(process.cwd(), ".cursor");
374503
- if (fs24.existsSync(cursorDir)) {
373071
+ const cursorDir = path17.join(process.cwd(), ".cursor");
373072
+ if (fs19.existsSync(cursorDir)) {
374504
373073
  detected["cursor"] = true;
374505
373074
  }
374506
- const claudeDir = path21.join(process.cwd(), ".claude");
374507
- const claudeMd = path21.join(process.cwd(), "CLAUDE.md");
374508
- if (fs24.existsSync(claudeDir) || fs24.existsSync(claudeMd)) {
373075
+ const claudeDir = path17.join(process.cwd(), ".claude");
373076
+ const claudeMd = path17.join(process.cwd(), "CLAUDE.md");
373077
+ if (fs19.existsSync(claudeDir) || fs19.existsSync(claudeMd)) {
374509
373078
  detected["claude"] = true;
374510
373079
  }
374511
- const agentsMd = path21.join(process.cwd(), "AGENTS.md");
374512
- if (fs24.existsSync(agentsMd)) {
373080
+ const agentsMd = path17.join(process.cwd(), "AGENTS.md");
373081
+ if (fs19.existsSync(agentsMd)) {
374513
373082
  detected["codex"] = true;
374514
373083
  }
374515
373084
  return detected;
374516
373085
  }
374517
373086
  function appendOrCreateFile(filePath, content) {
374518
- if (fs24.existsSync(filePath)) {
374519
- const existing = fs24.readFileSync(filePath, "utf-8");
373087
+ if (fs19.existsSync(filePath)) {
373088
+ const existing = fs19.readFileSync(filePath, "utf-8");
374520
373089
  if (existing.includes("specific docs") || existing.includes("specific check")) {
374521
373090
  return "unchanged";
374522
373091
  }
374523
373092
  const separator = existing.endsWith("\n") ? "\n" : "\n\n";
374524
- fs24.writeFileSync(filePath, existing + separator + content + "\n");
373093
+ fs19.writeFileSync(filePath, existing + separator + content + "\n");
374525
373094
  return "modified";
374526
373095
  } else {
374527
- fs24.writeFileSync(filePath, content + "\n");
373096
+ fs19.writeFileSync(filePath, content + "\n");
374528
373097
  return "created";
374529
373098
  }
374530
373099
  }
374531
373100
  function addToGitignore() {
374532
- const gitignorePath = path21.join(process.cwd(), ".gitignore");
373101
+ const gitignorePath = path17.join(process.cwd(), ".gitignore");
374533
373102
  const entries = [".specific", "specific.local"];
374534
- if (fs24.existsSync(gitignorePath)) {
374535
- const existing = fs24.readFileSync(gitignorePath, "utf-8");
373103
+ if (fs19.existsSync(gitignorePath)) {
373104
+ const existing = fs19.readFileSync(gitignorePath, "utf-8");
374536
373105
  const lines = existing.split("\n").map((l) => l.trim());
374537
373106
  const missingEntries = entries.filter((entry) => !lines.includes(entry));
374538
373107
  if (missingEntries.length === 0) {
374539
373108
  return "unchanged";
374540
373109
  }
374541
373110
  const separator = existing.endsWith("\n") ? "" : "\n";
374542
- fs24.writeFileSync(
373111
+ fs19.writeFileSync(
374543
373112
  gitignorePath,
374544
373113
  existing + separator + missingEntries.join("\n") + "\n"
374545
373114
  );
374546
373115
  return "modified";
374547
373116
  } else {
374548
- fs24.writeFileSync(gitignorePath, entries.join("\n") + "\n");
373117
+ fs19.writeFileSync(gitignorePath, entries.join("\n") + "\n");
374549
373118
  return "created";
374550
373119
  }
374551
373120
  }
374552
373121
  function configureClaudeCodePermissions() {
374553
- const claudeDir = path21.join(process.cwd(), ".claude");
374554
- const settingsPath = path21.join(claudeDir, "settings.local.json");
373122
+ const claudeDir = path17.join(process.cwd(), ".claude");
373123
+ const settingsPath = path17.join(claudeDir, "settings.local.json");
374555
373124
  const permissions = ["Bash(specific docs:*)", "Bash(specific check:*)"];
374556
- if (fs24.existsSync(settingsPath)) {
374557
- const existing = JSON.parse(fs24.readFileSync(settingsPath, "utf-8"));
373125
+ if (fs19.existsSync(settingsPath)) {
373126
+ const existing = JSON.parse(fs19.readFileSync(settingsPath, "utf-8"));
374558
373127
  const allowList = existing?.permissions?.allow || [];
374559
373128
  const missingPermissions = permissions.filter(
374560
373129
  (p) => !allowList.includes(p)
@@ -374569,39 +373138,39 @@ function configureClaudeCodePermissions() {
374569
373138
  existing.permissions.allow = [];
374570
373139
  }
374571
373140
  existing.permissions.allow.push(...missingPermissions);
374572
- fs24.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + "\n");
373141
+ fs19.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + "\n");
374573
373142
  return "modified";
374574
373143
  }
374575
- if (!fs24.existsSync(claudeDir)) {
374576
- fs24.mkdirSync(claudeDir);
373144
+ if (!fs19.existsSync(claudeDir)) {
373145
+ fs19.mkdirSync(claudeDir);
374577
373146
  }
374578
373147
  const settings = {
374579
373148
  permissions: {
374580
373149
  allow: permissions
374581
373150
  }
374582
373151
  };
374583
- fs24.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
373152
+ fs19.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
374584
373153
  return "created";
374585
373154
  }
374586
373155
  function createCursorRule() {
374587
- const cursorDir = path21.join(process.cwd(), ".cursor");
374588
- const rulesDir = path21.join(cursorDir, "rules");
374589
- const mdcPath = path21.join(rulesDir, "specific.mdc");
374590
- if (fs24.existsSync(mdcPath)) {
374591
- const existing = fs24.readFileSync(mdcPath, "utf-8");
373156
+ const cursorDir = path17.join(process.cwd(), ".cursor");
373157
+ const rulesDir = path17.join(cursorDir, "rules");
373158
+ const mdcPath = path17.join(rulesDir, "specific.mdc");
373159
+ if (fs19.existsSync(mdcPath)) {
373160
+ const existing = fs19.readFileSync(mdcPath, "utf-8");
374592
373161
  if (existing.includes("specific docs") || existing.includes("specific check")) {
374593
373162
  return "unchanged";
374594
373163
  }
374595
- fs24.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
373164
+ fs19.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
374596
373165
  return "modified";
374597
373166
  }
374598
- if (!fs24.existsSync(cursorDir)) {
374599
- fs24.mkdirSync(cursorDir);
373167
+ if (!fs19.existsSync(cursorDir)) {
373168
+ fs19.mkdirSync(cursorDir);
374600
373169
  }
374601
- if (!fs24.existsSync(rulesDir)) {
374602
- fs24.mkdirSync(rulesDir);
373170
+ if (!fs19.existsSync(rulesDir)) {
373171
+ fs19.mkdirSync(rulesDir);
374603
373172
  }
374604
- fs24.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
373173
+ fs19.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
374605
373174
  return "created";
374606
373175
  }
374607
373176
  function configureAgents(checked) {
@@ -374617,7 +373186,7 @@ function configureAgents(checked) {
374617
373186
  agents.filesModified.push(".cursor/rules/specific.mdc");
374618
373187
  }
374619
373188
  if (checked["claude"]) {
374620
- const claudeMdPath = path21.join(process.cwd(), "CLAUDE.md");
373189
+ const claudeMdPath = path17.join(process.cwd(), "CLAUDE.md");
374621
373190
  const status = appendOrCreateFile(claudeMdPath, SPECIFIC_INSTRUCTIONS);
374622
373191
  if (status === "created") agents.filesCreated.push("CLAUDE.md");
374623
373192
  else if (status === "modified") agents.filesModified.push("CLAUDE.md");
@@ -374628,7 +373197,7 @@ function configureAgents(checked) {
374628
373197
  agents.filesModified.push(".claude/settings.local.json");
374629
373198
  }
374630
373199
  if (checked["codex"]) {
374631
- const agentsMdPath = path21.join(process.cwd(), "AGENTS.md");
373200
+ const agentsMdPath = path17.join(process.cwd(), "AGENTS.md");
374632
373201
  const status = appendOrCreateFile(agentsMdPath, SPECIFIC_INSTRUCTIONS);
374633
373202
  if (status === "created") agents.filesCreated.push("AGENTS.md");
374634
373203
  else if (status === "modified") agents.filesModified.push("AGENTS.md");
@@ -374643,16 +373212,6 @@ function configureAgents(checked) {
374643
373212
  return { agents, git, showManualInstructions: !!checked["other"] };
374644
373213
  }
374645
373214
  function getInitialSetupPhase() {
374646
- if (!systemSetupNeeded()) return "agents";
374647
- if (os11.platform() === "darwin") {
374648
- if (dnsSetupNeeded()) return "setup-dns";
374649
- if (tlsSetupNeeded()) return "setup-tls";
374650
- return "agents";
374651
- }
374652
- return "setup-combined";
374653
- }
374654
- function getNextPhase(current) {
374655
- if (current === "setup-dns" && tlsSetupNeeded()) return "setup-tls";
374656
373215
  return "agents";
374657
373216
  }
374658
373217
  function InitUI() {
@@ -374666,91 +373225,13 @@ function InitUI() {
374666
373225
  };
374667
373226
  });
374668
373227
  const [phase, setPhase] = useState(getInitialSetupPhase);
374669
- const [stepPhase, setStepPhase] = useState(
374670
- () => getInitialSetupPhase() === "agents" ? "done" : "prompt"
374671
- );
374672
- const [stepError, setStepError] = useState();
374673
373228
  const [focusedIndex, setFocusedIndex] = useState(initialState.focusedIndex);
374674
373229
  const [checked, setChecked] = useState(
374675
373230
  initialState.detected
374676
373231
  );
374677
373232
  const [result, setResult] = useState(null);
374678
- const [setupResult, setSetupResult] = useState(
374679
- () => !systemSetupNeeded() ? { dns: { status: "success" }, tls: { status: "success" } } : {}
374680
- );
374681
- useEffect(() => {
374682
- if (stepPhase !== "installing") return;
374683
- if (phase === "setup-dns") {
374684
- runSetupStep(
374685
- () => {
374686
- ensureCAGenerated();
374687
- performDnsSetup();
374688
- },
374689
- "dns"
374690
- );
374691
- } else if (phase === "setup-tls") {
374692
- runSetupStep(
374693
- () => {
374694
- ensureCAGenerated();
374695
- performTlsTrust();
374696
- },
374697
- "tls"
374698
- );
374699
- } else if (phase === "setup-combined") {
374700
- runSetupStep(
374701
- () => performSystemSetup(),
374702
- "combined"
374703
- );
374704
- }
374705
- }, [phase, stepPhase]);
374706
- function runSetupStep(fn, type) {
374707
- try {
374708
- fn();
374709
- if (type === "combined") {
374710
- setSetupResult((r) => ({
374711
- ...r,
374712
- dns: { status: "success" },
374713
- tls: { status: "success" }
374714
- }));
374715
- } else {
374716
- setSetupResult((r) => ({ ...r, [type]: { status: "success" } }));
374717
- }
374718
- setStepPhase("done");
374719
- const next = getNextPhase(phase);
374720
- setPhase(next);
374721
- if (next !== "agents") {
374722
- setStepPhase("prompt");
374723
- }
374724
- } catch (err) {
374725
- const errorMsg = err instanceof Error ? err.message : String(err);
374726
- if (type === "combined") {
374727
- setSetupResult((r) => ({
374728
- ...r,
374729
- dns: { status: "error", error: errorMsg },
374730
- tls: { status: "error", error: errorMsg }
374731
- }));
374732
- } else {
374733
- setSetupResult((r) => ({
374734
- ...r,
374735
- [type]: { status: "error", error: errorMsg }
374736
- }));
374737
- }
374738
- setStepError(errorMsg);
374739
- setStepPhase("error");
374740
- const next = getNextPhase(phase);
374741
- setPhase(next);
374742
- if (next !== "agents") {
374743
- setStepPhase("prompt");
374744
- setStepError(void 0);
374745
- }
374746
- }
374747
- }
374748
373233
  const isSubmitFocused = focusedIndex === options.length;
374749
373234
  useInput((input, key) => {
374750
- if ((phase === "setup-dns" || phase === "setup-tls" || phase === "setup-combined") && stepPhase === "prompt" && key.return) {
374751
- setStepPhase("installing");
374752
- return;
374753
- }
374754
373235
  if (phase !== "agents") return;
374755
373236
  if (key.upArrow || input === "k") {
374756
373237
  setFocusedIndex((prev) => Math.max(0, prev - 1));
@@ -374779,30 +373260,13 @@ function InitUI() {
374779
373260
  return () => clearTimeout(timer);
374780
373261
  }
374781
373262
  }, [phase, exit]);
374782
- if (phase === "setup-dns" || phase === "setup-tls" || phase === "setup-combined") {
374783
- if (stepPhase === "prompt") {
374784
- const isDns = phase === "setup-dns";
374785
- const isTls = phase === "setup-tls";
374786
- return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Configure development environment"), /* @__PURE__ */ React2.createElement(Text2, null, " "), (isDns || phase === "setup-combined") && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, "We need to do a one-time setup for all your Specific projects."), /* @__PURE__ */ React2.createElement(Text2, null, "This is so we can run your apps locally with secure URLs:"), /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "green" }, "https://your-app.spcf.localhost"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "You will be prompted to authorize with your password.")), isTls && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, "We need to trust a development certificate for HTTPS."), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "You will be prompted to authorize.")), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "Press ", /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Enter"), " to continue."));
374787
- }
374788
- if (stepPhase === "installing") {
374789
- return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Configure development environment"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "Awaiting authorization\u2026"));
374790
- }
374791
- }
374792
373263
  if (phase === "done") {
374793
373264
  const selectedAgents = Object.entries(checked).filter(([, v]) => v).length;
374794
373265
  const agentChanges = result && (result.agents.filesCreated.length > 0 || result.agents.filesModified.length > 0);
374795
373266
  const gitChanges = result?.git && (result.git.filesCreated.length > 0 || result.git.filesModified.length > 0);
374796
- const setupSuccess2 = (!setupResult.dns || setupResult.dns.status === "success") && (!setupResult.tls || setupResult.tls.status === "success");
374797
- const setupError2 = setupResult.dns?.status === "error" || setupResult.tls?.status === "error";
374798
- const setupErrorMsg2 = setupResult.dns?.error || setupResult.tls?.error;
374799
- return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, setupSuccess2 && /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " TLS and DNS configured"), setupError2 && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "red" }, "\u2717"), " Failed to set up TLS and DNS"), setupErrorMsg2 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", setupErrorMsg2)), result && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Coding agents configured"), result.agents.filesCreated.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Created: ", result.agents.filesCreated.join(", ")), result.agents.filesModified.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Modified: ", result.agents.filesModified.join(", ")), !agentChanges && selectedAgents > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "No changes needed (files already configured)"), selectedAgents === 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " No agents selected")), result?.git && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Git configured"), result.git.filesCreated.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Created: ", result.git.filesCreated.join(", ")), result.git.filesModified.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Modified: ", result.git.filesModified.join(", ")), !gitChanges && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "No changes needed (.gitignore already configured)")), result?.showManualInstructions && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Manual configuration selected"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, " Add this to your agent's system prompt:"), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"), /* @__PURE__ */ React2.createElement(Text2, null, SPECIFIC_INSTRUCTIONS), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, " ", "We also recommend allowing your agent to run `specific docs *`"), /* @__PURE__ */ React2.createElement(Text2, null, " and `specific check *` without confirmation.")));
373267
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, result && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Coding agents configured"), result.agents.filesCreated.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Created: ", result.agents.filesCreated.join(", ")), result.agents.filesModified.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Modified: ", result.agents.filesModified.join(", ")), !agentChanges && selectedAgents > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "No changes needed (files already configured)"), selectedAgents === 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " No agents selected")), result?.git && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Git configured"), result.git.filesCreated.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Created: ", result.git.filesCreated.join(", ")), result.git.filesModified.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Modified: ", result.git.filesModified.join(", ")), !gitChanges && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "No changes needed (.gitignore already configured)")), result?.showManualInstructions && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Manual configuration selected"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, " Add this to your agent's system prompt:"), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"), /* @__PURE__ */ React2.createElement(Text2, null, SPECIFIC_INSTRUCTIONS), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, " ", "We also recommend allowing your agent to run `specific docs *`"), /* @__PURE__ */ React2.createElement(Text2, null, " and `specific check *` without confirmation.")));
374800
373268
  }
374801
- const setupSuccess = (!setupResult.dns || setupResult.dns.status === "success") && (!setupResult.tls || setupResult.tls.status === "success");
374802
- const setupError = setupResult.dns?.status === "error" || setupResult.tls?.status === "error";
374803
- const setupErrorMsg = setupResult.dns?.error || setupResult.tls?.error;
374804
- const setupLine = setupSuccess ? /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " TLS and DNS configured") : setupError ? /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "red" }, "\u2717"), " TLS and DNS setup failed", " ", /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "(", setupErrorMsg, ")")) : null;
374805
- return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, setupLine, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u25CF"), " Configure coding agents", " ", /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "(space to select)")), /* @__PURE__ */ React2.createElement(Text2, null, " "), options.map((option, index) => {
373269
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u25CF"), " Configure coding agents", " ", /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "(space to select)")), /* @__PURE__ */ React2.createElement(Text2, null, " "), options.map((option, index) => {
374806
373270
  const isFocused = focusedIndex === index;
374807
373271
  const isChecked = checked[option.id];
374808
373272
  return /* @__PURE__ */ React2.createElement(Box2, { key: option.id }, /* @__PURE__ */ React2.createElement(Text2, { ...isFocused && { color: "cyan" } }, isFocused ? "\u276F " : " ", isChecked ? "[\u2713]" : "[ ]", " ", option.label));
@@ -374872,11 +373336,6 @@ Valid agents: ${VALID_AGENT_IDS.join(", ")}`
374872
373336
  console.log(" No changes needed (.gitignore already configured)");
374873
373337
  }
374874
373338
  }
374875
- if (systemSetupNeeded()) {
374876
- console.log(
374877
- "\u26A0 TLS and DNS not configured (run `specific init` in a terminal to set up)"
374878
- );
374879
- }
374880
373339
  if (result.showManualInstructions) {
374881
373340
  console.log("\n\u2713 Manual configuration selected");
374882
373341
  console.log("\n Add this to your agent's system prompt:");
@@ -374891,8 +373350,8 @@ Valid agents: ${VALID_AGENT_IDS.join(", ")}`
374891
373350
  }
374892
373351
 
374893
373352
  // src/commands/docs.tsx
374894
- import { readFileSync as readFileSync12, existsSync as existsSync20 } from "fs";
374895
- import { join as join22, dirname as dirname8 } from "path";
373353
+ import { readFileSync as readFileSync9, existsSync as existsSync17 } from "fs";
373354
+ import { join as join18, dirname as dirname8 } from "path";
374896
373355
  import { fileURLToPath as fileURLToPath3 } from "url";
374897
373356
 
374898
373357
  // src/lib/beta/registry.ts
@@ -374908,16 +373367,16 @@ var BETA_REGISTRY = [
374908
373367
  ];
374909
373368
 
374910
373369
  // src/lib/beta/storage.ts
374911
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync11, existsSync as existsSync19, mkdirSync as mkdirSync15 } from "fs";
374912
- import { join as join21 } from "path";
373370
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync7, existsSync as existsSync16, mkdirSync as mkdirSync12 } from "fs";
373371
+ import { join as join17 } from "path";
374913
373372
  var BETAS_FILE = ".specific/betas.json";
374914
373373
  function loadEnabledBetas(projectDir = process.cwd()) {
374915
- const filePath = join21(projectDir, BETAS_FILE);
374916
- if (!existsSync19(filePath)) {
373374
+ const filePath = join17(projectDir, BETAS_FILE);
373375
+ if (!existsSync16(filePath)) {
374917
373376
  return [];
374918
373377
  }
374919
373378
  try {
374920
- const content = readFileSync11(filePath, "utf-8");
373379
+ const content = readFileSync8(filePath, "utf-8");
374921
373380
  const data = JSON.parse(content);
374922
373381
  return data.enabled ?? [];
374923
373382
  } catch {
@@ -374936,26 +373395,26 @@ function disableBeta(name, projectDir = process.cwd()) {
374936
373395
  saveBetas(enabled, projectDir);
374937
373396
  }
374938
373397
  function saveBetas(enabled, projectDir) {
374939
- const specificDir = join21(projectDir, ".specific");
374940
- if (!existsSync19(specificDir)) {
374941
- mkdirSync15(specificDir, { recursive: true });
373398
+ const specificDir = join17(projectDir, ".specific");
373399
+ if (!existsSync16(specificDir)) {
373400
+ mkdirSync12(specificDir, { recursive: true });
374942
373401
  }
374943
373402
  const data = { enabled };
374944
- writeFileSync11(
374945
- join21(projectDir, BETAS_FILE),
373403
+ writeFileSync7(
373404
+ join17(projectDir, BETAS_FILE),
374946
373405
  JSON.stringify(data, null, 2) + "\n"
374947
373406
  );
374948
373407
  }
374949
373408
 
374950
373409
  // src/commands/docs.tsx
374951
373410
  var __dirname3 = dirname8(fileURLToPath3(import.meta.url));
374952
- var docsDir = join22(__dirname3, "docs");
373411
+ var docsDir = join18(__dirname3, "docs");
374953
373412
  var _embeddedDocs = null;
374954
- function docsCommand(path30) {
374955
- const content = resolveDocContent(path30);
373413
+ function docsCommand(path26) {
373414
+ const content = resolveDocContent(path26);
374956
373415
  if (!content) {
374957
373416
  console.error(
374958
- `Documentation not found: ${path30 || "index"}
373417
+ `Documentation not found: ${path26 || "index"}
374959
373418
 
374960
373419
  Run 'specific docs' to see available topics.`
374961
373420
  );
@@ -374973,33 +373432,33 @@ function filterBetaTags(content, enabledBetas) {
374973
373432
  }
374974
373433
  );
374975
373434
  }
374976
- function resolveDocContent(path30) {
374977
- const normalized = path30?.replace(/^\/+|\/+$/g, "") || void 0;
373435
+ function resolveDocContent(path26) {
373436
+ const normalized = path26?.replace(/^\/+|\/+$/g, "") || void 0;
374978
373437
  if (_embeddedDocs) {
374979
373438
  return resolveEmbeddedDoc(normalized);
374980
373439
  }
374981
373440
  return resolveFilesystemDoc(normalized);
374982
373441
  }
374983
- function resolveEmbeddedDoc(path30) {
374984
- if (!path30) {
373442
+ function resolveEmbeddedDoc(path26) {
373443
+ if (!path26) {
374985
373444
  return _embeddedDocs.get("index.md") ?? null;
374986
373445
  }
374987
- const direct = _embeddedDocs.get(`${path30}.md`);
373446
+ const direct = _embeddedDocs.get(`${path26}.md`);
374988
373447
  if (direct) return direct;
374989
- return _embeddedDocs.get(`${path30}/index.md`) ?? null;
373448
+ return _embeddedDocs.get(`${path26}/index.md`) ?? null;
374990
373449
  }
374991
- function resolveFilesystemDoc(path30) {
374992
- if (!path30) {
374993
- const indexPath2 = join22(docsDir, "index.md");
374994
- return existsSync20(indexPath2) ? readFileSync12(indexPath2, "utf-8") : null;
373450
+ function resolveFilesystemDoc(path26) {
373451
+ if (!path26) {
373452
+ const indexPath2 = join18(docsDir, "index.md");
373453
+ return existsSync17(indexPath2) ? readFileSync9(indexPath2, "utf-8") : null;
374995
373454
  }
374996
- const directPath = join22(docsDir, `${path30}.md`);
374997
- if (existsSync20(directPath)) {
374998
- return readFileSync12(directPath, "utf-8");
373455
+ const directPath = join18(docsDir, `${path26}.md`);
373456
+ if (existsSync17(directPath)) {
373457
+ return readFileSync9(directPath, "utf-8");
374999
373458
  }
375000
- const indexPath = join22(docsDir, path30, "index.md");
375001
- if (existsSync20(indexPath)) {
375002
- return readFileSync12(indexPath, "utf-8");
373459
+ const indexPath = join18(docsDir, path26, "index.md");
373460
+ if (existsSync17(indexPath)) {
373461
+ return readFileSync9(indexPath, "utf-8");
375003
373462
  }
375004
373463
  return null;
375005
373464
  }
@@ -375007,9 +373466,9 @@ function resolveFilesystemDoc(path30) {
375007
373466
  // src/commands/check.tsx
375008
373467
  import React3, { useState as useState2, useEffect as useEffect2 } from "react";
375009
373468
  import { render as render3, Text as Text3, Box as Box3 } from "ink";
375010
- import Spinner3 from "ink-spinner";
375011
- import * as fs25 from "fs";
375012
- import * as path22 from "path";
373469
+ import Spinner2 from "ink-spinner";
373470
+ import * as fs20 from "fs";
373471
+ import * as path18 from "path";
375013
373472
  import { execFile as execFile7 } from "child_process";
375014
373473
 
375015
373474
  // node_modules/.pnpm/@specific+config@file+..+config/node_modules/@specific/config/dist/parser.js
@@ -375681,20 +374140,20 @@ function CheckUI() {
375681
374140
  const [state, setState] = useState2({ status: "loading" });
375682
374141
  useEffect2(() => {
375683
374142
  async function load() {
375684
- const configPath = path22.join(process.cwd(), "specific.hcl");
375685
- if (!fs25.existsSync(configPath)) {
374143
+ const configPath = path18.join(process.cwd(), "specific.hcl");
374144
+ if (!fs20.existsSync(configPath)) {
375686
374145
  setState({
375687
374146
  status: "error",
375688
374147
  error: "No specific.hcl found in current directory"
375689
374148
  });
375690
374149
  return;
375691
374150
  }
375692
- const hcl = fs25.readFileSync(configPath, "utf-8");
374151
+ const hcl = fs20.readFileSync(configPath, "utf-8");
375693
374152
  try {
375694
374153
  const config2 = await parseConfig2(hcl);
375695
- const localPath = path22.join(process.cwd(), LOCAL_FILE);
375696
- if (fs25.existsSync(localPath)) {
375697
- const localContent = fs25.readFileSync(localPath, "utf-8");
374154
+ const localPath = path18.join(process.cwd(), LOCAL_FILE);
374155
+ if (fs20.existsSync(localPath)) {
374156
+ const localContent = fs20.readFileSync(localPath, "utf-8");
375698
374157
  try {
375699
374158
  await parseLocalFile(localContent);
375700
374159
  } catch (localErr) {
@@ -375707,8 +374166,8 @@ function CheckUI() {
375707
374166
  }
375708
374167
  for (const build of config2.builds) {
375709
374168
  if (build.dockerfile) {
375710
- const dockerfilePath = path22.resolve(process.cwd(), build.dockerfile);
375711
- if (!fs25.existsSync(dockerfilePath)) {
374169
+ const dockerfilePath = path18.resolve(process.cwd(), build.dockerfile);
374170
+ if (!fs20.existsSync(dockerfilePath)) {
375712
374171
  setState({
375713
374172
  status: "error",
375714
374173
  error: `Build "${build.name}": Dockerfile not found at "${build.dockerfile}" (resolved to ${dockerfilePath})`
@@ -375720,11 +374179,11 @@ function CheckUI() {
375720
374179
  const reshapeChecks2 = [];
375721
374180
  for (const pg of config2.postgres) {
375722
374181
  if (pg.reshape?.enabled) {
375723
- const migrationsDir = path22.resolve(
374182
+ const migrationsDir = path18.resolve(
375724
374183
  process.cwd(),
375725
374184
  pg.reshape.migrations_dir ?? "migrations"
375726
374185
  );
375727
- if (!fs25.existsSync(migrationsDir)) {
374186
+ if (!fs20.existsSync(migrationsDir)) {
375728
374187
  reshapeChecks2.push({
375729
374188
  databaseName: pg.name,
375730
374189
  migrationsDir: pg.reshape.migrations_dir ?? "migrations",
@@ -375755,7 +374214,7 @@ function CheckUI() {
375755
374214
  load();
375756
374215
  }, []);
375757
374216
  if (state.status === "loading") {
375758
- return /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, { color: "blue" }, /* @__PURE__ */ React3.createElement(Spinner3, { type: "dots" })), /* @__PURE__ */ React3.createElement(Text3, null, " Checking configuration..."));
374217
+ return /* @__PURE__ */ React3.createElement(Box3, null, /* @__PURE__ */ React3.createElement(Text3, { color: "blue" }, /* @__PURE__ */ React3.createElement(Spinner2, { type: "dots" })), /* @__PURE__ */ React3.createElement(Text3, null, " Checking configuration..."));
375759
374218
  }
375760
374219
  if (state.status === "error") {
375761
374220
  return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, { color: "red" }, state.error));
@@ -375776,7 +374235,7 @@ function checkCommand() {
375776
374235
  // src/commands/dev.tsx
375777
374236
  import React6, { useState as useState5, useEffect as useEffect3, useRef } from "react";
375778
374237
  import { render as render4, Text as Text6, Box as Box6, useApp as useApp2, Static } from "ink";
375779
- import Spinner4 from "ink-spinner";
374238
+ import Spinner3 from "ink-spinner";
375780
374239
  import { Readable as Readable2 } from "stream";
375781
374240
 
375782
374241
  // src/lib/ui/SecretInput.tsx
@@ -375873,13 +374332,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
375873
374332
  devEnvRef.current = devEnv;
375874
374333
  let currentColorMap = /* @__PURE__ */ new Map();
375875
374334
  devEnv.on("status", (status) => {
375876
- if (status === "setup") {
375877
- setState((s) => ({
375878
- ...s,
375879
- status: "installing-ca",
375880
- caInstallPhase: "installing"
375881
- }));
375882
- } else if (status === "loading") {
374335
+ if (status === "loading") {
375883
374336
  setState((s) => ({ ...s, status: "loading" }));
375884
374337
  } else if (status === "starting") {
375885
374338
  const config2 = devEnv.config;
@@ -375988,20 +374441,11 @@ function DevUI({ instanceKey, tunnelEnabled }) {
375988
374441
  }
375989
374442
  });
375990
374443
  devEnv.on("error", (error) => {
375991
- setState((s) => {
375992
- if (s.status === "installing-ca") {
375993
- return {
375994
- ...s,
375995
- caInstallPhase: "error",
375996
- caError: error.message
375997
- };
375998
- }
375999
- return {
376000
- ...s,
376001
- status: "error",
376002
- error: error.message
376003
- };
376004
- });
374444
+ setState((s) => ({
374445
+ ...s,
374446
+ status: "error",
374447
+ error: error.message
374448
+ }));
376005
374449
  });
376006
374450
  devEnv.on("configError", (errorStr) => {
376007
374451
  setState((s) => ({ ...s, parseError: errorStr }));
@@ -376116,16 +374560,8 @@ function DevUI({ instanceKey, tunnelEnabled }) {
376116
374560
  currentConfigIndex: void 0
376117
374561
  }));
376118
374562
  };
376119
- if (state.status === "installing-ca") {
376120
- if (state.caInstallPhase === "installing") {
376121
- return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Configure development environment"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Awaiting authorization\u2026"));
376122
- }
376123
- if (state.caInstallPhase === "error") {
376124
- return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Setup failed: ", state.caError));
376125
- }
376126
- }
376127
374563
  if (state.status === "loading") {
376128
- return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner4, { type: "dots" })));
374564
+ return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner3, { type: "dots" })));
376129
374565
  }
376130
374566
  if (state.status === "awaiting-secrets" && state.missingSecrets && state.currentSecretIndex !== void 0) {
376131
374567
  const currentSecret = state.missingSecrets[state.currentSecretIndex];
@@ -376152,20 +374588,20 @@ function DevUI({ instanceKey, tunnelEnabled }) {
376152
374588
  ));
376153
374589
  }
376154
374590
  if (state.status === "waiting") {
376155
- return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " No specific.hcl in project yet. Go build something with your coding agent!")));
374591
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner3, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " No specific.hcl in project yet. Go build something with your coding agent!")));
376156
374592
  }
376157
374593
  if (state.status === "error") {
376158
374594
  return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, state.error));
376159
374595
  }
376160
374596
  if (state.status === "stopping") {
376161
- return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Shutting down..."));
374597
+ return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner3, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Shutting down..."));
376162
374598
  }
376163
374599
  if (state.status === "reloading") {
376164
- return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Specific dev server")), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Reloading (specific.hcl changed)...")));
374600
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Specific dev server")), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "yellow" }, /* @__PURE__ */ React6.createElement(Spinner3, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Reloading (specific.hcl changed)...")));
376165
374601
  }
376166
374602
  const { config, resources, services, output } = state;
376167
374603
  if (!config) {
376168
- return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner4, { type: "dots" })));
374604
+ return /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner3, { type: "dots" })));
376169
374605
  }
376170
374606
  if (state.status === "starting") {
376171
374607
  return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Specific dev server"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (Ctrl+C to stop)")), /* @__PURE__ */ React6.createElement(Text6, null, " "), (config.postgres.length > 0 || config.redis.length > 0 || config.storage.length > 0 || config.temporal.length > 0) && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Resources:"), config.postgres.map((pg) => {
@@ -376228,17 +374664,8 @@ function DevUI({ instanceKey, tunnelEnabled }) {
376228
374664
  const defaultPort = running?.ports.get("default");
376229
374665
  const portInfo = defaultPort ? ` \u2192 localhost:${defaultPort}` : "";
376230
374666
  return /* @__PURE__ */ React6.createElement(Text6, { key: svc.name }, /* @__PURE__ */ React6.createElement(Text6, { color: isReady ? "green" : "gray" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, svc.name, portInfo));
376231
- }), /* @__PURE__ */ React6.createElement(Text6, null, " ")), /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Starting...")));
374667
+ }), /* @__PURE__ */ React6.createElement(Text6, null, " ")), /* @__PURE__ */ React6.createElement(Box6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "blue" }, /* @__PURE__ */ React6.createElement(Spinner3, { type: "dots" })), /* @__PURE__ */ React6.createElement(Text6, null, " Starting...")));
376232
374668
  }
376233
- const portSuffix = (() => {
376234
- if (!state.adminUrl || !state.adminUrl.startsWith("https://")) return "";
376235
- try {
376236
- const p = new URL(state.adminUrl).port;
376237
- return p ? `:${p}` : "";
376238
- } catch {
376239
- return "";
376240
- }
376241
- })();
376242
374669
  const staticItems = [
376243
374670
  {
376244
374671
  key: "title",
@@ -376287,7 +374714,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
376287
374714
  const proxyName = endpoint.name === "default" ? svc.name : `${svc.name}-${endpoint.name}`;
376288
374715
  return {
376289
374716
  key: `svc-${svc.name}-${endpoint.name}`,
376290
- content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, displayName), port ? endpoint.public ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, null, " \u2192 "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "https://", proxyName, instanceKey === "default" ? "" : `.${instanceKey}`, ".spcf.localhost", portSuffix), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", port, ")")) : /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", port, ")") : null)
374717
+ content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, displayName), port ? endpoint.public ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, null, " \u2192 "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "http://localhost:", port)) : /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", port, ")") : null)
376291
374718
  };
376292
374719
  });
376293
374720
  }),
@@ -376362,9 +374789,9 @@ function devCommand(instanceKey, tunnelEnabled = false) {
376362
374789
  init_open();
376363
374790
  import React7, { useState as useState6, useEffect as useEffect4, useCallback } from "react";
376364
374791
  import { render as render5, Text as Text7, Box as Box7, useApp as useApp3, useInput as useInput4 } from "ink";
376365
- import Spinner5 from "ink-spinner";
376366
- import * as fs26 from "fs";
376367
- import * as path23 from "path";
374792
+ import Spinner4 from "ink-spinner";
374793
+ import * as fs21 from "fs";
374794
+ import * as path19 from "path";
376368
374795
  function formatBytes(bytes) {
376369
374796
  if (bytes < 1024) return `${bytes} B`;
376370
374797
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
@@ -376400,7 +374827,7 @@ function PhaseIndicator({
376400
374827
  }
376401
374828
  if (phaseIndex === currentIndex) {
376402
374829
  if (showSpinner) {
376403
- return /* @__PURE__ */ React7.createElement(Text7, null, " ", /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner5, { type: "dots" })), " ", label);
374830
+ return /* @__PURE__ */ React7.createElement(Text7, null, " ", /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), " ", label);
376404
374831
  }
376405
374832
  return /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "yellow" }, " ", "\u25CF"), " ", label);
376406
374833
  }
@@ -377118,13 +375545,13 @@ function DeployUI({ environment, config }) {
377118
375545
  verificationUri
377119
375546
  } = state;
377120
375547
  if (phase === "checking-auth") {
377121
- return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Checking authentication..."));
375548
+ return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Checking authentication..."));
377122
375549
  }
377123
375550
  if (phase === "logging-in") {
377124
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Log in to Specific"), userCode ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Text7, null, "Your authentication code:", " ", /* @__PURE__ */ React7.createElement(Text7, { color: "cyan", bold: true }, userCode)), /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Waiting for authentication in browser...")), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "If the browser didn't open, visit: ", verificationUri)) : /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Initiating login...")));
375551
+ return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Log in to Specific"), userCode ? /* @__PURE__ */ React7.createElement(React7.Fragment, null, /* @__PURE__ */ React7.createElement(Text7, null, "Your authentication code:", " ", /* @__PURE__ */ React7.createElement(Text7, { color: "cyan", bold: true }, userCode)), /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Waiting for authentication in browser...")), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "If the browser didn't open, visit: ", verificationUri)) : /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Initiating login...")));
377125
375552
  }
377126
375553
  if (phase === "loading-projects") {
377127
- return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Loading projects..."));
375554
+ return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Loading projects..."));
377128
375555
  }
377129
375556
  if (phase === "selecting-project" && projects && state.organizations && selectedIndex !== void 0) {
377130
375557
  const selectorItems = buildSelectorItems(projects, state.organizations);
@@ -377155,7 +375582,7 @@ function DeployUI({ environment, config }) {
377155
375582
  return /* @__PURE__ */ React7.createElement(NameInput, { onSubmit: handleNameSubmit, onCancel: handleNameCancel });
377156
375583
  }
377157
375584
  if (phase === "creating-project") {
377158
- return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Creating project..."));
375585
+ return /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "blue" }, /* @__PURE__ */ React7.createElement(Spinner4, { type: "dots" })), /* @__PURE__ */ React7.createElement(Text7, null, " Creating project..."));
377159
375586
  }
377160
375587
  const currentSecret = missingSecrets && currentSecretIndex !== void 0 ? missingSecrets[currentSecretIndex] : void 0;
377161
375588
  const currentConfig = missingConfigs && currentConfigIndex !== void 0 ? missingConfigs[currentConfigIndex] : void 0;
@@ -377236,13 +375663,13 @@ function DeployUI({ environment, config }) {
377236
375663
  ), phase === "error" && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, deployment?.error ? /* @__PURE__ */ React7.createElement(StructuredError, { error: deployment.error }) : /* @__PURE__ */ React7.createElement(Text7, { color: "red", bold: true }, error), buildOutput && /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Build output:"), /* @__PURE__ */ React7.createElement(Text7, null, buildOutput))), phase === "success" && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, "Deployment successful!"), deployment?.publicUrls && Object.keys(deployment.publicUrls).length > 0 && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Public URLs:"), Object.entries(deployment.publicUrls).map(([name, url]) => /* @__PURE__ */ React7.createElement(Text7, { key: name }, " ", name, ": ", /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, url))))));
377237
375664
  }
377238
375665
  async function deployCommand(environment) {
377239
- const configPath = path23.join(process.cwd(), "specific.hcl");
377240
- if (!fs26.existsSync(configPath)) {
375666
+ const configPath = path19.join(process.cwd(), "specific.hcl");
375667
+ if (!fs21.existsSync(configPath)) {
377241
375668
  console.error("Error: No specific.hcl found in current directory");
377242
375669
  process.exit(1);
377243
375670
  }
377244
375671
  let config;
377245
- const hcl = fs26.readFileSync(configPath, "utf-8");
375672
+ const hcl = fs21.readFileSync(configPath, "utf-8");
377246
375673
  try {
377247
375674
  config = await parseConfig2(hcl);
377248
375675
  } catch (err) {
@@ -377263,8 +375690,8 @@ async function deployCommand(environment) {
377263
375690
 
377264
375691
  // src/commands/exec.tsx
377265
375692
  import { spawn as spawn6 } from "child_process";
377266
- import * as fs27 from "fs";
377267
- import * as path24 from "path";
375693
+ import * as fs22 from "fs";
375694
+ import * as path20 from "path";
377268
375695
  function startSpinner(text) {
377269
375696
  const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
377270
375697
  let i = 0;
@@ -377305,13 +375732,13 @@ async function execCommand(serviceName, command, instanceKey = "default") {
377305
375732
  }
377306
375733
  }
377307
375734
  };
377308
- const configPath = path24.join(process.cwd(), "specific.hcl");
377309
- if (!fs27.existsSync(configPath)) {
375735
+ const configPath = path20.join(process.cwd(), "specific.hcl");
375736
+ if (!fs22.existsSync(configPath)) {
377310
375737
  console.error("Error: No specific.hcl found in current directory");
377311
375738
  process.exit(1);
377312
375739
  }
377313
375740
  let config;
377314
- const hcl = fs27.readFileSync(configPath, "utf-8");
375741
+ const hcl = fs22.readFileSync(configPath, "utf-8");
377315
375742
  try {
377316
375743
  config = await parseConfig2(hcl);
377317
375744
  } catch (err) {
@@ -377448,11 +375875,11 @@ async function execCommand(serviceName, command, instanceKey = "default") {
377448
375875
  process.on("SIGTERM", () => handleSignal("SIGTERM"));
377449
375876
  let effectiveCwd = process.cwd();
377450
375877
  if (service.root) {
377451
- effectiveCwd = path24.resolve(process.cwd(), service.root);
375878
+ effectiveCwd = path20.resolve(process.cwd(), service.root);
377452
375879
  } else if (service.build) {
377453
375880
  const build = config.builds.find((b) => b.name === service.build.name);
377454
375881
  if (build?.root) {
377455
- effectiveCwd = path24.resolve(process.cwd(), build.root);
375882
+ effectiveCwd = path20.resolve(process.cwd(), build.root);
377456
375883
  }
377457
375884
  }
377458
375885
  spinner?.stop();
@@ -377481,8 +375908,8 @@ async function execCommand(serviceName, command, instanceKey = "default") {
377481
375908
 
377482
375909
  // src/commands/psql.tsx
377483
375910
  import { spawn as spawn7 } from "child_process";
377484
- import * as fs28 from "fs";
377485
- import * as path25 from "path";
375911
+ import * as fs23 from "fs";
375912
+ import * as path21 from "path";
377486
375913
  async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []) {
377487
375914
  let startedResources = [];
377488
375915
  let ownsInstances = false;
@@ -377499,13 +375926,13 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
377499
375926
  }
377500
375927
  }
377501
375928
  };
377502
- const configPath = path25.join(process.cwd(), "specific.hcl");
377503
- if (!fs28.existsSync(configPath)) {
375929
+ const configPath = path21.join(process.cwd(), "specific.hcl");
375930
+ if (!fs23.existsSync(configPath)) {
377504
375931
  console.error("Error: No specific.hcl found in current directory");
377505
375932
  process.exit(1);
377506
375933
  }
377507
375934
  let config;
377508
- const hcl = fs28.readFileSync(configPath, "utf-8");
375935
+ const hcl = fs23.readFileSync(configPath, "utf-8");
377509
375936
  try {
377510
375937
  config = await parseConfig2(hcl);
377511
375938
  } catch (err) {
@@ -377629,8 +376056,8 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
377629
376056
 
377630
376057
  // src/commands/reshape.tsx
377631
376058
  import { spawn as spawn8 } from "child_process";
377632
- import * as fs29 from "fs";
377633
- import * as path26 from "path";
376059
+ import * as fs24 from "fs";
376060
+ import * as path22 from "path";
377634
376061
  var VALID_ACTIONS = ["start", "complete", "status", "abort", "check"];
377635
376062
  var MIGRATION_SUBCOMMANDS = ["start", "complete", "abort"];
377636
376063
  var OFFLINE_ACTIONS = ["check"];
@@ -377642,13 +376069,13 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
377642
376069
  process.exit(1);
377643
376070
  }
377644
376071
  const isOfflineAction = OFFLINE_ACTIONS.includes(action);
377645
- const configPath = path26.join(process.cwd(), "specific.hcl");
376072
+ const configPath = path22.join(process.cwd(), "specific.hcl");
377646
376073
  let config;
377647
376074
  let migrationsDir = "migrations";
377648
376075
  let targetDb;
377649
376076
  try {
377650
- if (fs29.existsSync(configPath)) {
377651
- const configContent = fs29.readFileSync(configPath, "utf-8");
376077
+ if (fs24.existsSync(configPath)) {
376078
+ const configContent = fs24.readFileSync(configPath, "utf-8");
377652
376079
  config = await parseConfig2(configContent);
377653
376080
  if (databaseName) {
377654
376081
  const postgresConfig = config.postgres.find((p) => p.name === databaseName);
@@ -377782,9 +376209,9 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
377782
376209
  }
377783
376210
  const isMigrationSubcommand = MIGRATION_SUBCOMMANDS.includes(action);
377784
376211
  const reshapeArgs = isMigrationSubcommand ? ["migration", action] : [action];
377785
- const fullMigrationsPath = path26.join(process.cwd(), migrationsDir);
376212
+ const fullMigrationsPath = path22.join(process.cwd(), migrationsDir);
377786
376213
  if (action === "check" || action === "start") {
377787
- if (fs29.existsSync(fullMigrationsPath)) {
376214
+ if (fs24.existsSync(fullMigrationsPath)) {
377788
376215
  reshapeArgs.push("--dirs", fullMigrationsPath);
377789
376216
  } else if (action === "check") {
377790
376217
  console.error(`Error: Migrations directory not found: ${fullMigrationsPath}`);
@@ -377833,22 +376260,22 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
377833
376260
  // src/commands/clean.tsx
377834
376261
  import React8, { useState as useState7, useEffect as useEffect5 } from "react";
377835
376262
  import { render as render6, Text as Text8, Box as Box8 } from "ink";
377836
- import Spinner6 from "ink-spinner";
377837
- import * as fs30 from "fs";
377838
- import * as path27 from "path";
376263
+ import Spinner5 from "ink-spinner";
376264
+ import * as fs25 from "fs";
376265
+ import * as path23 from "path";
377839
376266
  function CleanUI({ instanceKey }) {
377840
376267
  const [state, setState] = useState7({ status: "checking" });
377841
376268
  useEffect5(() => {
377842
376269
  async function clean() {
377843
376270
  const projectRoot = process.cwd();
377844
- const specificDir = path27.join(projectRoot, ".specific");
377845
- if (!fs30.existsSync(specificDir)) {
376271
+ const specificDir = path23.join(projectRoot, ".specific");
376272
+ if (!fs25.existsSync(specificDir)) {
377846
376273
  setState({ status: "nothing" });
377847
376274
  return;
377848
376275
  }
377849
376276
  if (instanceKey) {
377850
- const keyDir = path27.join(specificDir, "keys", instanceKey);
377851
- if (!fs30.existsSync(keyDir)) {
376277
+ const keyDir = path23.join(specificDir, "keys", instanceKey);
376278
+ if (!fs25.existsSync(keyDir)) {
377852
376279
  setState({ status: "nothing" });
377853
376280
  return;
377854
376281
  }
@@ -377864,7 +376291,7 @@ function CleanUI({ instanceKey }) {
377864
376291
  await stateManager.cleanStaleState();
377865
376292
  setState({ status: "cleaning" });
377866
376293
  try {
377867
- fs30.rmSync(keyDir, { recursive: true, force: true });
376294
+ fs25.rmSync(keyDir, { recursive: true, force: true });
377868
376295
  setState({ status: "success" });
377869
376296
  } catch (err) {
377870
376297
  setState({
@@ -377873,13 +376300,13 @@ function CleanUI({ instanceKey }) {
377873
376300
  });
377874
376301
  }
377875
376302
  } else {
377876
- const keysDir = path27.join(specificDir, "keys");
377877
- if (!fs30.existsSync(keysDir)) {
376303
+ const keysDir = path23.join(specificDir, "keys");
376304
+ if (!fs25.existsSync(keysDir)) {
377878
376305
  setState({ status: "nothing" });
377879
376306
  return;
377880
376307
  }
377881
- const keys = fs30.readdirSync(keysDir).filter(
377882
- (f) => fs30.statSync(path27.join(keysDir, f)).isDirectory()
376308
+ const keys = fs25.readdirSync(keysDir).filter(
376309
+ (f) => fs25.statSync(path23.join(keysDir, f)).isDirectory()
377883
376310
  );
377884
376311
  for (const key of keys) {
377885
376312
  const stateManager2 = new InstanceStateManager(projectRoot, key);
@@ -377903,7 +376330,7 @@ function CleanUI({ instanceKey }) {
377903
376330
  }
377904
376331
  setState({ status: "cleaning" });
377905
376332
  try {
377906
- fs30.rmSync(keysDir, { recursive: true, force: true });
376333
+ fs25.rmSync(keysDir, { recursive: true, force: true });
377907
376334
  setState({ status: "success" });
377908
376335
  } catch (err) {
377909
376336
  setState({
@@ -377916,10 +376343,10 @@ function CleanUI({ instanceKey }) {
377916
376343
  clean();
377917
376344
  }, [instanceKey]);
377918
376345
  if (state.status === "checking") {
377919
- return /* @__PURE__ */ React8.createElement(Box8, null, /* @__PURE__ */ React8.createElement(Text8, { color: "blue" }, /* @__PURE__ */ React8.createElement(Spinner6, { type: "dots" })), /* @__PURE__ */ React8.createElement(Text8, null, " Checking for running instances..."));
376346
+ return /* @__PURE__ */ React8.createElement(Box8, null, /* @__PURE__ */ React8.createElement(Text8, { color: "blue" }, /* @__PURE__ */ React8.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React8.createElement(Text8, null, " Checking for running instances..."));
377920
376347
  }
377921
376348
  if (state.status === "cleaning") {
377922
- return /* @__PURE__ */ React8.createElement(Box8, null, /* @__PURE__ */ React8.createElement(Text8, { color: "blue" }, /* @__PURE__ */ React8.createElement(Spinner6, { type: "dots" })), /* @__PURE__ */ React8.createElement(Text8, null, " Cleaning app data..."));
376349
+ return /* @__PURE__ */ React8.createElement(Box8, null, /* @__PURE__ */ React8.createElement(Text8, { color: "blue" }, /* @__PURE__ */ React8.createElement(Spinner5, { type: "dots" })), /* @__PURE__ */ React8.createElement(Text8, null, " Cleaning app data..."));
377923
376350
  }
377924
376351
  if (state.status === "error") {
377925
376352
  return /* @__PURE__ */ React8.createElement(Text8, { color: "red" }, "Error: ", state.error);
@@ -378065,11 +376492,11 @@ function betaCommand() {
378065
376492
  // src/commands/update.tsx
378066
376493
  import React11, { useState as useState10, useEffect as useEffect8 } from "react";
378067
376494
  import { render as render9, Text as Text11, Box as Box10, useApp as useApp6 } from "ink";
378068
- import Spinner7 from "ink-spinner";
376495
+ import Spinner6 from "ink-spinner";
378069
376496
 
378070
376497
  // src/lib/update.ts
378071
- import * as fs31 from "fs";
378072
- import * as path28 from "path";
376498
+ import * as fs26 from "fs";
376499
+ import * as path24 from "path";
378073
376500
  var BINARIES_BASE_URL = "https://binaries.specific.dev/cli";
378074
376501
  function compareVersions(a, b) {
378075
376502
  const partsA = a.split(".").map(Number);
@@ -378083,7 +376510,7 @@ function compareVersions(a, b) {
378083
376510
  return 0;
378084
376511
  }
378085
376512
  async function checkForUpdate() {
378086
- const currentVersion = "0.1.92";
376513
+ const currentVersion = "0.1.93";
378087
376514
  const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
378088
376515
  if (!response.ok) {
378089
376516
  throw new Error(`Failed to check for updates: HTTP ${response.status}`);
@@ -378097,9 +376524,9 @@ function getCurrentBinaryPath() {
378097
376524
  }
378098
376525
  function isBinaryWritable() {
378099
376526
  const binaryPath = getCurrentBinaryPath();
378100
- const dir = path28.dirname(binaryPath);
376527
+ const dir = path24.dirname(binaryPath);
378101
376528
  try {
378102
- fs31.accessSync(dir, fs31.constants.W_OK);
376529
+ fs26.accessSync(dir, fs26.constants.W_OK);
378103
376530
  return true;
378104
376531
  } catch {
378105
376532
  return false;
@@ -378107,24 +376534,24 @@ function isBinaryWritable() {
378107
376534
  }
378108
376535
  async function performUpdate(version, onProgress) {
378109
376536
  const binaryPath = getCurrentBinaryPath();
378110
- const binaryDir = path28.dirname(binaryPath);
378111
- const tempPath = path28.join(binaryDir, `.specific-update-${process.pid}`);
376537
+ const binaryDir = path24.dirname(binaryPath);
376538
+ const tempPath = path24.join(binaryDir, `.specific-update-${process.pid}`);
378112
376539
  try {
378113
- const { platform: platform8, arch: arch3 } = getPlatformInfo();
378114
- const url = `${BINARIES_BASE_URL}/${version}/specific-${platform8}-${arch3}`;
376540
+ const { platform: platform3, arch: arch3 } = getPlatformInfo();
376541
+ const url = `${BINARIES_BASE_URL}/${version}/specific-${platform3}-${arch3}`;
378115
376542
  await downloadFile(url, tempPath, onProgress);
378116
- const stat4 = fs31.statSync(tempPath);
376543
+ const stat4 = fs26.statSync(tempPath);
378117
376544
  if (stat4.size === 0) {
378118
376545
  throw new Error("Downloaded binary is empty");
378119
376546
  }
378120
- fs31.chmodSync(tempPath, 493);
376547
+ fs26.chmodSync(tempPath, 493);
378121
376548
  onProgress?.({ phase: "finalizing" });
378122
- fs31.unlinkSync(binaryPath);
378123
- fs31.renameSync(tempPath, binaryPath);
376549
+ fs26.unlinkSync(binaryPath);
376550
+ fs26.renameSync(tempPath, binaryPath);
378124
376551
  } catch (error) {
378125
376552
  try {
378126
- if (fs31.existsSync(tempPath)) {
378127
- fs31.unlinkSync(tempPath);
376553
+ if (fs26.existsSync(tempPath)) {
376554
+ fs26.unlinkSync(tempPath);
378128
376555
  }
378129
376556
  } catch {
378130
376557
  }
@@ -378134,21 +376561,21 @@ async function performUpdate(version, onProgress) {
378134
376561
 
378135
376562
  // src/lib/background-update.ts
378136
376563
  import { spawn as spawn9 } from "child_process";
378137
- import * as fs32 from "fs";
378138
- import * as path29 from "path";
378139
- import * as os12 from "os";
378140
- var SPECIFIC_DIR = path29.join(os12.homedir(), ".specific");
378141
- var RATE_LIMIT_FILE = path29.join(SPECIFIC_DIR, "last-update-check");
378142
- var LOCK_FILE = path29.join(SPECIFIC_DIR, "update.lock");
376564
+ import * as fs27 from "fs";
376565
+ import * as path25 from "path";
376566
+ import * as os6 from "os";
376567
+ var SPECIFIC_DIR = path25.join(os6.homedir(), ".specific");
376568
+ var RATE_LIMIT_FILE = path25.join(SPECIFIC_DIR, "last-update-check");
376569
+ var LOCK_FILE = path25.join(SPECIFIC_DIR, "update.lock");
378143
376570
  var RATE_LIMIT_MS = 60 * 60 * 1e3;
378144
376571
  var STALE_LOCK_MS = 10 * 60 * 1e3;
378145
376572
  function writeCheckTimestamp() {
378146
- fs32.mkdirSync(SPECIFIC_DIR, { recursive: true });
378147
- fs32.writeFileSync(RATE_LIMIT_FILE, String(Date.now()), "utf-8");
376573
+ fs27.mkdirSync(SPECIFIC_DIR, { recursive: true });
376574
+ fs27.writeFileSync(RATE_LIMIT_FILE, String(Date.now()), "utf-8");
378148
376575
  }
378149
376576
  function isRateLimited() {
378150
376577
  try {
378151
- const content = fs32.readFileSync(RATE_LIMIT_FILE, "utf-8").trim();
376578
+ const content = fs27.readFileSync(RATE_LIMIT_FILE, "utf-8").trim();
378152
376579
  const lastCheck = parseInt(content, 10);
378153
376580
  if (isNaN(lastCheck)) return false;
378154
376581
  return Date.now() - lastCheck < RATE_LIMIT_MS;
@@ -378247,7 +376674,7 @@ function UpdateUI() {
378247
376674
  }
378248
376675
  }, [state.phase, exit]);
378249
376676
  if (state.phase === "checking") {
378250
- return /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text11, { color: "blue" }, /* @__PURE__ */ React11.createElement(Spinner7, { type: "dots" })), /* @__PURE__ */ React11.createElement(Text11, null, " Checking for updates..."));
376677
+ return /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text11, { color: "blue" }, /* @__PURE__ */ React11.createElement(Spinner6, { type: "dots" })), /* @__PURE__ */ React11.createElement(Text11, null, " Checking for updates..."));
378251
376678
  }
378252
376679
  if (state.phase === "up-to-date") {
378253
376680
  return /* @__PURE__ */ React11.createElement(Text11, { color: "green" }, "Already up to date (v", state.checkResult.currentVersion, ")");
@@ -378260,7 +376687,7 @@ function UpdateUI() {
378260
376687
  const { currentVersion, latestVersion } = state.checkResult;
378261
376688
  const { progress } = state;
378262
376689
  const progressText = progress?.percent ? ` (${progress.percent}%)` : "";
378263
- return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text11, null, "Updating: v", currentVersion, " \u2192 v", latestVersion), /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text11, { color: "blue" }, /* @__PURE__ */ React11.createElement(Spinner7, { type: "dots" })), /* @__PURE__ */ React11.createElement(Text11, null, " ", progress?.phase === "finalizing" ? "Installing..." : `Downloading${progressText}`)));
376690
+ return /* @__PURE__ */ React11.createElement(Box10, { flexDirection: "column" }, /* @__PURE__ */ React11.createElement(Text11, null, "Updating: v", currentVersion, " \u2192 v", latestVersion), /* @__PURE__ */ React11.createElement(Box10, null, /* @__PURE__ */ React11.createElement(Text11, { color: "blue" }, /* @__PURE__ */ React11.createElement(Spinner6, { type: "dots" })), /* @__PURE__ */ React11.createElement(Text11, null, " ", progress?.phase === "finalizing" ? "Installing..." : `Downloading${progressText}`)));
378264
376691
  }
378265
376692
  if (state.phase === "success") {
378266
376693
  const { currentVersion, latestVersion } = state.checkResult;
@@ -378282,7 +376709,7 @@ function updateCommand() {
378282
376709
  var program = new Command();
378283
376710
  var env = "production";
378284
376711
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
378285
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.92").enablePositionalOptions();
376712
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.93").enablePositionalOptions();
378286
376713
  program.command("init").description("Initialize project for use with a coding agent").option("--agent <name...>", "Agents to configure (cursor, claude, codex, other)").action((options2) => initCommand(options2));
378287
376714
  program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
378288
376715
  program.command("check").description("Validate specific.hcl configuration").action(checkCommand);