@specific.dev/cli 0.1.60 → 0.1.62

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 (50) 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 +1 -1
  4. package/dist/admin/__next.!KGRlZmF1bHQp.txt +1 -1
  5. package/dist/admin/__next._full.txt +1 -1
  6. package/dist/admin/__next._head.txt +1 -1
  7. package/dist/admin/__next._index.txt +1 -1
  8. package/dist/admin/__next._tree.txt +1 -1
  9. package/dist/admin/_not-found/__next._full.txt +1 -1
  10. package/dist/admin/_not-found/__next._head.txt +1 -1
  11. package/dist/admin/_not-found/__next._index.txt +1 -1
  12. package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
  13. package/dist/admin/_not-found/__next._not-found.txt +1 -1
  14. package/dist/admin/_not-found/__next._tree.txt +1 -1
  15. package/dist/admin/_not-found/index.html +1 -1
  16. package/dist/admin/_not-found/index.txt +1 -1
  17. package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.__PAGE__.txt +1 -1
  18. package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.txt +1 -1
  19. package/dist/admin/databases/__next.!KGRlZmF1bHQp.txt +1 -1
  20. package/dist/admin/databases/__next._full.txt +1 -1
  21. package/dist/admin/databases/__next._head.txt +1 -1
  22. package/dist/admin/databases/__next._index.txt +1 -1
  23. package/dist/admin/databases/__next._tree.txt +1 -1
  24. package/dist/admin/databases/index.html +1 -1
  25. package/dist/admin/databases/index.txt +1 -1
  26. package/dist/admin/fullscreen/__next._full.txt +1 -1
  27. package/dist/admin/fullscreen/__next._head.txt +1 -1
  28. package/dist/admin/fullscreen/__next._index.txt +1 -1
  29. package/dist/admin/fullscreen/__next._tree.txt +1 -1
  30. package/dist/admin/fullscreen/__next.fullscreen.__PAGE__.txt +1 -1
  31. package/dist/admin/fullscreen/__next.fullscreen.txt +1 -1
  32. package/dist/admin/fullscreen/databases/__next._full.txt +1 -1
  33. package/dist/admin/fullscreen/databases/__next._head.txt +1 -1
  34. package/dist/admin/fullscreen/databases/__next._index.txt +1 -1
  35. package/dist/admin/fullscreen/databases/__next._tree.txt +1 -1
  36. package/dist/admin/fullscreen/databases/__next.fullscreen.databases.__PAGE__.txt +1 -1
  37. package/dist/admin/fullscreen/databases/__next.fullscreen.databases.txt +1 -1
  38. package/dist/admin/fullscreen/databases/__next.fullscreen.txt +1 -1
  39. package/dist/admin/fullscreen/databases/index.html +1 -1
  40. package/dist/admin/fullscreen/databases/index.txt +1 -1
  41. package/dist/admin/fullscreen/index.html +1 -1
  42. package/dist/admin/fullscreen/index.txt +1 -1
  43. package/dist/admin/index.html +1 -1
  44. package/dist/admin/index.txt +1 -1
  45. package/dist/cli.js +789 -652
  46. package/dist/postinstall.js +1 -1
  47. package/package.json +2 -1
  48. /package/dist/admin/_next/static/{tZ6oW5Gt46x7GjGGbdB6L → bSt01e539un5ZT_sTTRm7}/_buildManifest.js +0 -0
  49. /package/dist/admin/_next/static/{tZ6oW5Gt46x7GjGGbdB6L → bSt01e539un5ZT_sTTRm7}/_clientMiddlewareManifest.json +0 -0
  50. /package/dist/admin/_next/static/{tZ6oW5Gt46x7GjGGbdB6L → bSt01e539un5ZT_sTTRm7}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -39,10 +39,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
39
39
  ));
40
40
 
41
41
  // node_modules/.pnpm/is-docker@3.0.0/node_modules/is-docker/index.js
42
- import fs4 from "node:fs";
42
+ import fs5 from "node:fs";
43
43
  function hasDockerEnv() {
44
44
  try {
45
- fs4.statSync("/.dockerenv");
45
+ fs5.statSync("/.dockerenv");
46
46
  return true;
47
47
  } catch {
48
48
  return false;
@@ -50,7 +50,7 @@ function hasDockerEnv() {
50
50
  }
51
51
  function hasDockerCGroup() {
52
52
  try {
53
- return fs4.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
53
+ return fs5.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
54
54
  } catch {
55
55
  return false;
56
56
  }
@@ -68,7 +68,7 @@ var init_is_docker = __esm({
68
68
  });
69
69
 
70
70
  // node_modules/.pnpm/is-inside-container@1.0.0/node_modules/is-inside-container/index.js
71
- import fs5 from "node:fs";
71
+ import fs6 from "node:fs";
72
72
  function isInsideContainer() {
73
73
  if (cachedResult === void 0) {
74
74
  cachedResult = hasContainerEnv() || isDocker();
@@ -81,7 +81,7 @@ var init_is_inside_container = __esm({
81
81
  init_is_docker();
82
82
  hasContainerEnv = () => {
83
83
  try {
84
- fs5.statSync("/run/.containerenv");
84
+ fs6.statSync("/run/.containerenv");
85
85
  return true;
86
86
  } catch {
87
87
  return false;
@@ -92,8 +92,8 @@ var init_is_inside_container = __esm({
92
92
 
93
93
  // node_modules/.pnpm/is-wsl@3.1.0/node_modules/is-wsl/index.js
94
94
  import process2 from "node:process";
95
- import os3 from "node:os";
96
- import fs6 from "node:fs";
95
+ import os4 from "node:os";
96
+ import fs7 from "node:fs";
97
97
  var isWsl, is_wsl_default;
98
98
  var init_is_wsl = __esm({
99
99
  "node_modules/.pnpm/is-wsl@3.1.0/node_modules/is-wsl/index.js"() {
@@ -102,14 +102,14 @@ var init_is_wsl = __esm({
102
102
  if (process2.platform !== "linux") {
103
103
  return false;
104
104
  }
105
- if (os3.release().toLowerCase().includes("microsoft")) {
105
+ if (os4.release().toLowerCase().includes("microsoft")) {
106
106
  if (isInsideContainer()) {
107
107
  return false;
108
108
  }
109
109
  return true;
110
110
  }
111
111
  try {
112
- return fs6.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isInsideContainer() : false;
112
+ return fs7.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isInsideContainer() : false;
113
113
  } catch {
114
114
  return false;
115
115
  }
@@ -179,7 +179,7 @@ var init_utilities = __esm({
179
179
  // node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/index.js
180
180
  import { promisify as promisify2 } from "node:util";
181
181
  import childProcess2 from "node:child_process";
182
- import fs7, { constants as fsConstants } from "node:fs/promises";
182
+ import fs8, { constants as fsConstants } from "node:fs/promises";
183
183
  var execFile2, wslDrivesMountPoint, powerShellPathFromWsl, powerShellPath2, canAccessPowerShellPromise, canAccessPowerShell, wslDefaultBrowser, convertWslPathToWindows;
184
184
  var init_wsl_utils = __esm({
185
185
  "node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/index.js"() {
@@ -198,14 +198,14 @@ var init_wsl_utils = __esm({
198
198
  const configFilePath = "/etc/wsl.conf";
199
199
  let isConfigFileExists = false;
200
200
  try {
201
- await fs7.access(configFilePath, fsConstants.F_OK);
201
+ await fs8.access(configFilePath, fsConstants.F_OK);
202
202
  isConfigFileExists = true;
203
203
  } catch {
204
204
  }
205
205
  if (!isConfigFileExists) {
206
206
  return defaultMountPoint;
207
207
  }
208
- const configContent = await fs7.readFile(configFilePath, { encoding: "utf8" });
208
+ const configContent = await fs8.readFile(configFilePath, { encoding: "utf8" });
209
209
  const parsedMountPoint = parseMountPointFromConfig(configContent);
210
210
  if (parsedMountPoint === void 0) {
211
211
  return defaultMountPoint;
@@ -224,7 +224,7 @@ var init_wsl_utils = __esm({
224
224
  canAccessPowerShellPromise ??= (async () => {
225
225
  try {
226
226
  const psPath = await powerShellPath2();
227
- await fs7.access(psPath, fsConstants.X_OK);
227
+ await fs8.access(psPath, fsConstants.X_OK);
228
228
  return true;
229
229
  } catch {
230
230
  return false;
@@ -238,15 +238,15 @@ var init_wsl_utils = __esm({
238
238
  const { stdout } = await executePowerShell(command, { powerShellPath: psPath });
239
239
  return stdout.trim();
240
240
  };
241
- convertWslPathToWindows = async (path26) => {
242
- if (/^[a-z]+:\/\//i.test(path26)) {
243
- return path26;
241
+ convertWslPathToWindows = async (path27) => {
242
+ if (/^[a-z]+:\/\//i.test(path27)) {
243
+ return path27;
244
244
  }
245
245
  try {
246
- const { stdout } = await execFile2("wslpath", ["-aw", path26], { encoding: "utf8" });
246
+ const { stdout } = await execFile2("wslpath", ["-aw", path27], { encoding: "utf8" });
247
247
  return stdout.trim();
248
248
  } catch {
249
- return path26;
249
+ return path27;
250
250
  }
251
251
  };
252
252
  }
@@ -432,10 +432,10 @@ __export(open_exports, {
432
432
  openApp: () => openApp
433
433
  });
434
434
  import process8 from "node:process";
435
- import path4 from "node:path";
435
+ import path5 from "node:path";
436
436
  import { fileURLToPath } from "node:url";
437
437
  import childProcess3 from "node:child_process";
438
- import fs8, { constants as fsConstants2 } from "node:fs/promises";
438
+ import fs9, { constants as fsConstants2 } from "node:fs/promises";
439
439
  function detectArchBinary(binary) {
440
440
  if (typeof binary === "string" || Array.isArray(binary)) {
441
441
  return binary;
@@ -446,16 +446,16 @@ function detectArchBinary(binary) {
446
446
  }
447
447
  return archBinary;
448
448
  }
449
- function detectPlatformBinary({ [platform2]: platformBinary }, { wsl } = {}) {
449
+ function detectPlatformBinary({ [platform3]: platformBinary }, { wsl } = {}) {
450
450
  if (wsl && is_wsl_default) {
451
451
  return detectArchBinary(wsl);
452
452
  }
453
453
  if (!platformBinary) {
454
- throw new Error(`${platform2} is not supported`);
454
+ throw new Error(`${platform3} is not supported`);
455
455
  }
456
456
  return detectArchBinary(platformBinary);
457
457
  }
458
- var fallbackAttemptSymbol, __dirname, localXdgOpenPath, platform2, arch, tryEachApp, baseOpen, open, openApp, apps, open_default;
458
+ var fallbackAttemptSymbol, __dirname, localXdgOpenPath, platform3, arch, tryEachApp, baseOpen, open, openApp, apps, open_default;
459
459
  var init_open = __esm({
460
460
  "node_modules/.pnpm/open@11.0.0/node_modules/open/index.js"() {
461
461
  init_wsl_utils();
@@ -465,9 +465,9 @@ var init_open = __esm({
465
465
  init_is_inside_container();
466
466
  init_is_in_ssh();
467
467
  fallbackAttemptSymbol = Symbol("fallbackAttempt");
468
- __dirname = import.meta.url ? path4.dirname(fileURLToPath(import.meta.url)) : "";
469
- localXdgOpenPath = path4.join(__dirname, "xdg-open");
470
- ({ platform: platform2, arch } = process8);
468
+ __dirname = import.meta.url ? path5.dirname(fileURLToPath(import.meta.url)) : "";
469
+ localXdgOpenPath = path5.join(__dirname, "xdg-open");
470
+ ({ platform: platform3, arch } = process8);
471
471
  tryEachApp = async (apps2, opener) => {
472
472
  if (apps2.length === 0) {
473
473
  return;
@@ -564,7 +564,7 @@ var init_open = __esm({
564
564
  if (is_wsl_default && !isInsideContainer() && !is_in_ssh_default && !app) {
565
565
  shouldUseWindowsInWsl = await canAccessPowerShell();
566
566
  }
567
- if (platform2 === "darwin") {
567
+ if (platform3 === "darwin") {
568
568
  command = "open";
569
569
  if (options2.wait) {
570
570
  cliArguments.push("--wait-apps");
@@ -578,7 +578,7 @@ var init_open = __esm({
578
578
  if (app) {
579
579
  cliArguments.push("-a", app);
580
580
  }
581
- } else if (platform2 === "win32" || shouldUseWindowsInWsl) {
581
+ } else if (platform3 === "win32" || shouldUseWindowsInWsl) {
582
582
  command = await powerShellPath2();
583
583
  cliArguments.push(...executePowerShell.argumentsPrefix);
584
584
  if (!is_wsl_default) {
@@ -614,11 +614,11 @@ var init_open = __esm({
614
614
  const isBundled = !__dirname || __dirname === "/";
615
615
  let exeLocalXdgOpen = false;
616
616
  try {
617
- await fs8.access(localXdgOpenPath, fsConstants2.X_OK);
617
+ await fs9.access(localXdgOpenPath, fsConstants2.X_OK);
618
618
  exeLocalXdgOpen = true;
619
619
  } catch {
620
620
  }
621
- const useSystemXdgOpen = process8.versions.electron ?? (platform2 === "android" || isBundled || !exeLocalXdgOpen);
621
+ const useSystemXdgOpen = process8.versions.electron ?? (platform3 === "android" || isBundled || !exeLocalXdgOpen);
622
622
  command = useSystemXdgOpen ? "xdg-open" : localXdgOpenPath;
623
623
  }
624
624
  if (appArguments.length > 0) {
@@ -629,7 +629,7 @@ var init_open = __esm({
629
629
  childProcessOptions.detached = true;
630
630
  }
631
631
  }
632
- if (platform2 === "darwin" && appArguments.length > 0) {
632
+ if (platform3 === "darwin" && appArguments.length > 0) {
633
633
  cliArguments.push("--args", ...appArguments);
634
634
  }
635
635
  if (options2.target) {
@@ -754,8 +754,8 @@ var require_dist = __commonJS({
754
754
  var $global, $module, $NaN = NaN;
755
755
  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");
756
756
  if ("undefined" != typeof module && ($module = module), !$global.fs && $global.require) try {
757
- var fs28 = $global.require("fs");
758
- "object" == typeof fs28 && null !== fs28 && 0 !== Object.keys(fs28).length && ($global.fs = fs28);
757
+ var fs29 = $global.require("fs");
758
+ "object" == typeof fs29 && null !== fs29 && 0 !== Object.keys(fs29).length && ($global.fs = fs29);
759
759
  } catch (e) {
760
760
  }
761
761
  if (!$global.fs) {
@@ -182949,8 +182949,11 @@ import { Command } from "commander";
182949
182949
  import React2, { useState, useEffect } from "react";
182950
182950
  import { render as render2, Text as Text2, Box as Box2, useInput, useApp } from "ink";
182951
182951
  import "ink-spinner";
182952
- import * as fs10 from "fs";
182953
- import * as path6 from "path";
182952
+ import * as fs11 from "fs";
182953
+ import * as path7 from "path";
182954
+
182955
+ // src/lib/dev/system-setup.ts
182956
+ import { execSync as execSync2 } from "child_process";
182954
182957
 
182955
182958
  // src/lib/dev/local-ca.ts
182956
182959
  import * as fs from "fs";
@@ -182965,6 +182968,38 @@ function caFilesExist() {
182965
182968
  const caDir = getCADir();
182966
182969
  return fs.existsSync(path.join(caDir, "ca.key")) && fs.existsSync(path.join(caDir, "ca.crt"));
182967
182970
  }
182971
+ function caInstalledInTrustStore() {
182972
+ if (!caFilesExist()) {
182973
+ return false;
182974
+ }
182975
+ const platform5 = os.platform();
182976
+ const certPath = path.join(getCADir(), "ca.crt");
182977
+ const diskCert = fs.readFileSync(certPath, "utf-8").replace(/\r\n/g, "\n").trim();
182978
+ try {
182979
+ if (platform5 === "darwin") {
182980
+ const keychainCert = execSync(
182981
+ 'security find-certificate -c "Specific Local Development CA" -p /Library/Keychains/System.keychain',
182982
+ { encoding: "utf-8" }
182983
+ ).replace(/\r\n/g, "\n").trim();
182984
+ return keychainCert === diskCert;
182985
+ } else if (platform5 === "linux") {
182986
+ const trustPaths = [
182987
+ "/usr/local/share/ca-certificates/specific-local-ca.crt",
182988
+ "/etc/pki/ca-trust/source/anchors/specific-local-ca.crt"
182989
+ ];
182990
+ for (const trustPath of trustPaths) {
182991
+ if (fs.existsSync(trustPath)) {
182992
+ const trustedCert = fs.readFileSync(trustPath, "utf-8").replace(/\r\n/g, "\n").trim();
182993
+ return trustedCert === diskCert;
182994
+ }
182995
+ }
182996
+ return false;
182997
+ }
182998
+ } catch {
182999
+ return false;
183000
+ }
183001
+ return false;
183002
+ }
182968
183003
  function createNameConstraintsExtension(permittedDNSNames) {
182969
183004
  const nameConstraintsOID = "2.5.29.30";
182970
183005
  const permittedSubtrees = permittedDNSNames.map((dnsName) => {
@@ -183054,32 +183089,30 @@ function removeCA() {
183054
183089
  fs.unlinkSync(certPath);
183055
183090
  }
183056
183091
  }
183057
- function installCAToTrustStore(certPath) {
183058
- const platform4 = os.platform();
183059
- if (platform4 === "darwin") {
183060
- execSync(
183061
- `sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${certPath}"`,
183062
- { stdio: "inherit" }
183063
- );
183064
- } else if (platform4 === "linux") {
183092
+ function getCAInstallCommands(certPath) {
183093
+ const platform5 = os.platform();
183094
+ if (platform5 === "darwin") {
183095
+ return [
183096
+ // Remove any existing cert with the same CN first — add-trusted-cert
183097
+ // silently does nothing if a cert with the same subject already exists.
183098
+ 'security delete-certificate -c "Specific Local Development CA" /Library/Keychains/System.keychain 2>/dev/null || true',
183099
+ `security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${certPath}"`
183100
+ ];
183101
+ } else if (platform5 === "linux") {
183065
183102
  if (fs.existsSync("/usr/local/share/ca-certificates")) {
183066
- execSync(
183067
- `sudo cp "${certPath}" /usr/local/share/ca-certificates/specific-local-ca.crt`,
183068
- { stdio: "inherit" }
183069
- );
183070
- execSync("sudo update-ca-certificates", { stdio: "inherit" });
183103
+ return [
183104
+ `cp "${certPath}" /usr/local/share/ca-certificates/specific-local-ca.crt`,
183105
+ "update-ca-certificates"
183106
+ ];
183071
183107
  } else if (fs.existsSync("/etc/pki/ca-trust/source/anchors")) {
183072
- execSync(
183073
- `sudo cp "${certPath}" /etc/pki/ca-trust/source/anchors/specific-local-ca.crt`,
183074
- { stdio: "inherit" }
183075
- );
183076
- execSync("sudo update-ca-trust extract", { stdio: "inherit" });
183077
- } else {
183078
- throw new Error("Could not detect Linux certificate trust mechanism");
183108
+ return [
183109
+ `cp "${certPath}" /etc/pki/ca-trust/source/anchors/specific-local-ca.crt`,
183110
+ "update-ca-trust extract"
183111
+ ];
183079
183112
  }
183080
- } else {
183081
- throw new Error(`Unsupported platform: ${platform4}`);
183113
+ throw new Error("Could not detect Linux certificate trust mechanism");
183082
183114
  }
183115
+ throw new Error(`Unsupported platform: ${platform5}`);
183083
183116
  }
183084
183117
  function generateCertificate(domain, keys = []) {
183085
183118
  const caDir = getCADir();
@@ -183118,14 +183151,164 @@ function generateCertificate(domain, keys = []) {
183118
183151
  };
183119
183152
  }
183120
183153
 
183154
+ // src/lib/dev/resolver-config.ts
183155
+ import * as fs2 from "fs";
183156
+ import * as os2 from "os";
183157
+ var RESOLVER_FILE_MACOS = "/etc/resolver/local.spcf.app";
183158
+ var RESOLVER_FILE_LINUX = "/etc/systemd/resolved.conf.d/specific-local.conf";
183159
+ function resolverConfigExists() {
183160
+ const platform5 = os2.platform();
183161
+ if (platform5 === "darwin") {
183162
+ return fs2.existsSync(RESOLVER_FILE_MACOS);
183163
+ } else if (platform5 === "linux") {
183164
+ return fs2.existsSync(RESOLVER_FILE_LINUX);
183165
+ }
183166
+ return false;
183167
+ }
183168
+ function getResolverInstallCommands(port) {
183169
+ const platform5 = os2.platform();
183170
+ if (platform5 === "darwin") {
183171
+ return [
183172
+ "mkdir -p /etc/resolver",
183173
+ `printf "nameserver 127.0.0.1\\nport ${port}\\n" > ${RESOLVER_FILE_MACOS}`
183174
+ ];
183175
+ } else if (platform5 === "linux") {
183176
+ if (fs2.existsSync("/etc/systemd/resolved.conf.d") || fs2.existsSync("/etc/systemd")) {
183177
+ return [
183178
+ "mkdir -p /etc/systemd/resolved.conf.d",
183179
+ `printf "[Resolve]\\nDNS=127.0.0.1:${port}\\nDomains=~local.spcf.app\\n" > ${RESOLVER_FILE_LINUX}`,
183180
+ "systemctl restart systemd-resolved"
183181
+ ];
183182
+ }
183183
+ return [];
183184
+ }
183185
+ return [];
183186
+ }
183187
+
183188
+ // src/lib/dev/dns-server.ts
183189
+ import DNS from "dns2";
183190
+
183191
+ // src/lib/dev/debug-logger.ts
183192
+ import * as fs3 from "fs";
183193
+ import * as os3 from "os";
183194
+ import * as path2 from "path";
183195
+ var DEBUG_LOG_PATH = ".specific/debug.log";
183196
+ var logStream = null;
183197
+ function writeLog(source, message) {
183198
+ const logPath = path2.join(os3.homedir(), DEBUG_LOG_PATH);
183199
+ fs3.mkdirSync(path2.dirname(logPath), { recursive: true });
183200
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
183201
+ const line = message.endsWith("\n") ? message : message + "\n";
183202
+ fs3.appendFileSync(logPath, `[${timestamp}] [${source}] ${line}`);
183203
+ }
183204
+ function pipeProcess(source, proc) {
183205
+ proc.stdout?.on("data", (data) => {
183206
+ const text = data.toString();
183207
+ const lines = text.split("\n").filter((line) => line.length > 0);
183208
+ for (const line of lines) {
183209
+ writeLog(source, line);
183210
+ }
183211
+ });
183212
+ proc.stderr?.on("data", (data) => {
183213
+ const text = data.toString();
183214
+ const lines = text.split("\n").filter((line) => line.length > 0);
183215
+ for (const line of lines) {
183216
+ writeLog(source, line);
183217
+ }
183218
+ });
183219
+ proc.on("error", (err) => {
183220
+ writeLog(`${source}:error`, err.message);
183221
+ });
183222
+ proc.on("exit", (code, signal) => {
183223
+ writeLog(source, `Process exited with code=${code} signal=${signal}`);
183224
+ });
183225
+ }
183226
+ function closeDebugLog() {
183227
+ if (logStream) {
183228
+ logStream.end();
183229
+ logStream = null;
183230
+ }
183231
+ }
183232
+
183233
+ // src/lib/dev/dns-server.ts
183234
+ var DNS_PORT = 15353;
183235
+ async function startDnsServer(port = DNS_PORT) {
183236
+ const server = DNS.createServer({
183237
+ udp: true,
183238
+ handle: (request, send) => {
183239
+ const response = DNS.Packet.createResponseFromRequest(request);
183240
+ for (const question of request.questions) {
183241
+ if (question.type === DNS.Packet.TYPE.A) {
183242
+ response.answers.push({
183243
+ name: question.name,
183244
+ type: DNS.Packet.TYPE.A,
183245
+ class: DNS.Packet.CLASS.IN,
183246
+ ttl: 300,
183247
+ address: "127.0.0.1"
183248
+ });
183249
+ }
183250
+ }
183251
+ send(response);
183252
+ }
183253
+ });
183254
+ await server.listen({
183255
+ udp: { port, address: "127.0.0.1" }
183256
+ });
183257
+ writeLog("dns", `DNS server started on 127.0.0.1:${port}`);
183258
+ return {
183259
+ port,
183260
+ async stop() {
183261
+ await server.close();
183262
+ writeLog("dns", "DNS server stopped");
183263
+ }
183264
+ };
183265
+ }
183266
+
183267
+ // src/lib/dev/system-setup.ts
183268
+ import * as path3 from "path";
183269
+ function systemSetupNeeded() {
183270
+ return !caInstalledInTrustStore() || !resolverConfigExists();
183271
+ }
183272
+ function performSystemSetup() {
183273
+ const needsGenerate = !caFilesExist();
183274
+ const needsInstallCA = !caInstalledInTrustStore();
183275
+ const needsResolver = !resolverConfigExists();
183276
+ if (!needsInstallCA && !needsResolver) {
183277
+ return;
183278
+ }
183279
+ if (needsGenerate) {
183280
+ const { key, cert } = generateRootCA();
183281
+ saveCA(key, cert);
183282
+ }
183283
+ const certPath = path3.join(getCADir(), "ca.crt");
183284
+ const commands = [];
183285
+ if (needsInstallCA) {
183286
+ commands.push(...getCAInstallCommands(certPath));
183287
+ }
183288
+ if (needsResolver) {
183289
+ commands.push(...getResolverInstallCommands(DNS_PORT));
183290
+ }
183291
+ if (commands.length === 0) {
183292
+ return;
183293
+ }
183294
+ try {
183295
+ execSync2(`sudo sh -c '${commands.join(" && ")}'`, { stdio: "inherit" });
183296
+ } catch (err) {
183297
+ if (needsGenerate) {
183298
+ removeCA();
183299
+ }
183300
+ throw err;
183301
+ }
183302
+ }
183303
+
183121
183304
  // src/lib/analytics/index.ts
183122
183305
  import { PostHog } from "posthog-node";
183123
- import * as os5 from "os";
183306
+ import * as os6 from "os";
183124
183307
  import * as crypto from "crypto";
183125
183308
 
183126
183309
  // src/lib/project/config.ts
183127
- import * as fs2 from "fs";
183128
- import * as path2 from "path";
183310
+ import * as fs4 from "fs";
183311
+ import * as path4 from "path";
183129
183312
  var PROJECT_ID_FILE = ".specific/project_id";
183130
183313
  var ProjectNotLinkedError = class extends Error {
183131
183314
  constructor() {
@@ -183140,32 +183323,32 @@ Run: specific deploy`
183140
183323
  }
183141
183324
  };
183142
183325
  function readProjectId(projectDir = process.cwd()) {
183143
- const projectIdPath = path2.join(projectDir, PROJECT_ID_FILE);
183144
- if (!fs2.existsSync(projectIdPath)) {
183326
+ const projectIdPath = path4.join(projectDir, PROJECT_ID_FILE);
183327
+ if (!fs4.existsSync(projectIdPath)) {
183145
183328
  throw new ProjectNotLinkedError();
183146
183329
  }
183147
- const projectId = fs2.readFileSync(projectIdPath, "utf-8").trim();
183330
+ const projectId = fs4.readFileSync(projectIdPath, "utf-8").trim();
183148
183331
  if (!projectId) {
183149
183332
  throw new Error(`${PROJECT_ID_FILE} is empty`);
183150
183333
  }
183151
183334
  return projectId;
183152
183335
  }
183153
183336
  function hasProjectId(projectDir = process.cwd()) {
183154
- const projectIdPath = path2.join(projectDir, PROJECT_ID_FILE);
183155
- return fs2.existsSync(projectIdPath);
183337
+ const projectIdPath = path4.join(projectDir, PROJECT_ID_FILE);
183338
+ return fs4.existsSync(projectIdPath);
183156
183339
  }
183157
183340
  function writeProjectId(projectId, projectDir = process.cwd()) {
183158
- const specificDir = path2.join(projectDir, ".specific");
183159
- if (!fs2.existsSync(specificDir)) {
183160
- fs2.mkdirSync(specificDir, { recursive: true });
183341
+ const specificDir = path4.join(projectDir, ".specific");
183342
+ if (!fs4.existsSync(specificDir)) {
183343
+ fs4.mkdirSync(specificDir, { recursive: true });
183161
183344
  }
183162
- fs2.writeFileSync(path2.join(specificDir, "project_id"), projectId + "\n");
183345
+ fs4.writeFileSync(path4.join(specificDir, "project_id"), projectId + "\n");
183163
183346
  }
183164
183347
 
183165
183348
  // src/lib/auth/credentials.ts
183166
- import * as fs9 from "fs";
183167
- import * as path5 from "path";
183168
- import * as os4 from "os";
183349
+ import * as fs10 from "fs";
183350
+ import * as path6 from "path";
183351
+ import * as os5 from "os";
183169
183352
 
183170
183353
  // src/lib/auth/errors.ts
183171
183354
  var RefreshTokenExpiredError = class extends Error {
@@ -183242,48 +183425,6 @@ import React from "react";
183242
183425
  import { render, Box, Text } from "ink";
183243
183426
  import Spinner from "ink-spinner";
183244
183427
 
183245
- // src/lib/dev/debug-logger.ts
183246
- import * as fs3 from "fs";
183247
- import * as os2 from "os";
183248
- import * as path3 from "path";
183249
- var DEBUG_LOG_PATH = ".specific/debug.log";
183250
- var logStream = null;
183251
- function writeLog(source, message) {
183252
- const logPath = path3.join(os2.homedir(), DEBUG_LOG_PATH);
183253
- fs3.mkdirSync(path3.dirname(logPath), { recursive: true });
183254
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
183255
- const line = message.endsWith("\n") ? message : message + "\n";
183256
- fs3.appendFileSync(logPath, `[${timestamp}] [${source}] ${line}`);
183257
- }
183258
- function pipeProcess(source, proc) {
183259
- proc.stdout?.on("data", (data) => {
183260
- const text = data.toString();
183261
- const lines = text.split("\n").filter((line) => line.length > 0);
183262
- for (const line of lines) {
183263
- writeLog(source, line);
183264
- }
183265
- });
183266
- proc.stderr?.on("data", (data) => {
183267
- const text = data.toString();
183268
- const lines = text.split("\n").filter((line) => line.length > 0);
183269
- for (const line of lines) {
183270
- writeLog(source, line);
183271
- }
183272
- });
183273
- proc.on("error", (err) => {
183274
- writeLog(`${source}:error`, err.message);
183275
- });
183276
- proc.on("exit", (code, signal) => {
183277
- writeLog(source, `Process exited with code=${code} signal=${signal}`);
183278
- });
183279
- }
183280
- function closeDebugLog() {
183281
- if (logStream) {
183282
- logStream.end();
183283
- logStream = null;
183284
- }
183285
- }
183286
-
183287
183428
  // src/lib/api/client.ts
183288
183429
  var ApiClient = class {
183289
183430
  baseUrl;
@@ -183756,18 +183897,18 @@ function performLogin(options2 = {}) {
183756
183897
 
183757
183898
  // src/lib/auth/credentials.ts
183758
183899
  function getUserCredentialsDir() {
183759
- return path5.join(os4.homedir(), ".specific");
183900
+ return path6.join(os5.homedir(), ".specific");
183760
183901
  }
183761
183902
  function getCredentialsPath() {
183762
- return path5.join(getUserCredentialsDir(), "credentials.json");
183903
+ return path6.join(getUserCredentialsDir(), "credentials.json");
183763
183904
  }
183764
183905
  function readUserCredentials() {
183765
183906
  const credentialsPath = getCredentialsPath();
183766
- if (!fs9.existsSync(credentialsPath)) {
183907
+ if (!fs10.existsSync(credentialsPath)) {
183767
183908
  return null;
183768
183909
  }
183769
183910
  try {
183770
- const content = fs9.readFileSync(credentialsPath, "utf-8");
183911
+ const content = fs10.readFileSync(credentialsPath, "utf-8");
183771
183912
  return JSON.parse(content);
183772
183913
  } catch {
183773
183914
  return null;
@@ -183775,18 +183916,18 @@ function readUserCredentials() {
183775
183916
  }
183776
183917
  function writeUserCredentials(credentials) {
183777
183918
  const dir = getUserCredentialsDir();
183778
- if (!fs9.existsSync(dir)) {
183779
- fs9.mkdirSync(dir, { recursive: true, mode: 448 });
183919
+ if (!fs10.existsSync(dir)) {
183920
+ fs10.mkdirSync(dir, { recursive: true, mode: 448 });
183780
183921
  }
183781
183922
  const credentialsPath = getCredentialsPath();
183782
- fs9.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {
183923
+ fs10.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {
183783
183924
  mode: 384
183784
183925
  });
183785
183926
  }
183786
183927
  function clearUserCredentials() {
183787
183928
  const credentialsPath = getCredentialsPath();
183788
- if (fs9.existsSync(credentialsPath)) {
183789
- fs9.unlinkSync(credentialsPath);
183929
+ if (fs10.existsSync(credentialsPath)) {
183930
+ fs10.unlinkSync(credentialsPath);
183790
183931
  }
183791
183932
  }
183792
183933
  function isLoggedIn() {
@@ -183840,7 +183981,7 @@ function isEnabled() {
183840
183981
  }
183841
183982
  function getAnonymousId() {
183842
183983
  if (anonymousId) return anonymousId;
183843
- const machineId = `${os5.hostname()}-${os5.userInfo().username}`;
183984
+ const machineId = `${os6.hostname()}-${os6.userInfo().username}`;
183844
183985
  anonymousId = crypto.createHash("sha256").update(machineId).digest("hex").slice(0, 16);
183845
183986
  return anonymousId;
183846
183987
  }
@@ -183873,7 +184014,7 @@ function trackEvent(event, properties) {
183873
184014
  event,
183874
184015
  properties: {
183875
184016
  ...properties,
183876
- cli_version: "0.1.60",
184017
+ cli_version: "0.1.62",
183877
184018
  platform: process.platform,
183878
184019
  node_version: process.version,
183879
184020
  project_id: getProjectId(),
@@ -183910,67 +184051,67 @@ var options = [
183910
184051
  { id: "other", label: "Other / Manual" }
183911
184052
  ];
183912
184053
  function isGitProject() {
183913
- const gitPath = path6.join(process.cwd(), ".git");
183914
- return fs10.existsSync(gitPath);
184054
+ const gitPath = path7.join(process.cwd(), ".git");
184055
+ return fs11.existsSync(gitPath);
183915
184056
  }
183916
184057
  function detectExistingAgents() {
183917
184058
  const detected = {};
183918
- const cursorDir = path6.join(process.cwd(), ".cursor");
183919
- if (fs10.existsSync(cursorDir)) {
184059
+ const cursorDir = path7.join(process.cwd(), ".cursor");
184060
+ if (fs11.existsSync(cursorDir)) {
183920
184061
  detected["cursor"] = true;
183921
184062
  }
183922
- const claudeDir = path6.join(process.cwd(), ".claude");
183923
- const claudeMd = path6.join(process.cwd(), "CLAUDE.md");
183924
- if (fs10.existsSync(claudeDir) || fs10.existsSync(claudeMd)) {
184063
+ const claudeDir = path7.join(process.cwd(), ".claude");
184064
+ const claudeMd = path7.join(process.cwd(), "CLAUDE.md");
184065
+ if (fs11.existsSync(claudeDir) || fs11.existsSync(claudeMd)) {
183925
184066
  detected["claude"] = true;
183926
184067
  }
183927
- const agentsMd = path6.join(process.cwd(), "AGENTS.md");
183928
- if (fs10.existsSync(agentsMd)) {
184068
+ const agentsMd = path7.join(process.cwd(), "AGENTS.md");
184069
+ if (fs11.existsSync(agentsMd)) {
183929
184070
  detected["codex"] = true;
183930
184071
  }
183931
184072
  return detected;
183932
184073
  }
183933
184074
  function appendOrCreateFile(filePath, content) {
183934
- if (fs10.existsSync(filePath)) {
183935
- const existing = fs10.readFileSync(filePath, "utf-8");
184075
+ if (fs11.existsSync(filePath)) {
184076
+ const existing = fs11.readFileSync(filePath, "utf-8");
183936
184077
  if (existing.includes("specific docs") || existing.includes("specific check")) {
183937
184078
  return "unchanged";
183938
184079
  }
183939
184080
  const separator = existing.endsWith("\n") ? "\n" : "\n\n";
183940
- fs10.writeFileSync(filePath, existing + separator + content + "\n");
184081
+ fs11.writeFileSync(filePath, existing + separator + content + "\n");
183941
184082
  return "modified";
183942
184083
  } else {
183943
- fs10.writeFileSync(filePath, content + "\n");
184084
+ fs11.writeFileSync(filePath, content + "\n");
183944
184085
  return "created";
183945
184086
  }
183946
184087
  }
183947
184088
  function addToGitignore() {
183948
- const gitignorePath = path6.join(process.cwd(), ".gitignore");
184089
+ const gitignorePath = path7.join(process.cwd(), ".gitignore");
183949
184090
  const entries = [".specific", "specific.local"];
183950
- if (fs10.existsSync(gitignorePath)) {
183951
- const existing = fs10.readFileSync(gitignorePath, "utf-8");
184091
+ if (fs11.existsSync(gitignorePath)) {
184092
+ const existing = fs11.readFileSync(gitignorePath, "utf-8");
183952
184093
  const lines = existing.split("\n").map((l) => l.trim());
183953
184094
  const missingEntries = entries.filter((entry) => !lines.includes(entry));
183954
184095
  if (missingEntries.length === 0) {
183955
184096
  return "unchanged";
183956
184097
  }
183957
184098
  const separator = existing.endsWith("\n") ? "" : "\n";
183958
- fs10.writeFileSync(
184099
+ fs11.writeFileSync(
183959
184100
  gitignorePath,
183960
184101
  existing + separator + missingEntries.join("\n") + "\n"
183961
184102
  );
183962
184103
  return "modified";
183963
184104
  } else {
183964
- fs10.writeFileSync(gitignorePath, entries.join("\n") + "\n");
184105
+ fs11.writeFileSync(gitignorePath, entries.join("\n") + "\n");
183965
184106
  return "created";
183966
184107
  }
183967
184108
  }
183968
184109
  function configureClaudeCodePermissions() {
183969
- const claudeDir = path6.join(process.cwd(), ".claude");
183970
- const settingsPath = path6.join(claudeDir, "settings.local.json");
184110
+ const claudeDir = path7.join(process.cwd(), ".claude");
184111
+ const settingsPath = path7.join(claudeDir, "settings.local.json");
183971
184112
  const permissions = ["Bash(specific docs:*)", "Bash(specific check:*)"];
183972
- if (fs10.existsSync(settingsPath)) {
183973
- const existing = JSON.parse(fs10.readFileSync(settingsPath, "utf-8"));
184113
+ if (fs11.existsSync(settingsPath)) {
184114
+ const existing = JSON.parse(fs11.readFileSync(settingsPath, "utf-8"));
183974
184115
  const allowList = existing?.permissions?.allow || [];
183975
184116
  const missingPermissions = permissions.filter(
183976
184117
  (p) => !allowList.includes(p)
@@ -183985,39 +184126,39 @@ function configureClaudeCodePermissions() {
183985
184126
  existing.permissions.allow = [];
183986
184127
  }
183987
184128
  existing.permissions.allow.push(...missingPermissions);
183988
- fs10.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + "\n");
184129
+ fs11.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + "\n");
183989
184130
  return "modified";
183990
184131
  }
183991
- if (!fs10.existsSync(claudeDir)) {
183992
- fs10.mkdirSync(claudeDir);
184132
+ if (!fs11.existsSync(claudeDir)) {
184133
+ fs11.mkdirSync(claudeDir);
183993
184134
  }
183994
184135
  const settings = {
183995
184136
  permissions: {
183996
184137
  allow: permissions
183997
184138
  }
183998
184139
  };
183999
- fs10.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
184140
+ fs11.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
184000
184141
  return "created";
184001
184142
  }
184002
184143
  function createCursorRule() {
184003
- const cursorDir = path6.join(process.cwd(), ".cursor");
184004
- const rulesDir = path6.join(cursorDir, "rules");
184005
- const mdcPath = path6.join(rulesDir, "specific.mdc");
184006
- if (fs10.existsSync(mdcPath)) {
184007
- const existing = fs10.readFileSync(mdcPath, "utf-8");
184144
+ const cursorDir = path7.join(process.cwd(), ".cursor");
184145
+ const rulesDir = path7.join(cursorDir, "rules");
184146
+ const mdcPath = path7.join(rulesDir, "specific.mdc");
184147
+ if (fs11.existsSync(mdcPath)) {
184148
+ const existing = fs11.readFileSync(mdcPath, "utf-8");
184008
184149
  if (existing.includes("specific docs") || existing.includes("specific check")) {
184009
184150
  return "unchanged";
184010
184151
  }
184011
- fs10.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
184152
+ fs11.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
184012
184153
  return "modified";
184013
184154
  }
184014
- if (!fs10.existsSync(cursorDir)) {
184015
- fs10.mkdirSync(cursorDir);
184155
+ if (!fs11.existsSync(cursorDir)) {
184156
+ fs11.mkdirSync(cursorDir);
184016
184157
  }
184017
- if (!fs10.existsSync(rulesDir)) {
184018
- fs10.mkdirSync(rulesDir);
184158
+ if (!fs11.existsSync(rulesDir)) {
184159
+ fs11.mkdirSync(rulesDir);
184019
184160
  }
184020
- fs10.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
184161
+ fs11.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
184021
184162
  return "created";
184022
184163
  }
184023
184164
  function configureAgents(checked) {
@@ -184033,7 +184174,7 @@ function configureAgents(checked) {
184033
184174
  agents.filesModified.push(".cursor/rules/specific.mdc");
184034
184175
  }
184035
184176
  if (checked["claude"]) {
184036
- const claudeMdPath = path6.join(process.cwd(), "CLAUDE.md");
184177
+ const claudeMdPath = path7.join(process.cwd(), "CLAUDE.md");
184037
184178
  const status = appendOrCreateFile(claudeMdPath, SPECIFIC_INSTRUCTIONS);
184038
184179
  if (status === "created") agents.filesCreated.push("CLAUDE.md");
184039
184180
  else if (status === "modified") agents.filesModified.push("CLAUDE.md");
@@ -184044,7 +184185,7 @@ function configureAgents(checked) {
184044
184185
  agents.filesModified.push(".claude/settings.local.json");
184045
184186
  }
184046
184187
  if (checked["codex"]) {
184047
- const agentsMdPath = path6.join(process.cwd(), "AGENTS.md");
184188
+ const agentsMdPath = path7.join(process.cwd(), "AGENTS.md");
184048
184189
  const status = appendOrCreateFile(agentsMdPath, SPECIFIC_INSTRUCTIONS);
184049
184190
  if (status === "created") agents.filesCreated.push("AGENTS.md");
184050
184191
  else if (status === "modified") agents.filesModified.push("AGENTS.md");
@@ -184070,16 +184211,16 @@ function InitUI() {
184070
184211
  };
184071
184212
  });
184072
184213
  const [phase, setPhase] = useState(
184073
- () => caFilesExist() ? "agents" : "installing-ca"
184214
+ () => !systemSetupNeeded() ? "agents" : "installing-ca"
184074
184215
  );
184075
- const [caInstallPhase, setCaInstallPhase] = useState(() => caFilesExist() ? "done" : "installing");
184216
+ const [caInstallPhase, setCaInstallPhase] = useState(() => !systemSetupNeeded() ? "done" : "installing");
184076
184217
  const [focusedIndex, setFocusedIndex] = useState(initialState.focusedIndex);
184077
184218
  const [checked, setChecked] = useState(
184078
184219
  initialState.detected
184079
184220
  );
184080
184221
  const [result, setResult] = useState(null);
184081
184222
  const [tlsResult, setTlsResult] = useState(
184082
- caFilesExist() ? { status: "success" } : null
184223
+ !systemSetupNeeded() ? { status: "success" } : null
184083
184224
  );
184084
184225
  useEffect(() => {
184085
184226
  if (phase === "installing-ca" && caInstallPhase === "installing") {
@@ -184088,14 +184229,11 @@ function InitUI() {
184088
184229
  }, [phase, caInstallPhase]);
184089
184230
  async function installCA() {
184090
184231
  try {
184091
- const { key, cert } = generateRootCA();
184092
- const certPath = saveCA(key, cert);
184093
- installCAToTrustStore(certPath);
184232
+ performSystemSetup();
184094
184233
  setTlsResult({ status: "success" });
184095
184234
  setCaInstallPhase("done");
184096
184235
  setPhase("agents");
184097
184236
  } catch (err) {
184098
- removeCA();
184099
184237
  setTlsResult({
184100
184238
  status: "error",
184101
184239
  error: err instanceof Error ? err.message : String(err)
@@ -184136,16 +184274,16 @@ function InitUI() {
184136
184274
  }, [phase, exit]);
184137
184275
  if (phase === "installing-ca") {
184138
184276
  if (caInstallPhase === "installing") {
184139
- return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "TLS Certificate Setup"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "Installing a local certificate authority (CA) to enable HTTPS"), /* @__PURE__ */ React2.createElement(Text2, null, "for local development. The CA is limited to Specific projects."), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "Your password is required to add the CA to your system's trust store."), /* @__PURE__ */ React2.createElement(Text2, null, " "));
184277
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Local Development Setup"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "Setting up TLS certificates and DNS resolution for local"), /* @__PURE__ */ React2.createElement(Text2, null, "development. This is a one-time setup for Specific projects."), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "Your password is required to configure your system."), /* @__PURE__ */ React2.createElement(Text2, null, " "));
184140
184278
  }
184141
184279
  }
184142
184280
  if (phase === "done") {
184143
184281
  const selectedAgents = Object.entries(checked).filter(([, v]) => v).length;
184144
184282
  const agentChanges = result && (result.agents.filesCreated.length > 0 || result.agents.filesModified.length > 0);
184145
184283
  const gitChanges = result?.git && (result.git.filesCreated.length > 0 || result.git.filesModified.length > 0);
184146
- return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, tlsResult === null && /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u25CF"), " Installing TLS certificates..."), tlsResult?.status === "success" && /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " TLS certificates installed"), tlsResult?.status === "error" && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "red" }, "\u2717"), " Failed to install TLS certificates"), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", tlsResult.error)), 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.")));
184284
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, tlsResult === null && /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u25CF"), " Setting up TLS and DNS..."), tlsResult?.status === "success" && /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " TLS and DNS configured"), tlsResult?.status === "error" && /* @__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"), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", tlsResult.error)), 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.")));
184147
184285
  }
184148
- const tlsLine = tlsResult?.status === "success" ? /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " TLS certificates installed") : tlsResult?.status === "error" ? /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "red" }, "\u2717"), " TLS certificates failed", " ", /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "(", tlsResult.error, ")")) : null;
184286
+ const tlsLine = tlsResult?.status === "success" ? /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " TLS and DNS configured") : tlsResult?.status === "error" ? /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "red" }, "\u2717"), " TLS and DNS setup failed", " ", /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "(", tlsResult.error, ")")) : null;
184149
184287
  return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, tlsLine, /* @__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) => {
184150
184288
  const isFocused = focusedIndex === index;
184151
184289
  const isChecked = checked[option.id];
@@ -184216,9 +184354,9 @@ Valid agents: ${VALID_AGENT_IDS.join(", ")}`
184216
184354
  console.log(" No changes needed (.gitignore already configured)");
184217
184355
  }
184218
184356
  }
184219
- if (!caFilesExist()) {
184357
+ if (systemSetupNeeded()) {
184220
184358
  console.log(
184221
- "\u26A0 TLS certificates not installed (run `specific init` in a terminal to set up)"
184359
+ "\u26A0 TLS and DNS not configured (run `specific init` in a terminal to set up)"
184222
184360
  );
184223
184361
  }
184224
184362
  if (result.showManualInstructions) {
@@ -184235,8 +184373,8 @@ Valid agents: ${VALID_AGENT_IDS.join(", ")}`
184235
184373
  }
184236
184374
 
184237
184375
  // src/commands/docs.tsx
184238
- import { readFileSync as readFileSync6, existsSync as existsSync6 } from "fs";
184239
- import { join as join7, dirname as dirname2 } from "path";
184376
+ import { readFileSync as readFileSync6, existsSync as existsSync7 } from "fs";
184377
+ import { join as join8, dirname as dirname2 } from "path";
184240
184378
  import { fileURLToPath as fileURLToPath2 } from "url";
184241
184379
 
184242
184380
  // src/lib/beta/registry.ts
@@ -184248,12 +184386,12 @@ var BETA_REGISTRY = [
184248
184386
  ];
184249
184387
 
184250
184388
  // src/lib/beta/storage.ts
184251
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync5, mkdirSync as mkdirSync6 } from "fs";
184252
- import { join as join6 } from "path";
184389
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync6 } from "fs";
184390
+ import { join as join7 } from "path";
184253
184391
  var BETAS_FILE = ".specific/betas.json";
184254
184392
  function loadEnabledBetas(projectDir = process.cwd()) {
184255
- const filePath = join6(projectDir, BETAS_FILE);
184256
- if (!existsSync5(filePath)) {
184393
+ const filePath = join7(projectDir, BETAS_FILE);
184394
+ if (!existsSync6(filePath)) {
184257
184395
  return [];
184258
184396
  }
184259
184397
  try {
@@ -184276,25 +184414,25 @@ function disableBeta(name, projectDir = process.cwd()) {
184276
184414
  saveBetas(enabled, projectDir);
184277
184415
  }
184278
184416
  function saveBetas(enabled, projectDir) {
184279
- const specificDir = join6(projectDir, ".specific");
184280
- if (!existsSync5(specificDir)) {
184417
+ const specificDir = join7(projectDir, ".specific");
184418
+ if (!existsSync6(specificDir)) {
184281
184419
  mkdirSync6(specificDir, { recursive: true });
184282
184420
  }
184283
184421
  const data = { enabled };
184284
184422
  writeFileSync5(
184285
- join6(projectDir, BETAS_FILE),
184423
+ join7(projectDir, BETAS_FILE),
184286
184424
  JSON.stringify(data, null, 2) + "\n"
184287
184425
  );
184288
184426
  }
184289
184427
 
184290
184428
  // src/commands/docs.tsx
184291
184429
  var __dirname2 = dirname2(fileURLToPath2(import.meta.url));
184292
- var docsDir = join7(__dirname2, "docs");
184293
- function docsCommand(path26) {
184294
- const docPath = resolveDocPath(path26);
184430
+ var docsDir = join8(__dirname2, "docs");
184431
+ function docsCommand(path27) {
184432
+ const docPath = resolveDocPath(path27);
184295
184433
  if (!docPath) {
184296
184434
  console.error(
184297
- `Documentation not found: ${path26 || "index"}
184435
+ `Documentation not found: ${path27 || "index"}
184298
184436
 
184299
184437
  Run 'specific docs' to see available topics.`
184300
184438
  );
@@ -184313,17 +184451,17 @@ function filterBetaTags(content, enabledBetas) {
184313
184451
  }
184314
184452
  );
184315
184453
  }
184316
- function resolveDocPath(path26) {
184317
- if (!path26) {
184318
- const indexPath2 = join7(docsDir, "index.md");
184319
- return existsSync6(indexPath2) ? indexPath2 : null;
184454
+ function resolveDocPath(path27) {
184455
+ if (!path27) {
184456
+ const indexPath2 = join8(docsDir, "index.md");
184457
+ return existsSync7(indexPath2) ? indexPath2 : null;
184320
184458
  }
184321
- const directPath = join7(docsDir, `${path26}.md`);
184322
- if (existsSync6(directPath)) {
184459
+ const directPath = join8(docsDir, `${path27}.md`);
184460
+ if (existsSync7(directPath)) {
184323
184461
  return directPath;
184324
184462
  }
184325
- const indexPath = join7(docsDir, path26, "index.md");
184326
- if (existsSync6(indexPath)) {
184463
+ const indexPath = join8(docsDir, path27, "index.md");
184464
+ if (existsSync7(indexPath)) {
184327
184465
  return indexPath;
184328
184466
  }
184329
184467
  return null;
@@ -184333,8 +184471,8 @@ function resolveDocPath(path26) {
184333
184471
  import React3, { useState as useState2, useEffect as useEffect2 } from "react";
184334
184472
  import { render as render3, Text as Text3, Box as Box3 } from "ink";
184335
184473
  import Spinner3 from "ink-spinner";
184336
- import * as fs12 from "fs";
184337
- import * as path8 from "path";
184474
+ import * as fs13 from "fs";
184475
+ import * as path9 from "path";
184338
184476
  import { execFile as execFile7 } from "child_process";
184339
184477
 
184340
184478
  // node_modules/.pnpm/@specific+config@file+..+config/node_modules/@specific/config/dist/parser.js
@@ -185011,32 +185149,32 @@ var ExtractionError = class extends Error {
185011
185149
  };
185012
185150
 
185013
185151
  // src/lib/bin/manager.ts
185014
- import * as fs11 from "fs";
185015
- import * as path7 from "path";
185016
- import * as os6 from "os";
185152
+ import * as fs12 from "fs";
185153
+ import * as path8 from "path";
185154
+ import * as os7 from "os";
185017
185155
  import { createReadStream } from "fs";
185018
185156
  import { createTarExtractor, extractTo } from "tar-vern";
185019
185157
  function getLibraryEnv(binary) {
185020
185158
  if (!binary.libraryPath) {
185021
185159
  return {};
185022
185160
  }
185023
- const platform4 = os6.platform();
185024
- if (platform4 === "darwin") {
185161
+ const platform5 = os7.platform();
185162
+ if (platform5 === "darwin") {
185025
185163
  return { DYLD_LIBRARY_PATH: binary.libraryPath };
185026
- } else if (platform4 === "linux") {
185164
+ } else if (platform5 === "linux") {
185027
185165
  return { LD_LIBRARY_PATH: binary.libraryPath };
185028
185166
  }
185029
185167
  return {};
185030
185168
  }
185031
185169
  function getBinBaseDir() {
185032
- return path7.join(os6.homedir(), ".specific", "bin");
185170
+ return path8.join(os7.homedir(), ".specific", "bin");
185033
185171
  }
185034
185172
  function getPlatformInfo() {
185035
- const platform4 = os6.platform();
185036
- const arch3 = os6.arch();
185037
- if (platform4 !== "darwin" && platform4 !== "linux") {
185173
+ const platform5 = os7.platform();
185174
+ const arch3 = os7.arch();
185175
+ if (platform5 !== "darwin" && platform5 !== "linux") {
185038
185176
  throw new Error(
185039
- `Unsupported platform: ${platform4}. Only macOS and Linux are supported.`
185177
+ `Unsupported platform: ${platform5}. Only macOS and Linux are supported.`
185040
185178
  );
185041
185179
  }
185042
185180
  const archStr = arch3;
@@ -185050,10 +185188,10 @@ function getPlatformInfo() {
185050
185188
  `Unsupported architecture: ${arch3}. Only x64 and arm64 are supported.`
185051
185189
  );
185052
185190
  }
185053
- return { platform: platform4, arch: mappedArch };
185191
+ return { platform: platform5, arch: mappedArch };
185054
185192
  }
185055
185193
  function getBinaryDir(definition, version, platformInfo) {
185056
- return path7.join(
185194
+ return path8.join(
185057
185195
  getBinBaseDir(),
185058
185196
  definition.name,
185059
185197
  version,
@@ -185063,8 +185201,8 @@ function getBinaryDir(definition, version, platformInfo) {
185063
185201
  function isBinaryInstalled(definition, version, platformInfo) {
185064
185202
  const binDir = getBinaryDir(definition, version, platformInfo);
185065
185203
  for (const execPath of definition.executables) {
185066
- const fullPath = path7.join(binDir, execPath);
185067
- if (!fs11.existsSync(fullPath)) {
185204
+ const fullPath = path8.join(binDir, execPath);
185205
+ if (!fs12.existsSync(fullPath)) {
185068
185206
  return false;
185069
185207
  }
185070
185208
  }
@@ -185092,12 +185230,12 @@ async function downloadFile(url, destPath, onProgress) {
185092
185230
  10
185093
185231
  );
185094
185232
  let bytesDownloaded = 0;
185095
- const parentDir = path7.dirname(destPath);
185096
- if (!fs11.existsSync(parentDir)) {
185097
- fs11.mkdirSync(parentDir, { recursive: true });
185233
+ const parentDir = path8.dirname(destPath);
185234
+ if (!fs12.existsSync(parentDir)) {
185235
+ fs12.mkdirSync(parentDir, { recursive: true });
185098
185236
  }
185099
185237
  const partPath = destPath + ".part";
185100
- const fileStream = fs11.createWriteStream(partPath);
185238
+ const fileStream = fs12.createWriteStream(partPath);
185101
185239
  try {
185102
185240
  const reader = response.body.getReader();
185103
185241
  while (true) {
@@ -185120,12 +185258,12 @@ async function downloadFile(url, destPath, onProgress) {
185120
185258
  else resolve10();
185121
185259
  });
185122
185260
  });
185123
- fs11.renameSync(partPath, destPath);
185261
+ fs12.renameSync(partPath, destPath);
185124
185262
  } catch (error) {
185125
185263
  try {
185126
185264
  fileStream.close();
185127
- if (fs11.existsSync(partPath)) {
185128
- fs11.unlinkSync(partPath);
185265
+ if (fs12.existsSync(partPath)) {
185266
+ fs12.unlinkSync(partPath);
185129
185267
  }
185130
185268
  } catch {
185131
185269
  }
@@ -185134,8 +185272,8 @@ async function downloadFile(url, destPath, onProgress) {
185134
185272
  }
185135
185273
  async function extractTarball(archivePath, destDir, definition, onProgress) {
185136
185274
  onProgress?.({ phase: "extracting" });
185137
- if (!fs11.existsSync(destDir)) {
185138
- fs11.mkdirSync(destDir, { recursive: true });
185275
+ if (!fs12.existsSync(destDir)) {
185276
+ fs12.mkdirSync(destDir, { recursive: true });
185139
185277
  }
185140
185278
  try {
185141
185279
  const fileStream = createReadStream(archivePath);
@@ -185143,9 +185281,9 @@ async function extractTarball(archivePath, destDir, definition, onProgress) {
185143
185281
  await extractTo(entries, destDir);
185144
185282
  onProgress?.({ phase: "finalizing" });
185145
185283
  for (const execPath of definition.executables) {
185146
- const fullPath = path7.join(destDir, execPath);
185147
- if (fs11.existsSync(fullPath)) {
185148
- fs11.chmodSync(fullPath, 493);
185284
+ const fullPath = path8.join(destDir, execPath);
185285
+ if (fs12.existsSync(fullPath)) {
185286
+ fs12.chmodSync(fullPath, 493);
185149
185287
  }
185150
185288
  }
185151
185289
  } catch (error) {
@@ -185169,24 +185307,24 @@ async function ensureBinary(definition, version, onProgress) {
185169
185307
  `Binary type definitions must have exactly one executable, got ${definition.executables.length}`
185170
185308
  );
185171
185309
  }
185172
- if (!fs11.existsSync(binDir)) {
185173
- fs11.mkdirSync(binDir, { recursive: true });
185310
+ if (!fs12.existsSync(binDir)) {
185311
+ fs12.mkdirSync(binDir, { recursive: true });
185174
185312
  }
185175
- const execPath = path7.join(binDir, definition.executables[0]);
185313
+ const execPath = path8.join(binDir, definition.executables[0]);
185176
185314
  await downloadFile(url, execPath, onProgress);
185177
- fs11.chmodSync(execPath, 493);
185315
+ fs12.chmodSync(execPath, 493);
185178
185316
  onProgress?.({ phase: "finalizing" });
185179
185317
  } else {
185180
- const downloadDir = path7.join(getBinBaseDir(), "downloads");
185318
+ const downloadDir = path8.join(getBinBaseDir(), "downloads");
185181
185319
  const archiveName = `${definition.name}-${resolvedVersion}-${platformInfo.platform}-${platformInfo.arch}.tar.gz`;
185182
- const archivePath = path7.join(downloadDir, archiveName);
185320
+ const archivePath = path8.join(downloadDir, archiveName);
185183
185321
  try {
185184
185322
  await downloadFile(url, archivePath, onProgress);
185185
185323
  await extractTarball(archivePath, binDir, definition, onProgress);
185186
185324
  } finally {
185187
185325
  try {
185188
- if (fs11.existsSync(archivePath)) {
185189
- fs11.unlinkSync(archivePath);
185326
+ if (fs12.existsSync(archivePath)) {
185327
+ fs12.unlinkSync(archivePath);
185190
185328
  }
185191
185329
  } catch {
185192
185330
  }
@@ -185195,10 +185333,10 @@ async function ensureBinary(definition, version, onProgress) {
185195
185333
  }
185196
185334
  const executables = {};
185197
185335
  for (const execPath of definition.executables) {
185198
- const name = path7.basename(execPath);
185199
- executables[name] = path7.join(binDir, execPath);
185336
+ const name = path8.basename(execPath);
185337
+ executables[name] = path8.join(binDir, execPath);
185200
185338
  }
185201
- const libraryPath = definition.libraryDir ? path7.join(binDir, definition.libraryDir) : void 0;
185339
+ const libraryPath = definition.libraryDir ? path8.join(binDir, definition.libraryDir) : void 0;
185202
185340
  return {
185203
185341
  rootDir: binDir,
185204
185342
  version: resolvedVersion,
@@ -185342,8 +185480,8 @@ function CheckUI() {
185342
185480
  const [state, setState] = useState2({ status: "loading" });
185343
185481
  useEffect2(() => {
185344
185482
  async function load() {
185345
- const configPath = path8.join(process.cwd(), "specific.hcl");
185346
- if (!fs12.existsSync(configPath)) {
185483
+ const configPath = path9.join(process.cwd(), "specific.hcl");
185484
+ if (!fs13.existsSync(configPath)) {
185347
185485
  setState({
185348
185486
  status: "error",
185349
185487
  error: "No specific.hcl found in current directory"
@@ -185351,16 +185489,16 @@ function CheckUI() {
185351
185489
  return;
185352
185490
  }
185353
185491
  try {
185354
- const hcl = fs12.readFileSync(configPath, "utf-8");
185492
+ const hcl = fs13.readFileSync(configPath, "utf-8");
185355
185493
  const config2 = await parseConfig(hcl);
185356
185494
  const reshapeChecks2 = [];
185357
185495
  for (const pg of config2.postgres) {
185358
185496
  if (pg.reshape?.enabled) {
185359
- const migrationsDir = path8.resolve(
185497
+ const migrationsDir = path9.resolve(
185360
185498
  process.cwd(),
185361
185499
  pg.reshape.migrations_dir ?? "migrations"
185362
185500
  );
185363
- if (!fs12.existsSync(migrationsDir)) {
185501
+ if (!fs13.existsSync(migrationsDir)) {
185364
185502
  reshapeChecks2.push({
185365
185503
  databaseName: pg.name,
185366
185504
  migrationsDir: pg.reshape.migrations_dir ?? "migrations",
@@ -185413,8 +185551,8 @@ function checkCommand() {
185413
185551
  import React6, { useState as useState5, useEffect as useEffect3, useRef } from "react";
185414
185552
  import { render as render4, Text as Text6, Box as Box6, useApp as useApp2, Static } from "ink";
185415
185553
  import Spinner4 from "ink-spinner";
185416
- import * as fs21 from "fs";
185417
- import * as path18 from "path";
185554
+ import * as fs22 from "fs";
185555
+ import * as path19 from "path";
185418
185556
 
185419
185557
  // node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
185420
185558
  import { EventEmitter } from "node:events";
@@ -185506,7 +185644,7 @@ var ReaddirpStream = class extends Readable {
185506
185644
  this._directoryFilter = normalizeFilter(opts.directoryFilter);
185507
185645
  const statMethod = opts.lstat ? lstat : stat;
185508
185646
  if (wantBigintFsStats) {
185509
- this._stat = (path26) => statMethod(path26, { bigint: true });
185647
+ this._stat = (path27) => statMethod(path27, { bigint: true });
185510
185648
  } else {
185511
185649
  this._stat = statMethod;
185512
185650
  }
@@ -185531,8 +185669,8 @@ var ReaddirpStream = class extends Readable {
185531
185669
  const par = this.parent;
185532
185670
  const fil = par && par.files;
185533
185671
  if (fil && fil.length > 0) {
185534
- const { path: path26, depth } = par;
185535
- const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path26));
185672
+ const { path: path27, depth } = par;
185673
+ const slice = fil.splice(0, batch).map((dirent) => this._formatEntry(dirent, path27));
185536
185674
  const awaited = await Promise.all(slice);
185537
185675
  for (const entry of awaited) {
185538
185676
  if (!entry)
@@ -185572,20 +185710,20 @@ var ReaddirpStream = class extends Readable {
185572
185710
  this.reading = false;
185573
185711
  }
185574
185712
  }
185575
- async _exploreDir(path26, depth) {
185713
+ async _exploreDir(path27, depth) {
185576
185714
  let files;
185577
185715
  try {
185578
- files = await readdir(path26, this._rdOptions);
185716
+ files = await readdir(path27, this._rdOptions);
185579
185717
  } catch (error) {
185580
185718
  this._onError(error);
185581
185719
  }
185582
- return { files, depth, path: path26 };
185720
+ return { files, depth, path: path27 };
185583
185721
  }
185584
- async _formatEntry(dirent, path26) {
185722
+ async _formatEntry(dirent, path27) {
185585
185723
  let entry;
185586
185724
  const basename6 = this._isDirent ? dirent.name : dirent;
185587
185725
  try {
185588
- const fullPath = presolve(pjoin(path26, basename6));
185726
+ const fullPath = presolve(pjoin(path27, basename6));
185589
185727
  entry = { path: prelative(this._root, fullPath), fullPath, basename: basename6 };
185590
185728
  entry[this._statsProp] = this._isDirent ? dirent : await this._stat(fullPath);
185591
185729
  } catch (err) {
@@ -185985,16 +186123,16 @@ var delFromSet = (main, prop, item) => {
185985
186123
  };
185986
186124
  var isEmptySet = (val) => val instanceof Set ? val.size === 0 : !val;
185987
186125
  var FsWatchInstances = /* @__PURE__ */ new Map();
185988
- function createFsWatchInstance(path26, options2, listener, errHandler, emitRaw) {
186126
+ function createFsWatchInstance(path27, options2, listener, errHandler, emitRaw) {
185989
186127
  const handleEvent = (rawEvent, evPath) => {
185990
- listener(path26);
185991
- emitRaw(rawEvent, evPath, { watchedPath: path26 });
185992
- if (evPath && path26 !== evPath) {
185993
- fsWatchBroadcast(sp.resolve(path26, evPath), KEY_LISTENERS, sp.join(path26, evPath));
186128
+ listener(path27);
186129
+ emitRaw(rawEvent, evPath, { watchedPath: path27 });
186130
+ if (evPath && path27 !== evPath) {
186131
+ fsWatchBroadcast(sp.resolve(path27, evPath), KEY_LISTENERS, sp.join(path27, evPath));
185994
186132
  }
185995
186133
  };
185996
186134
  try {
185997
- return fs_watch(path26, {
186135
+ return fs_watch(path27, {
185998
186136
  persistent: options2.persistent
185999
186137
  }, handleEvent);
186000
186138
  } catch (error) {
@@ -186010,12 +186148,12 @@ var fsWatchBroadcast = (fullPath, listenerType, val1, val2, val3) => {
186010
186148
  listener(val1, val2, val3);
186011
186149
  });
186012
186150
  };
186013
- var setFsWatchListener = (path26, fullPath, options2, handlers) => {
186151
+ var setFsWatchListener = (path27, fullPath, options2, handlers) => {
186014
186152
  const { listener, errHandler, rawEmitter } = handlers;
186015
186153
  let cont = FsWatchInstances.get(fullPath);
186016
186154
  let watcher;
186017
186155
  if (!options2.persistent) {
186018
- watcher = createFsWatchInstance(path26, options2, listener, errHandler, rawEmitter);
186156
+ watcher = createFsWatchInstance(path27, options2, listener, errHandler, rawEmitter);
186019
186157
  if (!watcher)
186020
186158
  return;
186021
186159
  return watcher.close.bind(watcher);
@@ -186026,7 +186164,7 @@ var setFsWatchListener = (path26, fullPath, options2, handlers) => {
186026
186164
  addAndConvert(cont, KEY_RAW, rawEmitter);
186027
186165
  } else {
186028
186166
  watcher = createFsWatchInstance(
186029
- path26,
186167
+ path27,
186030
186168
  options2,
186031
186169
  fsWatchBroadcast.bind(null, fullPath, KEY_LISTENERS),
186032
186170
  errHandler,
@@ -186041,7 +186179,7 @@ var setFsWatchListener = (path26, fullPath, options2, handlers) => {
186041
186179
  cont.watcherUnusable = true;
186042
186180
  if (isWindows && error.code === "EPERM") {
186043
186181
  try {
186044
- const fd = await open2(path26, "r");
186182
+ const fd = await open2(path27, "r");
186045
186183
  await fd.close();
186046
186184
  broadcastErr(error);
186047
186185
  } catch (err) {
@@ -186072,7 +186210,7 @@ var setFsWatchListener = (path26, fullPath, options2, handlers) => {
186072
186210
  };
186073
186211
  };
186074
186212
  var FsWatchFileInstances = /* @__PURE__ */ new Map();
186075
- var setFsWatchFileListener = (path26, fullPath, options2, handlers) => {
186213
+ var setFsWatchFileListener = (path27, fullPath, options2, handlers) => {
186076
186214
  const { listener, rawEmitter } = handlers;
186077
186215
  let cont = FsWatchFileInstances.get(fullPath);
186078
186216
  const copts = cont && cont.options;
@@ -186094,7 +186232,7 @@ var setFsWatchFileListener = (path26, fullPath, options2, handlers) => {
186094
186232
  });
186095
186233
  const currmtime = curr.mtimeMs;
186096
186234
  if (curr.size !== prev.size || currmtime > prev.mtimeMs || currmtime === 0) {
186097
- foreach(cont.listeners, (listener2) => listener2(path26, curr));
186235
+ foreach(cont.listeners, (listener2) => listener2(path27, curr));
186098
186236
  }
186099
186237
  })
186100
186238
  };
@@ -186124,13 +186262,13 @@ var NodeFsHandler = class {
186124
186262
  * @param listener on fs change
186125
186263
  * @returns closer for the watcher instance
186126
186264
  */
186127
- _watchWithNodeFs(path26, listener) {
186265
+ _watchWithNodeFs(path27, listener) {
186128
186266
  const opts = this.fsw.options;
186129
- const directory = sp.dirname(path26);
186130
- const basename6 = sp.basename(path26);
186267
+ const directory = sp.dirname(path27);
186268
+ const basename6 = sp.basename(path27);
186131
186269
  const parent = this.fsw._getWatchedDir(directory);
186132
186270
  parent.add(basename6);
186133
- const absolutePath = sp.resolve(path26);
186271
+ const absolutePath = sp.resolve(path27);
186134
186272
  const options2 = {
186135
186273
  persistent: opts.persistent
186136
186274
  };
@@ -186140,12 +186278,12 @@ var NodeFsHandler = class {
186140
186278
  if (opts.usePolling) {
186141
186279
  const enableBin = opts.interval !== opts.binaryInterval;
186142
186280
  options2.interval = enableBin && isBinaryPath(basename6) ? opts.binaryInterval : opts.interval;
186143
- closer = setFsWatchFileListener(path26, absolutePath, options2, {
186281
+ closer = setFsWatchFileListener(path27, absolutePath, options2, {
186144
186282
  listener,
186145
186283
  rawEmitter: this.fsw._emitRaw
186146
186284
  });
186147
186285
  } else {
186148
- closer = setFsWatchListener(path26, absolutePath, options2, {
186286
+ closer = setFsWatchListener(path27, absolutePath, options2, {
186149
186287
  listener,
186150
186288
  errHandler: this._boundHandleError,
186151
186289
  rawEmitter: this.fsw._emitRaw
@@ -186167,7 +186305,7 @@ var NodeFsHandler = class {
186167
186305
  let prevStats = stats;
186168
186306
  if (parent.has(basename6))
186169
186307
  return;
186170
- const listener = async (path26, newStats) => {
186308
+ const listener = async (path27, newStats) => {
186171
186309
  if (!this.fsw._throttle(THROTTLE_MODE_WATCH, file, 5))
186172
186310
  return;
186173
186311
  if (!newStats || newStats.mtimeMs === 0) {
@@ -186181,11 +186319,11 @@ var NodeFsHandler = class {
186181
186319
  this.fsw._emit(EV.CHANGE, file, newStats2);
186182
186320
  }
186183
186321
  if ((isMacos || isLinux || isFreeBSD) && prevStats.ino !== newStats2.ino) {
186184
- this.fsw._closeFile(path26);
186322
+ this.fsw._closeFile(path27);
186185
186323
  prevStats = newStats2;
186186
186324
  const closer2 = this._watchWithNodeFs(file, listener);
186187
186325
  if (closer2)
186188
- this.fsw._addPathCloser(path26, closer2);
186326
+ this.fsw._addPathCloser(path27, closer2);
186189
186327
  } else {
186190
186328
  prevStats = newStats2;
186191
186329
  }
@@ -186217,7 +186355,7 @@ var NodeFsHandler = class {
186217
186355
  * @param item basename of this item
186218
186356
  * @returns true if no more processing is needed for this entry.
186219
186357
  */
186220
- async _handleSymlink(entry, directory, path26, item) {
186358
+ async _handleSymlink(entry, directory, path27, item) {
186221
186359
  if (this.fsw.closed) {
186222
186360
  return;
186223
186361
  }
@@ -186227,7 +186365,7 @@ var NodeFsHandler = class {
186227
186365
  this.fsw._incrReadyCount();
186228
186366
  let linkPath;
186229
186367
  try {
186230
- linkPath = await fsrealpath(path26);
186368
+ linkPath = await fsrealpath(path27);
186231
186369
  } catch (e) {
186232
186370
  this.fsw._emitReady();
186233
186371
  return true;
@@ -186237,12 +186375,12 @@ var NodeFsHandler = class {
186237
186375
  if (dir.has(item)) {
186238
186376
  if (this.fsw._symlinkPaths.get(full) !== linkPath) {
186239
186377
  this.fsw._symlinkPaths.set(full, linkPath);
186240
- this.fsw._emit(EV.CHANGE, path26, entry.stats);
186378
+ this.fsw._emit(EV.CHANGE, path27, entry.stats);
186241
186379
  }
186242
186380
  } else {
186243
186381
  dir.add(item);
186244
186382
  this.fsw._symlinkPaths.set(full, linkPath);
186245
- this.fsw._emit(EV.ADD, path26, entry.stats);
186383
+ this.fsw._emit(EV.ADD, path27, entry.stats);
186246
186384
  }
186247
186385
  this.fsw._emitReady();
186248
186386
  return true;
@@ -186272,9 +186410,9 @@ var NodeFsHandler = class {
186272
186410
  return;
186273
186411
  }
186274
186412
  const item = entry.path;
186275
- let path26 = sp.join(directory, item);
186413
+ let path27 = sp.join(directory, item);
186276
186414
  current.add(item);
186277
- if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path26, item)) {
186415
+ if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path27, item)) {
186278
186416
  return;
186279
186417
  }
186280
186418
  if (this.fsw.closed) {
@@ -186283,8 +186421,8 @@ var NodeFsHandler = class {
186283
186421
  }
186284
186422
  if (item === target || !target && !previous.has(item)) {
186285
186423
  this.fsw._incrReadyCount();
186286
- path26 = sp.join(dir, sp.relative(dir, path26));
186287
- this._addToNodeFs(path26, initialAdd, wh, depth + 1);
186424
+ path27 = sp.join(dir, sp.relative(dir, path27));
186425
+ this._addToNodeFs(path27, initialAdd, wh, depth + 1);
186288
186426
  }
186289
186427
  }).on(EV.ERROR, this._boundHandleError);
186290
186428
  return new Promise((resolve10, reject) => {
@@ -186353,13 +186491,13 @@ var NodeFsHandler = class {
186353
186491
  * @param depth Child path actually targeted for watch
186354
186492
  * @param target Child path actually targeted for watch
186355
186493
  */
186356
- async _addToNodeFs(path26, initialAdd, priorWh, depth, target) {
186494
+ async _addToNodeFs(path27, initialAdd, priorWh, depth, target) {
186357
186495
  const ready = this.fsw._emitReady;
186358
- if (this.fsw._isIgnored(path26) || this.fsw.closed) {
186496
+ if (this.fsw._isIgnored(path27) || this.fsw.closed) {
186359
186497
  ready();
186360
186498
  return false;
186361
186499
  }
186362
- const wh = this.fsw._getWatchHelpers(path26);
186500
+ const wh = this.fsw._getWatchHelpers(path27);
186363
186501
  if (priorWh) {
186364
186502
  wh.filterPath = (entry) => priorWh.filterPath(entry);
186365
186503
  wh.filterDir = (entry) => priorWh.filterDir(entry);
@@ -186375,8 +186513,8 @@ var NodeFsHandler = class {
186375
186513
  const follow = this.fsw.options.followSymlinks;
186376
186514
  let closer;
186377
186515
  if (stats.isDirectory()) {
186378
- const absPath = sp.resolve(path26);
186379
- const targetPath = follow ? await fsrealpath(path26) : path26;
186516
+ const absPath = sp.resolve(path27);
186517
+ const targetPath = follow ? await fsrealpath(path27) : path27;
186380
186518
  if (this.fsw.closed)
186381
186519
  return;
186382
186520
  closer = await this._handleDir(wh.watchPath, stats, initialAdd, depth, target, wh, targetPath);
@@ -186386,29 +186524,29 @@ var NodeFsHandler = class {
186386
186524
  this.fsw._symlinkPaths.set(absPath, targetPath);
186387
186525
  }
186388
186526
  } else if (stats.isSymbolicLink()) {
186389
- const targetPath = follow ? await fsrealpath(path26) : path26;
186527
+ const targetPath = follow ? await fsrealpath(path27) : path27;
186390
186528
  if (this.fsw.closed)
186391
186529
  return;
186392
186530
  const parent = sp.dirname(wh.watchPath);
186393
186531
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
186394
186532
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
186395
- closer = await this._handleDir(parent, stats, initialAdd, depth, path26, wh, targetPath);
186533
+ closer = await this._handleDir(parent, stats, initialAdd, depth, path27, wh, targetPath);
186396
186534
  if (this.fsw.closed)
186397
186535
  return;
186398
186536
  if (targetPath !== void 0) {
186399
- this.fsw._symlinkPaths.set(sp.resolve(path26), targetPath);
186537
+ this.fsw._symlinkPaths.set(sp.resolve(path27), targetPath);
186400
186538
  }
186401
186539
  } else {
186402
186540
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
186403
186541
  }
186404
186542
  ready();
186405
186543
  if (closer)
186406
- this.fsw._addPathCloser(path26, closer);
186544
+ this.fsw._addPathCloser(path27, closer);
186407
186545
  return false;
186408
186546
  } catch (error) {
186409
186547
  if (this.fsw._handleError(error)) {
186410
186548
  ready();
186411
- return path26;
186549
+ return path27;
186412
186550
  }
186413
186551
  }
186414
186552
  }
@@ -186451,24 +186589,24 @@ function createPattern(matcher) {
186451
186589
  }
186452
186590
  return () => false;
186453
186591
  }
186454
- function normalizePath(path26) {
186455
- if (typeof path26 !== "string")
186592
+ function normalizePath(path27) {
186593
+ if (typeof path27 !== "string")
186456
186594
  throw new Error("string expected");
186457
- path26 = sp2.normalize(path26);
186458
- path26 = path26.replace(/\\/g, "/");
186595
+ path27 = sp2.normalize(path27);
186596
+ path27 = path27.replace(/\\/g, "/");
186459
186597
  let prepend = false;
186460
- if (path26.startsWith("//"))
186598
+ if (path27.startsWith("//"))
186461
186599
  prepend = true;
186462
- path26 = path26.replace(DOUBLE_SLASH_RE, "/");
186600
+ path27 = path27.replace(DOUBLE_SLASH_RE, "/");
186463
186601
  if (prepend)
186464
- path26 = "/" + path26;
186465
- return path26;
186602
+ path27 = "/" + path27;
186603
+ return path27;
186466
186604
  }
186467
186605
  function matchPatterns(patterns, testString, stats) {
186468
- const path26 = normalizePath(testString);
186606
+ const path27 = normalizePath(testString);
186469
186607
  for (let index = 0; index < patterns.length; index++) {
186470
186608
  const pattern = patterns[index];
186471
- if (pattern(path26, stats)) {
186609
+ if (pattern(path27, stats)) {
186472
186610
  return true;
186473
186611
  }
186474
186612
  }
@@ -186506,19 +186644,19 @@ var toUnix = (string) => {
186506
186644
  }
186507
186645
  return str;
186508
186646
  };
186509
- var normalizePathToUnix = (path26) => toUnix(sp2.normalize(toUnix(path26)));
186510
- var normalizeIgnored = (cwd = "") => (path26) => {
186511
- if (typeof path26 === "string") {
186512
- return normalizePathToUnix(sp2.isAbsolute(path26) ? path26 : sp2.join(cwd, path26));
186647
+ var normalizePathToUnix = (path27) => toUnix(sp2.normalize(toUnix(path27)));
186648
+ var normalizeIgnored = (cwd = "") => (path27) => {
186649
+ if (typeof path27 === "string") {
186650
+ return normalizePathToUnix(sp2.isAbsolute(path27) ? path27 : sp2.join(cwd, path27));
186513
186651
  } else {
186514
- return path26;
186652
+ return path27;
186515
186653
  }
186516
186654
  };
186517
- var getAbsolutePath = (path26, cwd) => {
186518
- if (sp2.isAbsolute(path26)) {
186519
- return path26;
186655
+ var getAbsolutePath = (path27, cwd) => {
186656
+ if (sp2.isAbsolute(path27)) {
186657
+ return path27;
186520
186658
  }
186521
- return sp2.join(cwd, path26);
186659
+ return sp2.join(cwd, path27);
186522
186660
  };
186523
186661
  var EMPTY_SET = Object.freeze(/* @__PURE__ */ new Set());
186524
186662
  var DirEntry = class {
@@ -186583,10 +186721,10 @@ var WatchHelper = class {
186583
186721
  dirParts;
186584
186722
  followSymlinks;
186585
186723
  statMethod;
186586
- constructor(path26, follow, fsw) {
186724
+ constructor(path27, follow, fsw) {
186587
186725
  this.fsw = fsw;
186588
- const watchPath = path26;
186589
- this.path = path26 = path26.replace(REPLACER_RE, "");
186726
+ const watchPath = path27;
186727
+ this.path = path27 = path27.replace(REPLACER_RE, "");
186590
186728
  this.watchPath = watchPath;
186591
186729
  this.fullWatchPath = sp2.resolve(watchPath);
186592
186730
  this.dirParts = [];
@@ -186726,20 +186864,20 @@ var FSWatcher = class extends EventEmitter {
186726
186864
  this._closePromise = void 0;
186727
186865
  let paths = unifyPaths(paths_);
186728
186866
  if (cwd) {
186729
- paths = paths.map((path26) => {
186730
- const absPath = getAbsolutePath(path26, cwd);
186867
+ paths = paths.map((path27) => {
186868
+ const absPath = getAbsolutePath(path27, cwd);
186731
186869
  return absPath;
186732
186870
  });
186733
186871
  }
186734
- paths.forEach((path26) => {
186735
- this._removeIgnoredPath(path26);
186872
+ paths.forEach((path27) => {
186873
+ this._removeIgnoredPath(path27);
186736
186874
  });
186737
186875
  this._userIgnored = void 0;
186738
186876
  if (!this._readyCount)
186739
186877
  this._readyCount = 0;
186740
186878
  this._readyCount += paths.length;
186741
- Promise.all(paths.map(async (path26) => {
186742
- const res = await this._nodeFsHandler._addToNodeFs(path26, !_internal, void 0, 0, _origAdd);
186879
+ Promise.all(paths.map(async (path27) => {
186880
+ const res = await this._nodeFsHandler._addToNodeFs(path27, !_internal, void 0, 0, _origAdd);
186743
186881
  if (res)
186744
186882
  this._emitReady();
186745
186883
  return res;
@@ -186761,17 +186899,17 @@ var FSWatcher = class extends EventEmitter {
186761
186899
  return this;
186762
186900
  const paths = unifyPaths(paths_);
186763
186901
  const { cwd } = this.options;
186764
- paths.forEach((path26) => {
186765
- if (!sp2.isAbsolute(path26) && !this._closers.has(path26)) {
186902
+ paths.forEach((path27) => {
186903
+ if (!sp2.isAbsolute(path27) && !this._closers.has(path27)) {
186766
186904
  if (cwd)
186767
- path26 = sp2.join(cwd, path26);
186768
- path26 = sp2.resolve(path26);
186905
+ path27 = sp2.join(cwd, path27);
186906
+ path27 = sp2.resolve(path27);
186769
186907
  }
186770
- this._closePath(path26);
186771
- this._addIgnoredPath(path26);
186772
- if (this._watched.has(path26)) {
186908
+ this._closePath(path27);
186909
+ this._addIgnoredPath(path27);
186910
+ if (this._watched.has(path27)) {
186773
186911
  this._addIgnoredPath({
186774
- path: path26,
186912
+ path: path27,
186775
186913
  recursive: true
186776
186914
  });
186777
186915
  }
@@ -186835,38 +186973,38 @@ var FSWatcher = class extends EventEmitter {
186835
186973
  * @param stats arguments to be passed with event
186836
186974
  * @returns the error if defined, otherwise the value of the FSWatcher instance's `closed` flag
186837
186975
  */
186838
- async _emit(event, path26, stats) {
186976
+ async _emit(event, path27, stats) {
186839
186977
  if (this.closed)
186840
186978
  return;
186841
186979
  const opts = this.options;
186842
186980
  if (isWindows)
186843
- path26 = sp2.normalize(path26);
186981
+ path27 = sp2.normalize(path27);
186844
186982
  if (opts.cwd)
186845
- path26 = sp2.relative(opts.cwd, path26);
186846
- const args = [path26];
186983
+ path27 = sp2.relative(opts.cwd, path27);
186984
+ const args = [path27];
186847
186985
  if (stats != null)
186848
186986
  args.push(stats);
186849
186987
  const awf = opts.awaitWriteFinish;
186850
186988
  let pw;
186851
- if (awf && (pw = this._pendingWrites.get(path26))) {
186989
+ if (awf && (pw = this._pendingWrites.get(path27))) {
186852
186990
  pw.lastChange = /* @__PURE__ */ new Date();
186853
186991
  return this;
186854
186992
  }
186855
186993
  if (opts.atomic) {
186856
186994
  if (event === EVENTS.UNLINK) {
186857
- this._pendingUnlinks.set(path26, [event, ...args]);
186995
+ this._pendingUnlinks.set(path27, [event, ...args]);
186858
186996
  setTimeout(() => {
186859
- this._pendingUnlinks.forEach((entry, path27) => {
186997
+ this._pendingUnlinks.forEach((entry, path28) => {
186860
186998
  this.emit(...entry);
186861
186999
  this.emit(EVENTS.ALL, ...entry);
186862
- this._pendingUnlinks.delete(path27);
187000
+ this._pendingUnlinks.delete(path28);
186863
187001
  });
186864
187002
  }, typeof opts.atomic === "number" ? opts.atomic : 100);
186865
187003
  return this;
186866
187004
  }
186867
- if (event === EVENTS.ADD && this._pendingUnlinks.has(path26)) {
187005
+ if (event === EVENTS.ADD && this._pendingUnlinks.has(path27)) {
186868
187006
  event = EVENTS.CHANGE;
186869
- this._pendingUnlinks.delete(path26);
187007
+ this._pendingUnlinks.delete(path27);
186870
187008
  }
186871
187009
  }
186872
187010
  if (awf && (event === EVENTS.ADD || event === EVENTS.CHANGE) && this._readyEmitted) {
@@ -186884,16 +187022,16 @@ var FSWatcher = class extends EventEmitter {
186884
187022
  this.emitWithAll(event, args);
186885
187023
  }
186886
187024
  };
186887
- this._awaitWriteFinish(path26, awf.stabilityThreshold, event, awfEmit);
187025
+ this._awaitWriteFinish(path27, awf.stabilityThreshold, event, awfEmit);
186888
187026
  return this;
186889
187027
  }
186890
187028
  if (event === EVENTS.CHANGE) {
186891
- const isThrottled = !this._throttle(EVENTS.CHANGE, path26, 50);
187029
+ const isThrottled = !this._throttle(EVENTS.CHANGE, path27, 50);
186892
187030
  if (isThrottled)
186893
187031
  return this;
186894
187032
  }
186895
187033
  if (opts.alwaysStat && stats === void 0 && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
186896
- const fullPath = opts.cwd ? sp2.join(opts.cwd, path26) : path26;
187034
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path27) : path27;
186897
187035
  let stats2;
186898
187036
  try {
186899
187037
  stats2 = await stat3(fullPath);
@@ -186924,23 +187062,23 @@ var FSWatcher = class extends EventEmitter {
186924
187062
  * @param timeout duration of time to suppress duplicate actions
186925
187063
  * @returns tracking object or false if action should be suppressed
186926
187064
  */
186927
- _throttle(actionType, path26, timeout) {
187065
+ _throttle(actionType, path27, timeout) {
186928
187066
  if (!this._throttled.has(actionType)) {
186929
187067
  this._throttled.set(actionType, /* @__PURE__ */ new Map());
186930
187068
  }
186931
187069
  const action = this._throttled.get(actionType);
186932
187070
  if (!action)
186933
187071
  throw new Error("invalid throttle");
186934
- const actionPath = action.get(path26);
187072
+ const actionPath = action.get(path27);
186935
187073
  if (actionPath) {
186936
187074
  actionPath.count++;
186937
187075
  return false;
186938
187076
  }
186939
187077
  let timeoutObject;
186940
187078
  const clear = () => {
186941
- const item = action.get(path26);
187079
+ const item = action.get(path27);
186942
187080
  const count = item ? item.count : 0;
186943
- action.delete(path26);
187081
+ action.delete(path27);
186944
187082
  clearTimeout(timeoutObject);
186945
187083
  if (item)
186946
187084
  clearTimeout(item.timeoutObject);
@@ -186948,7 +187086,7 @@ var FSWatcher = class extends EventEmitter {
186948
187086
  };
186949
187087
  timeoutObject = setTimeout(clear, timeout);
186950
187088
  const thr = { timeoutObject, clear, count: 0 };
186951
- action.set(path26, thr);
187089
+ action.set(path27, thr);
186952
187090
  return thr;
186953
187091
  }
186954
187092
  _incrReadyCount() {
@@ -186962,44 +187100,44 @@ var FSWatcher = class extends EventEmitter {
186962
187100
  * @param event
186963
187101
  * @param awfEmit Callback to be called when ready for event to be emitted.
186964
187102
  */
186965
- _awaitWriteFinish(path26, threshold, event, awfEmit) {
187103
+ _awaitWriteFinish(path27, threshold, event, awfEmit) {
186966
187104
  const awf = this.options.awaitWriteFinish;
186967
187105
  if (typeof awf !== "object")
186968
187106
  return;
186969
187107
  const pollInterval = awf.pollInterval;
186970
187108
  let timeoutHandler;
186971
- let fullPath = path26;
186972
- if (this.options.cwd && !sp2.isAbsolute(path26)) {
186973
- fullPath = sp2.join(this.options.cwd, path26);
187109
+ let fullPath = path27;
187110
+ if (this.options.cwd && !sp2.isAbsolute(path27)) {
187111
+ fullPath = sp2.join(this.options.cwd, path27);
186974
187112
  }
186975
187113
  const now = /* @__PURE__ */ new Date();
186976
187114
  const writes = this._pendingWrites;
186977
187115
  function awaitWriteFinishFn(prevStat) {
186978
187116
  statcb(fullPath, (err, curStat) => {
186979
- if (err || !writes.has(path26)) {
187117
+ if (err || !writes.has(path27)) {
186980
187118
  if (err && err.code !== "ENOENT")
186981
187119
  awfEmit(err);
186982
187120
  return;
186983
187121
  }
186984
187122
  const now2 = Number(/* @__PURE__ */ new Date());
186985
187123
  if (prevStat && curStat.size !== prevStat.size) {
186986
- writes.get(path26).lastChange = now2;
187124
+ writes.get(path27).lastChange = now2;
186987
187125
  }
186988
- const pw = writes.get(path26);
187126
+ const pw = writes.get(path27);
186989
187127
  const df = now2 - pw.lastChange;
186990
187128
  if (df >= threshold) {
186991
- writes.delete(path26);
187129
+ writes.delete(path27);
186992
187130
  awfEmit(void 0, curStat);
186993
187131
  } else {
186994
187132
  timeoutHandler = setTimeout(awaitWriteFinishFn, pollInterval, curStat);
186995
187133
  }
186996
187134
  });
186997
187135
  }
186998
- if (!writes.has(path26)) {
186999
- writes.set(path26, {
187136
+ if (!writes.has(path27)) {
187137
+ writes.set(path27, {
187000
187138
  lastChange: now,
187001
187139
  cancelWait: () => {
187002
- writes.delete(path26);
187140
+ writes.delete(path27);
187003
187141
  clearTimeout(timeoutHandler);
187004
187142
  return event;
187005
187143
  }
@@ -187010,8 +187148,8 @@ var FSWatcher = class extends EventEmitter {
187010
187148
  /**
187011
187149
  * Determines whether user has asked to ignore this path.
187012
187150
  */
187013
- _isIgnored(path26, stats) {
187014
- if (this.options.atomic && DOT_RE.test(path26))
187151
+ _isIgnored(path27, stats) {
187152
+ if (this.options.atomic && DOT_RE.test(path27))
187015
187153
  return true;
187016
187154
  if (!this._userIgnored) {
187017
187155
  const { cwd } = this.options;
@@ -187021,17 +187159,17 @@ var FSWatcher = class extends EventEmitter {
187021
187159
  const list = [...ignoredPaths.map(normalizeIgnored(cwd)), ...ignored];
187022
187160
  this._userIgnored = anymatch(list, void 0);
187023
187161
  }
187024
- return this._userIgnored(path26, stats);
187162
+ return this._userIgnored(path27, stats);
187025
187163
  }
187026
- _isntIgnored(path26, stat4) {
187027
- return !this._isIgnored(path26, stat4);
187164
+ _isntIgnored(path27, stat4) {
187165
+ return !this._isIgnored(path27, stat4);
187028
187166
  }
187029
187167
  /**
187030
187168
  * Provides a set of common helpers and properties relating to symlink handling.
187031
187169
  * @param path file or directory pattern being watched
187032
187170
  */
187033
- _getWatchHelpers(path26) {
187034
- return new WatchHelper(path26, this.options.followSymlinks, this);
187171
+ _getWatchHelpers(path27) {
187172
+ return new WatchHelper(path27, this.options.followSymlinks, this);
187035
187173
  }
187036
187174
  // Directory helpers
187037
187175
  // -----------------
@@ -187063,63 +187201,63 @@ var FSWatcher = class extends EventEmitter {
187063
187201
  * @param item base path of item/directory
187064
187202
  */
187065
187203
  _remove(directory, item, isDirectory) {
187066
- const path26 = sp2.join(directory, item);
187067
- const fullPath = sp2.resolve(path26);
187068
- isDirectory = isDirectory != null ? isDirectory : this._watched.has(path26) || this._watched.has(fullPath);
187069
- if (!this._throttle("remove", path26, 100))
187204
+ const path27 = sp2.join(directory, item);
187205
+ const fullPath = sp2.resolve(path27);
187206
+ isDirectory = isDirectory != null ? isDirectory : this._watched.has(path27) || this._watched.has(fullPath);
187207
+ if (!this._throttle("remove", path27, 100))
187070
187208
  return;
187071
187209
  if (!isDirectory && this._watched.size === 1) {
187072
187210
  this.add(directory, item, true);
187073
187211
  }
187074
- const wp = this._getWatchedDir(path26);
187212
+ const wp = this._getWatchedDir(path27);
187075
187213
  const nestedDirectoryChildren = wp.getChildren();
187076
- nestedDirectoryChildren.forEach((nested) => this._remove(path26, nested));
187214
+ nestedDirectoryChildren.forEach((nested) => this._remove(path27, nested));
187077
187215
  const parent = this._getWatchedDir(directory);
187078
187216
  const wasTracked = parent.has(item);
187079
187217
  parent.remove(item);
187080
187218
  if (this._symlinkPaths.has(fullPath)) {
187081
187219
  this._symlinkPaths.delete(fullPath);
187082
187220
  }
187083
- let relPath = path26;
187221
+ let relPath = path27;
187084
187222
  if (this.options.cwd)
187085
- relPath = sp2.relative(this.options.cwd, path26);
187223
+ relPath = sp2.relative(this.options.cwd, path27);
187086
187224
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
187087
187225
  const event = this._pendingWrites.get(relPath).cancelWait();
187088
187226
  if (event === EVENTS.ADD)
187089
187227
  return;
187090
187228
  }
187091
- this._watched.delete(path26);
187229
+ this._watched.delete(path27);
187092
187230
  this._watched.delete(fullPath);
187093
187231
  const eventName = isDirectory ? EVENTS.UNLINK_DIR : EVENTS.UNLINK;
187094
- if (wasTracked && !this._isIgnored(path26))
187095
- this._emit(eventName, path26);
187096
- this._closePath(path26);
187232
+ if (wasTracked && !this._isIgnored(path27))
187233
+ this._emit(eventName, path27);
187234
+ this._closePath(path27);
187097
187235
  }
187098
187236
  /**
187099
187237
  * Closes all watchers for a path
187100
187238
  */
187101
- _closePath(path26) {
187102
- this._closeFile(path26);
187103
- const dir = sp2.dirname(path26);
187104
- this._getWatchedDir(dir).remove(sp2.basename(path26));
187239
+ _closePath(path27) {
187240
+ this._closeFile(path27);
187241
+ const dir = sp2.dirname(path27);
187242
+ this._getWatchedDir(dir).remove(sp2.basename(path27));
187105
187243
  }
187106
187244
  /**
187107
187245
  * Closes only file-specific watchers
187108
187246
  */
187109
- _closeFile(path26) {
187110
- const closers = this._closers.get(path26);
187247
+ _closeFile(path27) {
187248
+ const closers = this._closers.get(path27);
187111
187249
  if (!closers)
187112
187250
  return;
187113
187251
  closers.forEach((closer) => closer());
187114
- this._closers.delete(path26);
187252
+ this._closers.delete(path27);
187115
187253
  }
187116
- _addPathCloser(path26, closer) {
187254
+ _addPathCloser(path27, closer) {
187117
187255
  if (!closer)
187118
187256
  return;
187119
- let list = this._closers.get(path26);
187257
+ let list = this._closers.get(path27);
187120
187258
  if (!list) {
187121
187259
  list = [];
187122
- this._closers.set(path26, list);
187260
+ this._closers.set(path27, list);
187123
187261
  }
187124
187262
  list.push(closer);
187125
187263
  }
@@ -187168,8 +187306,8 @@ var PortAllocator = class {
187168
187306
  };
187169
187307
 
187170
187308
  // src/lib/dev/stable-port-allocator.ts
187171
- import * as fs13 from "fs";
187172
- import * as path9 from "path";
187309
+ import * as fs14 from "fs";
187310
+ import * as path10 from "path";
187173
187311
  var PORT_RANGE_START2 = 4e4;
187174
187312
  var PORT_RANGE_END2 = 49999;
187175
187313
  var StablePortAllocator = class {
@@ -187178,16 +187316,16 @@ var StablePortAllocator = class {
187178
187316
  savedPorts = {};
187179
187317
  usedPorts = /* @__PURE__ */ new Set();
187180
187318
  constructor(projectRoot, key = "default") {
187181
- this.portsDir = path9.join(projectRoot, ".specific", "keys", key);
187182
- this.portsFilePath = path9.join(this.portsDir, "ports.json");
187319
+ this.portsDir = path10.join(projectRoot, ".specific", "keys", key);
187320
+ this.portsFilePath = path10.join(this.portsDir, "ports.json");
187183
187321
  this.loadPorts();
187184
187322
  }
187185
187323
  loadPorts() {
187186
- if (!fs13.existsSync(this.portsFilePath)) {
187324
+ if (!fs14.existsSync(this.portsFilePath)) {
187187
187325
  return;
187188
187326
  }
187189
187327
  try {
187190
- const content = fs13.readFileSync(this.portsFilePath, "utf-8");
187328
+ const content = fs14.readFileSync(this.portsFilePath, "utf-8");
187191
187329
  const data = JSON.parse(content);
187192
187330
  if (data.version === 1 && data.ports) {
187193
187331
  this.savedPorts = data.ports;
@@ -187200,14 +187338,14 @@ var StablePortAllocator = class {
187200
187338
  }
187201
187339
  }
187202
187340
  savePorts() {
187203
- if (!fs13.existsSync(this.portsDir)) {
187204
- fs13.mkdirSync(this.portsDir, { recursive: true });
187341
+ if (!fs14.existsSync(this.portsDir)) {
187342
+ fs14.mkdirSync(this.portsDir, { recursive: true });
187205
187343
  }
187206
187344
  const data = {
187207
187345
  version: 1,
187208
187346
  ports: this.savedPorts
187209
187347
  };
187210
- fs13.writeFileSync(this.portsFilePath, JSON.stringify(data, null, 2));
187348
+ fs14.writeFileSync(this.portsFilePath, JSON.stringify(data, null, 2));
187211
187349
  }
187212
187350
  allocateRandom() {
187213
187351
  const rangeSize = PORT_RANGE_END2 - PORT_RANGE_START2 + 1;
@@ -187234,21 +187372,21 @@ var StablePortAllocator = class {
187234
187372
  };
187235
187373
 
187236
187374
  // src/lib/dev/database-manager.ts
187237
- import * as fs14 from "fs";
187238
- import * as path10 from "path";
187375
+ import * as fs15 from "fs";
187376
+ import * as path11 from "path";
187239
187377
  import * as net from "net";
187240
187378
  import { spawn } from "child_process";
187241
187379
  async function startPostgres(pg, port, dataDir, onProgress) {
187242
187380
  const binary = await ensureBinary(postgresBinary, void 0, onProgress);
187243
- const dbDataPath = path10.join(process.cwd(), dataDir, pg.name);
187381
+ const dbDataPath = path11.join(process.cwd(), dataDir, pg.name);
187244
187382
  const host = "127.0.0.1";
187245
187383
  const user = "postgres";
187246
187384
  const password = "postgres";
187247
187385
  const libraryEnv = getLibraryEnv(binary);
187248
187386
  const env2 = { ...process.env, ...libraryEnv };
187249
- const dataExists = fs14.existsSync(dbDataPath);
187387
+ const dataExists = fs15.existsSync(dbDataPath);
187250
187388
  if (!dataExists) {
187251
- fs14.mkdirSync(dbDataPath, { recursive: true });
187389
+ fs15.mkdirSync(dbDataPath, { recursive: true });
187252
187390
  await runCommand(
187253
187391
  binary.executables["initdb"],
187254
187392
  ["-D", dbDataPath, "-U", user, "--auth=trust", "--no-locale", "-E", "UTF8"],
@@ -187324,9 +187462,9 @@ async function startRedis(redis, port, onProgress) {
187324
187462
  }
187325
187463
  async function startStorage(storage, port, dataDir) {
187326
187464
  const S3rver = (await import("s3rver")).default;
187327
- const storageDataPath = path10.join(process.cwd(), dataDir, storage.name);
187328
- if (!fs14.existsSync(storageDataPath)) {
187329
- fs14.mkdirSync(storageDataPath, { recursive: true });
187465
+ const storageDataPath = path11.join(process.cwd(), dataDir, storage.name);
187466
+ if (!fs15.existsSync(storageDataPath)) {
187467
+ fs15.mkdirSync(storageDataPath, { recursive: true });
187330
187468
  }
187331
187469
  const host = "127.0.0.1";
187332
187470
  const accessKey = "S3RVER";
@@ -187460,7 +187598,7 @@ import { spawn as spawn2 } from "child_process";
187460
187598
  // src/lib/local/parser.ts
187461
187599
  var import_hcl2_json_parser2 = __toESM(require_dist(), 1);
187462
187600
  import { readFile, writeFile } from "fs/promises";
187463
- import { existsSync as existsSync11 } from "fs";
187601
+ import { existsSync as existsSync12 } from "fs";
187464
187602
  var { parseToObject: parseToObject2 } = import_hcl2_json_parser2.default;
187465
187603
  var LOCAL_FILE = "specific.local";
187466
187604
  var HEADER_COMMENT = `# Local secrets and configuration
@@ -187501,7 +187639,7 @@ async function parseLocalFile(content) {
187501
187639
  return { secrets, configs };
187502
187640
  }
187503
187641
  async function loadLocal() {
187504
- if (!existsSync11(LOCAL_FILE)) {
187642
+ if (!existsSync12(LOCAL_FILE)) {
187505
187643
  return { secrets: /* @__PURE__ */ new Map(), configs: /* @__PURE__ */ new Map() };
187506
187644
  }
187507
187645
  const content = await readFile(LOCAL_FILE, "utf-8");
@@ -187541,7 +187679,7 @@ ${newLine}
187541
187679
  }
187542
187680
  async function saveLocalSecret(name, value) {
187543
187681
  let content = "";
187544
- if (existsSync11(LOCAL_FILE)) {
187682
+ if (existsSync12(LOCAL_FILE)) {
187545
187683
  content = await readFile(LOCAL_FILE, "utf-8");
187546
187684
  } else {
187547
187685
  content = HEADER_COMMENT;
@@ -187551,7 +187689,7 @@ async function saveLocalSecret(name, value) {
187551
187689
  }
187552
187690
  async function saveLocalConfig(name, value) {
187553
187691
  let content = "";
187554
- if (existsSync11(LOCAL_FILE)) {
187692
+ if (existsSync12(LOCAL_FILE)) {
187555
187693
  content = await readFile(LOCAL_FILE, "utf-8");
187556
187694
  } else {
187557
187695
  content = HEADER_COMMENT;
@@ -187915,8 +188053,8 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
187915
188053
  }
187916
188054
 
187917
188055
  // src/lib/dev/instance-state.ts
187918
- import * as fs15 from "fs";
187919
- import * as path11 from "path";
188056
+ import * as fs16 from "fs";
188057
+ import * as path12 from "path";
187920
188058
  var InstanceStateManager = class {
187921
188059
  stateDir;
187922
188060
  statePath;
@@ -187925,16 +188063,16 @@ var InstanceStateManager = class {
187925
188063
  key;
187926
188064
  constructor(projectRoot, key = "default") {
187927
188065
  this.key = key;
187928
- this.stateDir = path11.join(projectRoot, ".specific", "keys", key);
187929
- this.statePath = path11.join(this.stateDir, "state.json");
187930
- this.lockPath = path11.join(this.stateDir, "state.lock");
188066
+ this.stateDir = path12.join(projectRoot, ".specific", "keys", key);
188067
+ this.statePath = path12.join(this.stateDir, "state.json");
188068
+ this.lockPath = path12.join(this.stateDir, "state.lock");
187931
188069
  }
187932
188070
  getKey() {
187933
188071
  return this.key;
187934
188072
  }
187935
188073
  ensureStateDir() {
187936
- if (!fs15.existsSync(this.stateDir)) {
187937
- fs15.mkdirSync(this.stateDir, { recursive: true });
188074
+ if (!fs16.existsSync(this.stateDir)) {
188075
+ fs16.mkdirSync(this.stateDir, { recursive: true });
187938
188076
  }
187939
188077
  }
187940
188078
  isProcessRunning(pid) {
@@ -187951,15 +188089,15 @@ var InstanceStateManager = class {
187951
188089
  const startTime = Date.now();
187952
188090
  while (Date.now() - startTime < timeoutMs) {
187953
188091
  try {
187954
- const fd = fs15.openSync(
188092
+ const fd = fs16.openSync(
187955
188093
  this.lockPath,
187956
- fs15.constants.O_CREAT | fs15.constants.O_EXCL | fs15.constants.O_WRONLY
188094
+ fs16.constants.O_CREAT | fs16.constants.O_EXCL | fs16.constants.O_WRONLY
187957
188095
  );
187958
- fs15.writeSync(fd, String(process.pid));
187959
- fs15.closeSync(fd);
188096
+ fs16.writeSync(fd, String(process.pid));
188097
+ fs16.closeSync(fd);
187960
188098
  return () => {
187961
188099
  try {
187962
- fs15.unlinkSync(this.lockPath);
188100
+ fs16.unlinkSync(this.lockPath);
187963
188101
  } catch {
187964
188102
  }
187965
188103
  };
@@ -187968,16 +188106,16 @@ var InstanceStateManager = class {
187968
188106
  if (err.code === "EEXIST") {
187969
188107
  try {
187970
188108
  const lockPid = parseInt(
187971
- fs15.readFileSync(this.lockPath, "utf-8").trim(),
188109
+ fs16.readFileSync(this.lockPath, "utf-8").trim(),
187972
188110
  10
187973
188111
  );
187974
188112
  if (!this.isProcessRunning(lockPid)) {
187975
- fs15.unlinkSync(this.lockPath);
188113
+ fs16.unlinkSync(this.lockPath);
187976
188114
  continue;
187977
188115
  }
187978
188116
  } catch {
187979
188117
  try {
187980
- fs15.unlinkSync(this.lockPath);
188118
+ fs16.unlinkSync(this.lockPath);
187981
188119
  } catch {
187982
188120
  }
187983
188121
  continue;
@@ -187991,12 +188129,12 @@ var InstanceStateManager = class {
187991
188129
  throw new Error("Failed to acquire state lock (timeout)");
187992
188130
  }
187993
188131
  async getExistingInstances() {
187994
- if (!fs15.existsSync(this.statePath)) {
188132
+ if (!fs16.existsSync(this.statePath)) {
187995
188133
  return null;
187996
188134
  }
187997
188135
  const releaseLock = await this.acquireLock();
187998
188136
  try {
187999
- const content = fs15.readFileSync(this.statePath, "utf-8");
188137
+ const content = fs16.readFileSync(this.statePath, "utf-8");
188000
188138
  const state = JSON.parse(content);
188001
188139
  if (!this.isProcessRunning(state.owner.pid)) {
188002
188140
  return null;
@@ -188009,21 +188147,21 @@ var InstanceStateManager = class {
188009
188147
  }
188010
188148
  }
188011
188149
  async cleanStaleState() {
188012
- if (!fs15.existsSync(this.statePath)) {
188150
+ if (!fs16.existsSync(this.statePath)) {
188013
188151
  return false;
188014
188152
  }
188015
188153
  const releaseLock = await this.acquireLock();
188016
188154
  try {
188017
- const content = fs15.readFileSync(this.statePath, "utf-8");
188155
+ const content = fs16.readFileSync(this.statePath, "utf-8");
188018
188156
  const state = JSON.parse(content);
188019
188157
  if (!this.isProcessRunning(state.owner.pid)) {
188020
- fs15.unlinkSync(this.statePath);
188158
+ fs16.unlinkSync(this.statePath);
188021
188159
  return true;
188022
188160
  }
188023
188161
  return false;
188024
188162
  } catch {
188025
188163
  try {
188026
- fs15.unlinkSync(this.statePath);
188164
+ fs16.unlinkSync(this.statePath);
188027
188165
  return true;
188028
188166
  } catch {
188029
188167
  }
@@ -188035,8 +188173,8 @@ var InstanceStateManager = class {
188035
188173
  async claimOwnership(command) {
188036
188174
  const releaseLock = await this.acquireLock();
188037
188175
  try {
188038
- if (fs15.existsSync(this.statePath)) {
188039
- const content = fs15.readFileSync(this.statePath, "utf-8");
188176
+ if (fs16.existsSync(this.statePath)) {
188177
+ const content = fs16.readFileSync(this.statePath, "utf-8");
188040
188178
  const state2 = JSON.parse(content);
188041
188179
  if (this.isProcessRunning(state2.owner.pid)) {
188042
188180
  throw new Error(`Instances already owned by PID ${state2.owner.pid}`);
@@ -188105,8 +188243,8 @@ var InstanceStateManager = class {
188105
188243
  }
188106
188244
  const releaseLock = await this.acquireLock();
188107
188245
  try {
188108
- if (fs15.existsSync(this.statePath)) {
188109
- fs15.unlinkSync(this.statePath);
188246
+ if (fs16.existsSync(this.statePath)) {
188247
+ fs16.unlinkSync(this.statePath);
188110
188248
  }
188111
188249
  this.ownsInstances = false;
188112
188250
  } finally {
@@ -188114,26 +188252,26 @@ var InstanceStateManager = class {
188114
188252
  }
188115
188253
  }
188116
188254
  readState() {
188117
- const content = fs15.readFileSync(this.statePath, "utf-8");
188255
+ const content = fs16.readFileSync(this.statePath, "utf-8");
188118
188256
  return JSON.parse(content);
188119
188257
  }
188120
188258
  writeStateAtomic(state) {
188121
188259
  this.ensureStateDir();
188122
188260
  const tmpPath = this.statePath + ".tmp";
188123
- fs15.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
188124
- fs15.renameSync(tmpPath, this.statePath);
188261
+ fs16.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
188262
+ fs16.renameSync(tmpPath, this.statePath);
188125
188263
  }
188126
188264
  };
188127
188265
 
188128
188266
  // src/lib/dev/http-proxy.ts
188129
188267
  import * as http from "http";
188130
188268
  import * as https from "https";
188131
- import * as fs16 from "fs";
188132
- import * as path12 from "path";
188269
+ import * as fs17 from "fs";
188270
+ import * as path13 from "path";
188133
188271
  import { fileURLToPath as fileURLToPath3 } from "url";
188134
188272
  import httpProxy from "http-proxy";
188135
- var __dirname3 = path12.dirname(fileURLToPath3(import.meta.url));
188136
- var adminDir = path12.join(__dirname3, "admin");
188273
+ var __dirname3 = path13.dirname(fileURLToPath3(import.meta.url));
188274
+ var adminDir = path13.join(__dirname3, "admin");
188137
188275
  var HTTP_PORT = 80;
188138
188276
  var HTTPS_PORT = 443;
188139
188277
  var DOMAIN_SUFFIX = ".local.spcf.app";
@@ -188419,18 +188557,18 @@ function serveStaticFile(res, pathname) {
188419
188557
  filePath = filePath + "index.html";
188420
188558
  }
188421
188559
  const relativePath = filePath.startsWith("/") ? filePath.slice(1) : filePath;
188422
- const fullPath = path12.join(adminDir, relativePath);
188423
- const resolvedPath = path12.resolve(fullPath);
188424
- const resolvedAdminDir = path12.resolve(adminDir);
188560
+ const fullPath = path13.join(adminDir, relativePath);
188561
+ const resolvedPath = path13.resolve(fullPath);
188562
+ const resolvedAdminDir = path13.resolve(adminDir);
188425
188563
  if (!resolvedPath.startsWith(resolvedAdminDir)) {
188426
188564
  res.writeHead(403, { "Content-Type": "text/html" });
188427
188565
  res.end("<h1>Forbidden</h1>");
188428
188566
  return;
188429
188567
  }
188430
- if (fs16.existsSync(resolvedPath)) {
188431
- if (fs16.statSync(resolvedPath).isDirectory()) {
188432
- const indexPath2 = path12.join(resolvedPath, "index.html");
188433
- if (fs16.existsSync(indexPath2)) {
188568
+ if (fs17.existsSync(resolvedPath)) {
188569
+ if (fs17.statSync(resolvedPath).isDirectory()) {
188570
+ const indexPath2 = path13.join(resolvedPath, "index.html");
188571
+ if (fs17.existsSync(indexPath2)) {
188434
188572
  return serveFile(res, indexPath2);
188435
188573
  }
188436
188574
  } else {
@@ -188438,28 +188576,28 @@ function serveStaticFile(res, pathname) {
188438
188576
  }
188439
188577
  }
188440
188578
  const htmlPath = resolvedPath + ".html";
188441
- if (fs16.existsSync(htmlPath)) {
188579
+ if (fs17.existsSync(htmlPath)) {
188442
188580
  return serveFile(res, htmlPath);
188443
188581
  }
188444
- const indexPath = path12.join(resolvedPath, "index.html");
188445
- if (fs16.existsSync(indexPath)) {
188582
+ const indexPath = path13.join(resolvedPath, "index.html");
188583
+ if (fs17.existsSync(indexPath)) {
188446
188584
  return serveFile(res, indexPath);
188447
188585
  }
188448
- const notFoundPath = path12.join(adminDir, "404.html");
188449
- if (fs16.existsSync(notFoundPath)) {
188586
+ const notFoundPath = path13.join(adminDir, "404.html");
188587
+ if (fs17.existsSync(notFoundPath)) {
188450
188588
  return serveFileContent(res, notFoundPath, "text/html", 404);
188451
188589
  }
188452
188590
  res.writeHead(404, { "Content-Type": "text/html" });
188453
188591
  res.end("<h1>Not Found</h1>");
188454
188592
  }
188455
188593
  function serveFile(res, filePath) {
188456
- const ext = path12.extname(filePath).toLowerCase();
188594
+ const ext = path13.extname(filePath).toLowerCase();
188457
188595
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
188458
188596
  serveFileContent(res, filePath, contentType, 200);
188459
188597
  }
188460
188598
  function serveFileContent(res, filePath, contentType, statusCode = 200) {
188461
188599
  try {
188462
- const content = fs16.readFileSync(filePath);
188600
+ const content = fs17.readFileSync(filePath);
188463
188601
  res.writeHead(statusCode, { "Content-Type": contentType });
188464
188602
  res.end(content);
188465
188603
  } catch (err) {
@@ -188519,8 +188657,8 @@ import { spawn as spawn3 } from "child_process";
188519
188657
 
188520
188658
  // src/lib/secrets/parser.ts
188521
188659
  import { readFile as readFile2, writeFile as writeFile2, mkdir } from "fs/promises";
188522
- import { existsSync as existsSync14 } from "fs";
188523
- import * as path13 from "path";
188660
+ import { existsSync as existsSync15 } from "fs";
188661
+ import * as path14 from "path";
188524
188662
  import * as crypto2 from "crypto";
188525
188663
  var GENERATED_SECRETS_FILE = ".specific/generated-secrets.json";
188526
188664
  async function loadSecrets() {
@@ -188537,7 +188675,7 @@ function generateRandomString(length = 64) {
188537
188675
  return result;
188538
188676
  }
188539
188677
  async function loadGeneratedSecrets() {
188540
- if (!existsSync14(GENERATED_SECRETS_FILE)) {
188678
+ if (!existsSync15(GENERATED_SECRETS_FILE)) {
188541
188679
  return /* @__PURE__ */ new Map();
188542
188680
  }
188543
188681
  const content = await readFile2(GENERATED_SECRETS_FILE, "utf-8");
@@ -188546,13 +188684,13 @@ async function loadGeneratedSecrets() {
188546
188684
  }
188547
188685
  async function saveGeneratedSecret(name, value) {
188548
188686
  let secrets = {};
188549
- if (existsSync14(GENERATED_SECRETS_FILE)) {
188687
+ if (existsSync15(GENERATED_SECRETS_FILE)) {
188550
188688
  const content = await readFile2(GENERATED_SECRETS_FILE, "utf-8");
188551
188689
  secrets = JSON.parse(content);
188552
188690
  }
188553
188691
  secrets[name] = value;
188554
- const dir = path13.dirname(GENERATED_SECRETS_FILE);
188555
- if (!existsSync14(dir)) {
188692
+ const dir = path14.dirname(GENERATED_SECRETS_FILE);
188693
+ if (!existsSync15(dir)) {
188556
188694
  await mkdir(dir, { recursive: true });
188557
188695
  }
188558
188696
  await writeFile2(GENERATED_SECRETS_FILE, JSON.stringify(secrets, null, 2) + "\n");
@@ -188706,8 +188844,8 @@ function sleep3(ms) {
188706
188844
 
188707
188845
  // src/lib/dev/drizzle-gateway-manager.ts
188708
188846
  import * as net3 from "net";
188709
- import * as fs17 from "fs";
188710
- import * as path14 from "path";
188847
+ import * as fs18 from "fs";
188848
+ import * as path15 from "path";
188711
188849
  import { spawn as spawn4 } from "child_process";
188712
188850
  import { randomUUID } from "crypto";
188713
188851
  function generateStoreJson(postgresInstances) {
@@ -188739,13 +188877,13 @@ async function startDrizzleGateway(postgresInstances, port, configDir, options2)
188739
188877
  options2?.onProgress
188740
188878
  );
188741
188879
  const host = "127.0.0.1";
188742
- const drizzleConfigDir = path14.join(configDir, "drizzle-gateway");
188743
- if (!fs17.existsSync(drizzleConfigDir)) {
188744
- fs17.mkdirSync(drizzleConfigDir, { recursive: true });
188880
+ const drizzleConfigDir = path15.join(configDir, "drizzle-gateway");
188881
+ if (!fs18.existsSync(drizzleConfigDir)) {
188882
+ fs18.mkdirSync(drizzleConfigDir, { recursive: true });
188745
188883
  }
188746
188884
  const storeJson = generateStoreJson(postgresInstances);
188747
- const storeJsonPath = path14.join(drizzleConfigDir, "store.json");
188748
- fs17.writeFileSync(storeJsonPath, JSON.stringify(storeJson, null, 2));
188885
+ const storeJsonPath = path15.join(drizzleConfigDir, "store.json");
188886
+ fs18.writeFileSync(storeJsonPath, JSON.stringify(storeJson, null, 2));
188749
188887
  writeLog("drizzle-gateway", `Starting Drizzle Gateway`);
188750
188888
  writeLog("drizzle-gateway", `STORE_PATH: ${drizzleConfigDir}`);
188751
188889
  writeLog("drizzle-gateway", `PORT: ${port}`);
@@ -188847,16 +188985,16 @@ function detectSyncDatabases(config) {
188847
188985
  }
188848
188986
 
188849
188987
  // src/lib/dev/reshape-watcher.ts
188850
- import * as fs18 from "fs";
188851
- import * as path15 from "path";
188988
+ import * as fs19 from "fs";
188989
+ import * as path16 from "path";
188852
188990
  import { spawnSync } from "child_process";
188853
188991
  function getMigrationFiles(dir, log) {
188854
188992
  log(`Scanning migrations directory: ${dir}`);
188855
- if (!fs18.existsSync(dir)) {
188993
+ if (!fs19.existsSync(dir)) {
188856
188994
  log(`Migrations directory does not exist: ${dir}`);
188857
188995
  return [];
188858
188996
  }
188859
- const files = fs18.readdirSync(dir);
188997
+ const files = fs19.readdirSync(dir);
188860
188998
  log(`Found ${files.length} files in directory`);
188861
188999
  const tomlFiles = files.filter((f) => f.endsWith(".toml")).sort((a, b) => a.localeCompare(b));
188862
189000
  log(`Found ${tomlFiles.length} .toml migration files: ${tomlFiles.join(", ") || "(none)"}`);
@@ -188906,7 +189044,7 @@ function runReshape(args, databaseUrl, migrationsDir, reshapeBinaryPath, log) {
188906
189044
  }
188907
189045
  function makeReadOnly(filePath, log) {
188908
189046
  try {
188909
- fs18.chmodSync(filePath, 292);
189047
+ fs19.chmodSync(filePath, 292);
188910
189048
  log(`Set file permissions to read-only (444): ${filePath}`);
188911
189049
  } catch (err) {
188912
189050
  const message = err instanceof Error ? err.message : String(err);
@@ -188967,7 +189105,7 @@ function createReshapeWatcher(options2) {
188967
189105
  log(`Successfully completed ${currentMigrationFiles.length - 1} migration(s)`);
188968
189106
  log(`Making completed migration files read-only...`);
188969
189107
  for (let i = 0; i < currentMigrationFiles.length - 1; i++) {
188970
- const filePath = path15.join(migrationsDir, currentMigrationFiles[i]);
189108
+ const filePath = path16.join(migrationsDir, currentMigrationFiles[i]);
188971
189109
  makeReadOnly(filePath, log);
188972
189110
  }
188973
189111
  log(`Starting latest migration "${lastMigrationName}" (will not be completed, allowing iteration)`);
@@ -188992,9 +189130,9 @@ function createReshapeWatcher(options2) {
188992
189130
  };
188993
189131
  const startWatching = () => {
188994
189132
  log(`Starting file watcher for migrations directory...`);
188995
- if (!fs18.existsSync(migrationsDir)) {
189133
+ if (!fs19.existsSync(migrationsDir)) {
188996
189134
  log(`Migrations directory does not exist, creating: ${migrationsDir}`);
188997
- fs18.mkdirSync(migrationsDir, { recursive: true });
189135
+ fs19.mkdirSync(migrationsDir, { recursive: true });
188998
189136
  }
188999
189137
  log(`Watching directory: ${migrationsDir}`);
189000
189138
  watcher = chokidar_default.watch(migrationsDir, {
@@ -189007,7 +189145,7 @@ function createReshapeWatcher(options2) {
189007
189145
  }
189008
189146
  });
189009
189147
  watcher.on("change", (filePath) => {
189010
- const filename = path15.basename(filePath);
189148
+ const filename = path16.basename(filePath);
189011
189149
  if (!filename.endsWith(".toml")) return;
189012
189150
  log(`File change detected: ${filename}`);
189013
189151
  log(` Full path: ${filePath}`);
@@ -189038,7 +189176,7 @@ function createReshapeWatcher(options2) {
189038
189176
  }
189039
189177
  });
189040
189178
  watcher.on("add", (filePath) => {
189041
- const filename = path15.basename(filePath);
189179
+ const filename = path16.basename(filePath);
189042
189180
  if (!filename.endsWith(".toml")) return;
189043
189181
  log(`New file detected: ${filename}`);
189044
189182
  log(` Full path: ${filePath}`);
@@ -189062,7 +189200,7 @@ function createReshapeWatcher(options2) {
189062
189200
  return;
189063
189201
  }
189064
189202
  log(`Previous migration completed successfully`);
189065
- const completedPath = path15.join(migrationsDir, startedMigration);
189203
+ const completedPath = path16.join(migrationsDir, startedMigration);
189066
189204
  makeReadOnly(completedPath, log);
189067
189205
  } else {
189068
189206
  log(`No previous migration was started`);
@@ -189087,7 +189225,7 @@ function createReshapeWatcher(options2) {
189087
189225
  onSearchPathChanged(newSearchPath);
189088
189226
  });
189089
189227
  watcher.on("unlink", (filePath) => {
189090
- const filename = path15.basename(filePath);
189228
+ const filename = path16.basename(filePath);
189091
189229
  if (!filename.endsWith(".toml")) return;
189092
189230
  log(`File deleted: ${filename}`);
189093
189231
  log(` Full path: ${filePath}`);
@@ -189311,24 +189449,24 @@ function watchConfigFile(configPath, debounceMs, onChange) {
189311
189449
  }
189312
189450
 
189313
189451
  // src/lib/dev/subdomain-generator.ts
189314
- import * as fs19 from "fs";
189315
- import * as path16 from "path";
189452
+ import * as fs20 from "fs";
189453
+ import * as path17 from "path";
189316
189454
  import { generateSlug } from "random-word-slugs";
189317
189455
  var StableSubdomainAllocator = class {
189318
189456
  tunnelsDir;
189319
189457
  tunnelsFilePath;
189320
189458
  baseSlug = null;
189321
189459
  constructor(projectRoot, key = "default") {
189322
- this.tunnelsDir = path16.join(projectRoot, ".specific", "keys", key);
189323
- this.tunnelsFilePath = path16.join(this.tunnelsDir, "tunnels.json");
189460
+ this.tunnelsDir = path17.join(projectRoot, ".specific", "keys", key);
189461
+ this.tunnelsFilePath = path17.join(this.tunnelsDir, "tunnels.json");
189324
189462
  this.loadTunnels();
189325
189463
  }
189326
189464
  loadTunnels() {
189327
- if (!fs19.existsSync(this.tunnelsFilePath)) {
189465
+ if (!fs20.existsSync(this.tunnelsFilePath)) {
189328
189466
  return;
189329
189467
  }
189330
189468
  try {
189331
- const content = fs19.readFileSync(this.tunnelsFilePath, "utf-8");
189469
+ const content = fs20.readFileSync(this.tunnelsFilePath, "utf-8");
189332
189470
  const data = JSON.parse(content);
189333
189471
  if (data.version === 1 && data.baseSlug) {
189334
189472
  this.baseSlug = data.baseSlug;
@@ -189338,14 +189476,14 @@ var StableSubdomainAllocator = class {
189338
189476
  }
189339
189477
  }
189340
189478
  saveTunnels() {
189341
- if (!fs19.existsSync(this.tunnelsDir)) {
189342
- fs19.mkdirSync(this.tunnelsDir, { recursive: true });
189479
+ if (!fs20.existsSync(this.tunnelsDir)) {
189480
+ fs20.mkdirSync(this.tunnelsDir, { recursive: true });
189343
189481
  }
189344
189482
  const data = {
189345
189483
  version: 1,
189346
189484
  baseSlug: this.baseSlug
189347
189485
  };
189348
- fs19.writeFileSync(this.tunnelsFilePath, JSON.stringify(data, null, 2));
189486
+ fs20.writeFileSync(this.tunnelsFilePath, JSON.stringify(data, null, 2));
189349
189487
  }
189350
189488
  generateBaseSlug() {
189351
189489
  return generateSlug(2, {
@@ -189384,6 +189522,7 @@ var StableSubdomainAllocator = class {
189384
189522
  import { EventEmitter as EventEmitter2 } from "node:events";
189385
189523
  import * as net4 from "node:net";
189386
189524
  var DEFAULT_HOST = "https://tunnel.spcf.app";
189525
+ var RECONNECT_DELAYS = [1e3, 2e3, 4e3, 8e3, 15e3];
189387
189526
  async function register(baseUrl, subdomain) {
189388
189527
  const res = await fetch(`${baseUrl}/${subdomain}`);
189389
189528
  if (!res.ok) {
@@ -189396,15 +189535,17 @@ var TunnelClientImpl = class extends EventEmitter2 {
189396
189535
  info;
189397
189536
  localPort;
189398
189537
  tunnelHost;
189538
+ baseUrl;
189399
189539
  url;
189400
189540
  subdomain;
189401
189541
  pool = /* @__PURE__ */ new Set();
189402
189542
  closed = false;
189403
- constructor(info, localPort, tunnelHost) {
189543
+ constructor(info, localPort, tunnelHost, baseUrl) {
189404
189544
  super();
189405
189545
  this.info = info;
189406
189546
  this.localPort = localPort;
189407
189547
  this.tunnelHost = tunnelHost;
189548
+ this.baseUrl = baseUrl;
189408
189549
  this.url = info.url;
189409
189550
  this.subdomain = info.id;
189410
189551
  this.fillPool();
@@ -189452,69 +189593,55 @@ var TunnelClientImpl = class extends EventEmitter2 {
189452
189593
  return;
189453
189594
  if (errored) {
189454
189595
  if (this.pool.size === 0)
189455
- this.emit("close");
189596
+ this.reconnect();
189456
189597
  } else {
189457
189598
  this.fillPool();
189458
189599
  }
189459
189600
  }
189601
+ async reconnect() {
189602
+ this.emit("close");
189603
+ for (let attempt = 0; !this.closed; attempt++) {
189604
+ const delay = RECONNECT_DELAYS[Math.min(attempt, RECONNECT_DELAYS.length - 1)];
189605
+ await new Promise((r) => setTimeout(r, delay));
189606
+ if (this.closed)
189607
+ return;
189608
+ try {
189609
+ this.info = await register(this.baseUrl, this.subdomain);
189610
+ this.fillPool();
189611
+ this.emit("reconnect");
189612
+ return;
189613
+ } catch {
189614
+ }
189615
+ }
189616
+ }
189460
189617
  };
189461
189618
  async function connect2(options2) {
189462
189619
  const host = options2.host ?? DEFAULT_HOST;
189463
189620
  const tunnelHost = new URL(host).hostname;
189464
189621
  const info = await register(host, options2.subdomain);
189465
- return new TunnelClientImpl(info, options2.port, tunnelHost);
189622
+ return new TunnelClientImpl(info, options2.port, tunnelHost, host);
189466
189623
  }
189467
189624
 
189468
189625
  // src/lib/dev/tunnel-manager.ts
189469
- var TUNNEL_HOST = "https://tunnel.spcf.app";
189470
189626
  async function startTunnel(serviceName, endpointName, port, subdomain, callbacks) {
189471
- let currentTunnel = null;
189472
- let stopped = false;
189473
- function setupTunnel(tunnel) {
189474
- tunnel.on("error", (err) => {
189475
- callbacks?.onError?.(serviceName, endpointName, err);
189476
- });
189477
- tunnel.on("close", () => {
189478
- if (!stopped) {
189479
- callbacks?.onClose?.(serviceName, endpointName);
189480
- reconnect();
189481
- }
189482
- });
189483
- }
189484
- async function reconnect() {
189485
- const delays = [1e3, 2e3, 4e3, 8e3, 15e3];
189486
- for (let attempt = 0; !stopped; attempt++) {
189487
- const delay = delays[Math.min(attempt, delays.length - 1)];
189488
- await new Promise((r) => setTimeout(r, delay));
189489
- if (stopped) return;
189490
- try {
189491
- currentTunnel = await connect2({ port, subdomain, host: TUNNEL_HOST });
189492
- setupTunnel(currentTunnel);
189493
- callbacks?.onReconnect?.(serviceName, endpointName);
189494
- return;
189495
- } catch {
189496
- }
189497
- }
189498
- }
189499
- currentTunnel = await connect2({ port, subdomain, host: TUNNEL_HOST });
189500
- setupTunnel(currentTunnel);
189627
+ const tunnel = await connect2({ port, subdomain });
189628
+ tunnel.on("error", (err) => callbacks?.onError?.(serviceName, endpointName, err));
189629
+ tunnel.on("close", () => callbacks?.onClose?.(serviceName, endpointName));
189630
+ tunnel.on("reconnect", () => callbacks?.onReconnect?.(serviceName, endpointName));
189501
189631
  return {
189502
189632
  serviceName,
189503
189633
  endpointName,
189504
189634
  localPort: port,
189505
- url: currentTunnel.url,
189635
+ url: tunnel.url,
189506
189636
  subdomain,
189507
- stop: async () => {
189508
- stopped = true;
189509
- currentTunnel?.close();
189510
- }
189637
+ stop: async () => tunnel.close()
189511
189638
  };
189512
189639
  }
189513
189640
 
189514
189641
  // src/lib/dev/proxy-registry.ts
189515
- import * as fs20 from "fs";
189516
- import * as path17 from "path";
189517
- import * as os7 from "os";
189642
+ import * as fs21 from "fs";
189643
+ import * as path18 from "path";
189644
+ import * as os8 from "os";
189518
189645
  import * as net5 from "net";
189519
189646
  var ProxyRegistryManager = class {
189520
189647
  proxyDir;
@@ -189524,14 +189651,14 @@ var ProxyRegistryManager = class {
189524
189651
  isOwner = false;
189525
189652
  registryWatcher = null;
189526
189653
  constructor() {
189527
- this.proxyDir = path17.join(os7.homedir(), ".specific", "proxy");
189528
- this.ownerPath = path17.join(this.proxyDir, "owner.json");
189529
- this.registryPath = path17.join(this.proxyDir, "registry.json");
189530
- this.lockPath = path17.join(this.proxyDir, "registry.lock");
189654
+ this.proxyDir = path18.join(os8.homedir(), ".specific", "proxy");
189655
+ this.ownerPath = path18.join(this.proxyDir, "owner.json");
189656
+ this.registryPath = path18.join(this.proxyDir, "registry.json");
189657
+ this.lockPath = path18.join(this.proxyDir, "registry.lock");
189531
189658
  }
189532
189659
  ensureProxyDir() {
189533
- if (!fs20.existsSync(this.proxyDir)) {
189534
- fs20.mkdirSync(this.proxyDir, { recursive: true });
189660
+ if (!fs21.existsSync(this.proxyDir)) {
189661
+ fs21.mkdirSync(this.proxyDir, { recursive: true });
189535
189662
  }
189536
189663
  }
189537
189664
  isProcessRunning(pid) {
@@ -189588,15 +189715,15 @@ var ProxyRegistryManager = class {
189588
189715
  const startTime = Date.now();
189589
189716
  while (Date.now() - startTime < timeoutMs) {
189590
189717
  try {
189591
- const fd = fs20.openSync(
189718
+ const fd = fs21.openSync(
189592
189719
  this.lockPath,
189593
- fs20.constants.O_CREAT | fs20.constants.O_EXCL | fs20.constants.O_WRONLY
189720
+ fs21.constants.O_CREAT | fs21.constants.O_EXCL | fs21.constants.O_WRONLY
189594
189721
  );
189595
- fs20.writeSync(fd, String(process.pid));
189596
- fs20.closeSync(fd);
189722
+ fs21.writeSync(fd, String(process.pid));
189723
+ fs21.closeSync(fd);
189597
189724
  return () => {
189598
189725
  try {
189599
- fs20.unlinkSync(this.lockPath);
189726
+ fs21.unlinkSync(this.lockPath);
189600
189727
  } catch {
189601
189728
  }
189602
189729
  };
@@ -189605,16 +189732,16 @@ var ProxyRegistryManager = class {
189605
189732
  if (err.code === "EEXIST") {
189606
189733
  try {
189607
189734
  const lockPid = parseInt(
189608
- fs20.readFileSync(this.lockPath, "utf-8").trim(),
189735
+ fs21.readFileSync(this.lockPath, "utf-8").trim(),
189609
189736
  10
189610
189737
  );
189611
189738
  if (!this.isProcessRunning(lockPid)) {
189612
- fs20.unlinkSync(this.lockPath);
189739
+ fs21.unlinkSync(this.lockPath);
189613
189740
  continue;
189614
189741
  }
189615
189742
  } catch {
189616
189743
  try {
189617
- fs20.unlinkSync(this.lockPath);
189744
+ fs21.unlinkSync(this.lockPath);
189618
189745
  } catch {
189619
189746
  }
189620
189747
  continue;
@@ -189634,8 +189761,8 @@ var ProxyRegistryManager = class {
189634
189761
  async claimProxyOwnership(key) {
189635
189762
  const releaseLock = await this.acquireLock();
189636
189763
  try {
189637
- if (fs20.existsSync(this.ownerPath)) {
189638
- const content = fs20.readFileSync(this.ownerPath, "utf-8");
189764
+ if (fs21.existsSync(this.ownerPath)) {
189765
+ const content = fs21.readFileSync(this.ownerPath, "utf-8");
189639
189766
  const ownerFile2 = JSON.parse(content);
189640
189767
  if (await this.isProxyOwnerHealthy(ownerFile2.owner.pid)) {
189641
189768
  return false;
@@ -189665,11 +189792,11 @@ var ProxyRegistryManager = class {
189665
189792
  }
189666
189793
  const releaseLock = await this.acquireLock();
189667
189794
  try {
189668
- if (fs20.existsSync(this.ownerPath)) {
189669
- const content = fs20.readFileSync(this.ownerPath, "utf-8");
189795
+ if (fs21.existsSync(this.ownerPath)) {
189796
+ const content = fs21.readFileSync(this.ownerPath, "utf-8");
189670
189797
  const ownerFile = JSON.parse(content);
189671
189798
  if (ownerFile.owner.pid === process.pid) {
189672
- fs20.unlinkSync(this.ownerPath);
189799
+ fs21.unlinkSync(this.ownerPath);
189673
189800
  }
189674
189801
  }
189675
189802
  this.isOwner = false;
@@ -189681,12 +189808,12 @@ var ProxyRegistryManager = class {
189681
189808
  * Get the current proxy owner.
189682
189809
  */
189683
189810
  async getProxyOwner() {
189684
- if (!fs20.existsSync(this.ownerPath)) {
189811
+ if (!fs21.existsSync(this.ownerPath)) {
189685
189812
  return null;
189686
189813
  }
189687
189814
  const releaseLock = await this.acquireLock();
189688
189815
  try {
189689
- const content = fs20.readFileSync(this.ownerPath, "utf-8");
189816
+ const content = fs21.readFileSync(this.ownerPath, "utf-8");
189690
189817
  const ownerFile = JSON.parse(content);
189691
189818
  if (!await this.isProxyOwnerHealthy(ownerFile.owner.pid)) {
189692
189819
  return null;
@@ -189780,7 +189907,7 @@ var ProxyRegistryManager = class {
189780
189907
  */
189781
189908
  watchRegistry(onChange) {
189782
189909
  this.ensureProxyDir();
189783
- if (!fs20.existsSync(this.registryPath)) {
189910
+ if (!fs21.existsSync(this.registryPath)) {
189784
189911
  const emptyRegistry = {
189785
189912
  version: 1,
189786
189913
  keys: {},
@@ -189823,13 +189950,13 @@ var ProxyRegistryManager = class {
189823
189950
  async attemptElection(key) {
189824
189951
  const releaseLock = await this.acquireLock();
189825
189952
  try {
189826
- if (fs20.existsSync(this.ownerPath)) {
189827
- const content = fs20.readFileSync(this.ownerPath, "utf-8");
189953
+ if (fs21.existsSync(this.ownerPath)) {
189954
+ const content = fs21.readFileSync(this.ownerPath, "utf-8");
189828
189955
  const ownerFile2 = JSON.parse(content);
189829
189956
  if (await this.isProxyOwnerHealthy(ownerFile2.owner.pid)) {
189830
189957
  return false;
189831
189958
  }
189832
- fs20.unlinkSync(this.ownerPath);
189959
+ fs21.unlinkSync(this.ownerPath);
189833
189960
  }
189834
189961
  const ownerFile = {
189835
189962
  version: 1,
@@ -189847,7 +189974,7 @@ var ProxyRegistryManager = class {
189847
189974
  }
189848
189975
  }
189849
189976
  readRegistry() {
189850
- if (!fs20.existsSync(this.registryPath)) {
189977
+ if (!fs21.existsSync(this.registryPath)) {
189851
189978
  return {
189852
189979
  version: 1,
189853
189980
  keys: {},
@@ -189855,7 +189982,7 @@ var ProxyRegistryManager = class {
189855
189982
  };
189856
189983
  }
189857
189984
  try {
189858
- const content = fs20.readFileSync(this.registryPath, "utf-8");
189985
+ const content = fs21.readFileSync(this.registryPath, "utf-8");
189859
189986
  return JSON.parse(content);
189860
189987
  } catch {
189861
189988
  return {
@@ -189868,8 +189995,8 @@ var ProxyRegistryManager = class {
189868
189995
  writeFileAtomic(filePath, data) {
189869
189996
  this.ensureProxyDir();
189870
189997
  const tmpPath = filePath + ".tmp";
189871
- fs20.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
189872
- fs20.renameSync(tmpPath, filePath);
189998
+ fs21.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
189999
+ fs21.renameSync(tmpPath, filePath);
189873
190000
  }
189874
190001
  };
189875
190002
 
@@ -189973,10 +190100,10 @@ var COLORS = ["cyan", "yellow", "green", "magenta", "blue"];
189973
190100
  function DevUI({ instanceKey, tunnelEnabled }) {
189974
190101
  const { exit } = useApp2();
189975
190102
  const [state, setState] = useState5(() => {
189976
- const caExists = tunnelEnabled || caFilesExist();
190103
+ const setupDone = tunnelEnabled || !systemSetupNeeded();
189977
190104
  return {
189978
- status: caExists ? "loading" : "installing-ca",
189979
- ...caExists ? {} : { caInstallPhase: "installing" },
190105
+ status: setupDone ? "loading" : "installing-ca",
190106
+ ...setupDone ? {} : { caInstallPhase: "installing" },
189980
190107
  resources: /* @__PURE__ */ new Map(),
189981
190108
  resourceStatus: /* @__PURE__ */ new Map(),
189982
190109
  services: [],
@@ -189989,18 +190116,15 @@ function DevUI({ instanceKey, tunnelEnabled }) {
189989
190116
  });
189990
190117
  useEffect3(() => {
189991
190118
  if (state.status === "installing-ca" && state.caInstallPhase === "installing") {
189992
- installCA();
190119
+ installSystemConfig();
189993
190120
  }
189994
190121
  }, [state.status, state.caInstallPhase]);
189995
- async function installCA() {
190122
+ async function installSystemConfig() {
189996
190123
  try {
189997
- const { key, cert } = generateRootCA();
189998
- const certPath = saveCA(key, cert);
189999
- installCAToTrustStore(certPath);
190124
+ performSystemSetup();
190000
190125
  setState((s) => ({ ...s, status: "loading", caInstallPhase: "done" }));
190001
190126
  setReadyToStart(true);
190002
190127
  } catch (err) {
190003
- removeCA();
190004
190128
  setState((s) => ({
190005
190129
  ...s,
190006
190130
  caInstallPhase: "error",
@@ -190018,13 +190142,14 @@ function DevUI({ instanceKey, tunnelEnabled }) {
190018
190142
  const registryWatcherCleanupRef = useRef(null);
190019
190143
  const electionIntervalRef = useRef(null);
190020
190144
  const tunnelsRef = useRef([]);
190145
+ const dnsServerRef = useRef(null);
190021
190146
  const proxyRef = useRef(null);
190022
190147
  const adminServerRef = useRef(null);
190023
190148
  const servicesRef = useRef([]);
190024
190149
  const resourcesRef = useRef(/* @__PURE__ */ new Map());
190025
190150
  const restartServicesRef = useRef(null);
190026
190151
  const [reloadTrigger, setReloadTrigger] = useState5(0);
190027
- const [readyToStart, setReadyToStart] = useState5(() => tunnelEnabled || caFilesExist());
190152
+ const [readyToStart, setReadyToStart] = useState5(() => tunnelEnabled || !systemSetupNeeded());
190028
190153
  const shutdown2 = async () => {
190029
190154
  if (shuttingDown.current) return;
190030
190155
  shuttingDown.current = true;
@@ -190043,6 +190168,8 @@ function DevUI({ instanceKey, tunnelEnabled }) {
190043
190168
  await Promise.all([
190044
190169
  // Stop proxy
190045
190170
  proxyRef.current?.stop(),
190171
+ // Stop DNS server
190172
+ dnsServerRef.current?.stop(),
190046
190173
  // Stop admin server
190047
190174
  adminServerRef.current?.stop(),
190048
190175
  // Stop all services
@@ -190086,6 +190213,8 @@ function DevUI({ instanceKey, tunnelEnabled }) {
190086
190213
  await Promise.all([
190087
190214
  // Stop proxy
190088
190215
  proxyRef.current?.stop(),
190216
+ // Stop DNS server
190217
+ dnsServerRef.current?.stop(),
190089
190218
  // Stop admin server
190090
190219
  adminServerRef.current?.stop(),
190091
190220
  // Stop all services
@@ -190106,6 +190235,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
190106
190235
  restartServicesRef.current = null;
190107
190236
  drizzleGatewayRef.current = null;
190108
190237
  proxyRef.current = null;
190238
+ dnsServerRef.current = null;
190109
190239
  adminServerRef.current = null;
190110
190240
  servicesRef.current = [];
190111
190241
  resourcesRef.current = /* @__PURE__ */ new Map();
@@ -190137,6 +190267,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
190137
190267
  writeLog("system", "Force shutting down");
190138
190268
  const allProcesses = [
190139
190269
  proxyRef.current,
190270
+ dnsServerRef.current,
190140
190271
  ...servicesRef.current,
190141
190272
  ...electricInstancesRef.current,
190142
190273
  drizzleGatewayRef.current,
@@ -190178,10 +190309,10 @@ function DevUI({ instanceKey, tunnelEnabled }) {
190178
190309
  }, [state.status]);
190179
190310
  useEffect3(() => {
190180
190311
  if (state.status !== "running") return;
190181
- const configPath = path18.join(process.cwd(), "specific.hcl");
190312
+ const configPath = path19.join(process.cwd(), "specific.hcl");
190182
190313
  const watcher = watchConfigFile(configPath, 1e3, () => {
190183
190314
  try {
190184
- const hcl = fs21.readFileSync(configPath, "utf-8");
190315
+ const hcl = fs22.readFileSync(configPath, "utf-8");
190185
190316
  parseConfig(hcl).then(() => {
190186
190317
  triggerReload();
190187
190318
  }).catch((err) => {
@@ -190306,8 +190437,8 @@ function DevUI({ instanceKey, tunnelEnabled }) {
190306
190437
  }));
190307
190438
  return;
190308
190439
  }
190309
- const configPath = path18.join(process.cwd(), "specific.hcl");
190310
- if (!fs21.existsSync(configPath)) {
190440
+ const configPath = path19.join(process.cwd(), "specific.hcl");
190441
+ if (!fs22.existsSync(configPath)) {
190311
190442
  writeLog("system", "Waiting for specific.hcl to appear");
190312
190443
  setState((s) => ({
190313
190444
  ...s,
@@ -190326,7 +190457,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
190326
190457
  }
190327
190458
  let config2;
190328
190459
  try {
190329
- const hcl = fs21.readFileSync(configPath, "utf-8");
190460
+ const hcl = fs22.readFileSync(configPath, "utf-8");
190330
190461
  config2 = await parseConfig(hcl);
190331
190462
  } catch (err) {
190332
190463
  setState((s) => ({
@@ -190432,7 +190563,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
190432
190563
  const drizzleGateway = await startDrizzleGateway(
190433
190564
  postgresResources,
190434
190565
  drizzlePort,
190435
- path18.join(process.cwd(), ".specific", "keys", instanceKey)
190566
+ path19.join(process.cwd(), ".specific", "keys", instanceKey)
190436
190567
  );
190437
190568
  startedDrizzleGateway = drizzleGateway;
190438
190569
  drizzleGatewayRef.current = drizzleGateway;
@@ -190452,7 +190583,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
190452
190583
  if (pg.reshape?.enabled) {
190453
190584
  const resource = resources2.get(pg.name);
190454
190585
  if (!resource) continue;
190455
- const migrationsDir = path18.resolve(
190586
+ const migrationsDir = path19.resolve(
190456
190587
  process.cwd(),
190457
190588
  pg.reshape.migrations_dir ?? "migrations"
190458
190589
  );
@@ -190620,10 +190751,10 @@ Add them to the config block in specific.local`);
190620
190751
  }
190621
190752
  const services2 = [];
190622
190753
  function resolveServiceCwd(service) {
190623
- if (service.root) return path18.resolve(process.cwd(), service.root);
190754
+ if (service.root) return path19.resolve(process.cwd(), service.root);
190624
190755
  if (service.build) {
190625
190756
  const build = config2.builds.find((b) => b.name === service.build.name);
190626
- if (build?.root) return path18.resolve(process.cwd(), build.root);
190757
+ if (build?.root) return path19.resolve(process.cwd(), build.root);
190627
190758
  }
190628
190759
  return process.cwd();
190629
190760
  }
@@ -190814,8 +190945,10 @@ Add them to the config block in specific.local`);
190814
190945
  writeLog("system", `Registered ${serviceInfos.length} services with proxy registry`);
190815
190946
  const becameProxyOwner = await proxyRegistry.claimProxyOwnership(instanceKey);
190816
190947
  if (becameProxyOwner) {
190817
- writeLog("system", "Claimed proxy ownership, starting HTTP proxy");
190948
+ writeLog("system", "Claimed proxy ownership, starting HTTP proxy and DNS server");
190818
190949
  try {
190950
+ const dnsServer = await startDnsServer();
190951
+ dnsServerRef.current = dnsServer;
190819
190952
  const currentServices = await proxyRegistry.getAllServices();
190820
190953
  const registeredKeys = [...new Set(currentServices.map((s) => s.key))];
190821
190954
  const certificate = generateCertificate("local.spcf.app", registeredKeys);
@@ -190880,12 +191013,14 @@ Add them to the config block in specific.local`);
190880
191013
  writeLog("system", "Proxy owner died, attempting election");
190881
191014
  const won = await proxyRegistry.attemptElection(instanceKey);
190882
191015
  if (won) {
190883
- writeLog("system", "Won election, starting HTTP proxy");
191016
+ writeLog("system", "Won election, starting HTTP proxy and DNS server");
190884
191017
  if (electionIntervalRef.current) {
190885
191018
  clearInterval(electionIntervalRef.current);
190886
191019
  electionIntervalRef.current = null;
190887
191020
  }
190888
191021
  try {
191022
+ const dnsServer = await startDnsServer();
191023
+ dnsServerRef.current = dnsServer;
190889
191024
  const electionServices = await proxyRegistry.getAllServices();
190890
191025
  const electionKeyRegistrations = await proxyRegistry.getAllKeyRegistrations();
190891
191026
  const electionKeyNames = Object.keys(electionKeyRegistrations);
@@ -190931,6 +191066,8 @@ Add them to the config block in specific.local`);
190931
191066
  startedProxy.stop().catch(() => {
190932
191067
  });
190933
191068
  }
191069
+ dnsServerRef.current?.stop().catch(() => {
191070
+ });
190934
191071
  for (const service of startedServices) {
190935
191072
  service.stop().catch(() => {
190936
191073
  });
@@ -190963,10 +191100,10 @@ Add them to the config block in specific.local`);
190963
191100
  }, [reloadTrigger, readyToStart, instanceKey]);
190964
191101
  if (state.status === "installing-ca") {
190965
191102
  if (state.caInstallPhase === "installing") {
190966
- return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "TLS Certificate Setup"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Installing a local certificate authority (CA) to enable HTTPS"), /* @__PURE__ */ React6.createElement(Text6, null, "for local development. The CA is limited to Specific projects."), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Your password is required to add the CA to your system's trust store."), /* @__PURE__ */ React6.createElement(Text6, null, " "));
191103
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Local Development Setup"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Setting up TLS certificates and DNS resolution for local"), /* @__PURE__ */ React6.createElement(Text6, null, "development. This is a one-time setup for Specific projects."), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Your password is required to configure your system."), /* @__PURE__ */ React6.createElement(Text6, null, " "));
190967
191104
  }
190968
191105
  if (state.caInstallPhase === "error") {
190969
- return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Failed to install CA: ", state.caError));
191106
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Setup failed: ", state.caError));
190970
191107
  }
190971
191108
  }
190972
191109
  if (state.status === "loading") {
@@ -191166,20 +191303,20 @@ function devCommand(instanceKey, tunnelEnabled = false) {
191166
191303
  }
191167
191304
 
191168
191305
  // src/lib/dev/git-worktree.ts
191169
- import { execSync as execSync2 } from "child_process";
191170
- import * as path19 from "path";
191306
+ import { execSync as execSync3 } from "child_process";
191307
+ import * as path20 from "path";
191171
191308
  function isInWorktree() {
191172
191309
  try {
191173
- const commonDir = execSync2("git rev-parse --git-common-dir", {
191310
+ const commonDir = execSync3("git rev-parse --git-common-dir", {
191174
191311
  encoding: "utf-8",
191175
191312
  stdio: ["pipe", "pipe", "pipe"]
191176
191313
  }).trim();
191177
- const gitDir = execSync2("git rev-parse --git-dir", {
191314
+ const gitDir = execSync3("git rev-parse --git-dir", {
191178
191315
  encoding: "utf-8",
191179
191316
  stdio: ["pipe", "pipe", "pipe"]
191180
191317
  }).trim();
191181
- const resolvedCommonDir = path19.resolve(commonDir);
191182
- const resolvedGitDir = path19.resolve(gitDir);
191318
+ const resolvedCommonDir = path20.resolve(commonDir);
191319
+ const resolvedGitDir = path20.resolve(gitDir);
191183
191320
  return resolvedCommonDir !== resolvedGitDir;
191184
191321
  } catch {
191185
191322
  return false;
@@ -191190,11 +191327,11 @@ function getWorktreeName() {
191190
191327
  return null;
191191
191328
  }
191192
191329
  try {
191193
- const gitDir = execSync2("git rev-parse --git-dir", {
191330
+ const gitDir = execSync3("git rev-parse --git-dir", {
191194
191331
  encoding: "utf-8",
191195
191332
  stdio: ["pipe", "pipe", "pipe"]
191196
191333
  }).trim();
191197
- return path19.basename(gitDir);
191334
+ return path20.basename(gitDir);
191198
191335
  } catch {
191199
191336
  return null;
191200
191337
  }
@@ -191209,36 +191346,36 @@ init_open();
191209
191346
  import React7, { useState as useState6, useEffect as useEffect4, useCallback } from "react";
191210
191347
  import { render as render5, Text as Text7, Box as Box7, useApp as useApp3, useInput as useInput5 } from "ink";
191211
191348
  import Spinner5 from "ink-spinner";
191212
- import * as fs23 from "fs";
191213
- import * as path21 from "path";
191349
+ import * as fs24 from "fs";
191350
+ import * as path22 from "path";
191214
191351
 
191215
191352
  // src/lib/deploy/build-tester.ts
191216
191353
  import { spawn as spawn5 } from "child_process";
191217
- import { existsSync as existsSync20 } from "fs";
191218
- import { join as join21, resolve as resolve7 } from "path";
191354
+ import { existsSync as existsSync21 } from "fs";
191355
+ import { join as join22, resolve as resolve7 } from "path";
191219
191356
  function getDependencyInstallCommand(build, projectDir) {
191220
191357
  switch (build.base) {
191221
191358
  case "node":
191222
- if (existsSync20(join21(projectDir, "pnpm-lock.yaml"))) {
191359
+ if (existsSync21(join22(projectDir, "pnpm-lock.yaml"))) {
191223
191360
  return "pnpm install --frozen-lockfile";
191224
- } else if (existsSync20(join21(projectDir, "yarn.lock"))) {
191361
+ } else if (existsSync21(join22(projectDir, "yarn.lock"))) {
191225
191362
  return "yarn install --frozen-lockfile";
191226
- } else if (existsSync20(join21(projectDir, "package-lock.json"))) {
191363
+ } else if (existsSync21(join22(projectDir, "package-lock.json"))) {
191227
191364
  return "npm ci";
191228
- } else if (existsSync20(join21(projectDir, "package.json"))) {
191365
+ } else if (existsSync21(join22(projectDir, "package.json"))) {
191229
191366
  return "npm install";
191230
191367
  }
191231
191368
  return null;
191232
191369
  case "python":
191233
- if (existsSync20(join21(projectDir, "poetry.lock"))) {
191370
+ if (existsSync21(join22(projectDir, "poetry.lock"))) {
191234
191371
  return "poetry install --no-interaction";
191235
- } else if (existsSync20(join21(projectDir, "Pipfile.lock"))) {
191372
+ } else if (existsSync21(join22(projectDir, "Pipfile.lock"))) {
191236
191373
  return "pipenv install --deploy";
191237
- } else if (existsSync20(join21(projectDir, "Pipfile"))) {
191374
+ } else if (existsSync21(join22(projectDir, "Pipfile"))) {
191238
191375
  return "pipenv install";
191239
- } else if (existsSync20(join21(projectDir, "pyproject.toml"))) {
191376
+ } else if (existsSync21(join22(projectDir, "pyproject.toml"))) {
191240
191377
  return "pip install .";
191241
- } else if (existsSync20(join21(projectDir, "requirements.txt"))) {
191378
+ } else if (existsSync21(join22(projectDir, "requirements.txt"))) {
191242
191379
  return "pip install -r requirements.txt";
191243
191380
  }
191244
191381
  return null;
@@ -191383,13 +191520,13 @@ async function testAllBuilds(builds, projectDir) {
191383
191520
  }
191384
191521
 
191385
191522
  // src/lib/tarball/create.ts
191386
- import { execSync as execSync3 } from "child_process";
191387
- import * as fs22 from "fs";
191388
- import * as path20 from "path";
191523
+ import { execSync as execSync4 } from "child_process";
191524
+ import * as fs23 from "fs";
191525
+ import * as path21 from "path";
191389
191526
  import { createTarPacker, createEntryItemGenerator } from "tar-vern";
191390
191527
  function isInsideGitRepository(dir) {
191391
191528
  try {
191392
- const result = execSync3("git rev-parse --is-inside-work-tree", {
191529
+ const result = execSync4("git rev-parse --is-inside-work-tree", {
191393
191530
  cwd: dir,
191394
191531
  encoding: "utf-8",
191395
191532
  stdio: ["pipe", "pipe", "pipe"]
@@ -191401,7 +191538,7 @@ function isInsideGitRepository(dir) {
191401
191538
  }
191402
191539
  async function createGitArchive(projectDir) {
191403
191540
  writeLog("tarball", "Creating tarball using git ls-files");
191404
- const filesOutput = execSync3(
191541
+ const filesOutput = execSync4(
191405
191542
  "git ls-files --cached --others --exclude-standard",
191406
191543
  {
191407
191544
  cwd: projectDir,
@@ -191442,10 +191579,10 @@ var EXCLUDED_DIRS = [
191442
191579
  ];
191443
191580
  async function collectPaths(baseDir, currentDir, exclude) {
191444
191581
  const results = [];
191445
- const entries = await fs22.promises.readdir(currentDir, { withFileTypes: true });
191582
+ const entries = await fs23.promises.readdir(currentDir, { withFileTypes: true });
191446
191583
  for (const entry of entries) {
191447
- const fullPath = path20.join(currentDir, entry.name);
191448
- const relativePath = path20.relative(baseDir, fullPath);
191584
+ const fullPath = path21.join(currentDir, entry.name);
191585
+ const relativePath = path21.relative(baseDir, fullPath);
191449
191586
  if (entry.isDirectory()) {
191450
191587
  if (!exclude.includes(entry.name)) {
191451
191588
  results.push(relativePath);
@@ -191460,8 +191597,8 @@ async function collectPaths(baseDir, currentDir, exclude) {
191460
191597
  }
191461
191598
  async function createTarArchive(projectDir) {
191462
191599
  writeLog("tarball", "Creating tarball using tar-vern (non-git project)");
191463
- const configPath = path20.join(projectDir, "specific.hcl");
191464
- if (!fs22.existsSync(configPath)) {
191600
+ const configPath = path21.join(projectDir, "specific.hcl");
191601
+ if (!fs23.existsSync(configPath)) {
191465
191602
  throw new Error("specific.hcl not found in project directory");
191466
191603
  }
191467
191604
  const relativePaths = await collectPaths(projectDir, projectDir, EXCLUDED_DIRS);
@@ -191478,8 +191615,8 @@ async function createTarArchive(projectDir) {
191478
191615
  }
191479
191616
  function findWidestContext(projectDir, contexts) {
191480
191617
  if (contexts.length === 0) return ".";
191481
- const absolute = contexts.map((c) => path20.resolve(projectDir, c));
191482
- const segments = absolute.map((p) => p.split(path20.sep).filter(Boolean));
191618
+ const absolute = contexts.map((c) => path21.resolve(projectDir, c));
191619
+ const segments = absolute.map((p) => p.split(path21.sep).filter(Boolean));
191483
191620
  const firstSegments = segments[0];
191484
191621
  if (!firstSegments) return ".";
191485
191622
  const minLen = Math.min(...segments.map((s) => s.length));
@@ -191493,12 +191630,12 @@ function findWidestContext(projectDir, contexts) {
191493
191630
  }
191494
191631
  }
191495
191632
  const ancestorSegments = firstSegments.slice(0, commonLength);
191496
- const ancestor = path20.sep + ancestorSegments.join(path20.sep);
191497
- return path20.relative(projectDir, ancestor) || ".";
191633
+ const ancestor = path21.sep + ancestorSegments.join(path21.sep);
191634
+ return path21.relative(projectDir, ancestor) || ".";
191498
191635
  }
191499
191636
  async function createProjectTarball(projectDir, context = ".") {
191500
- const contextDir = path20.resolve(projectDir, context);
191501
- const appPath = path20.relative(contextDir, projectDir) || ".";
191637
+ const contextDir = path21.resolve(projectDir, context);
191638
+ const appPath = path21.relative(contextDir, projectDir) || ".";
191502
191639
  writeLog("tarball", `Context: ${contextDir}, appPath: ${appPath}`);
191503
191640
  let tarball;
191504
191641
  if (isInsideGitRepository(contextDir)) {
@@ -192385,14 +192522,14 @@ ${errorMsg}`
192385
192522
  ), phase === "error" && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { color: "red" }, "Error: ", error)), 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))))));
192386
192523
  }
192387
192524
  async function deployCommand(environment, options2) {
192388
- const configPath = path21.join(process.cwd(), "specific.hcl");
192389
- if (!fs23.existsSync(configPath)) {
192525
+ const configPath = path22.join(process.cwd(), "specific.hcl");
192526
+ if (!fs24.existsSync(configPath)) {
192390
192527
  console.error("Error: No specific.hcl found in current directory");
192391
192528
  process.exit(1);
192392
192529
  }
192393
192530
  let config;
192394
192531
  try {
192395
- const hcl = fs23.readFileSync(configPath, "utf-8");
192532
+ const hcl = fs24.readFileSync(configPath, "utf-8");
192396
192533
  config = await parseConfig(hcl);
192397
192534
  } catch (err) {
192398
192535
  console.error(
@@ -192416,8 +192553,8 @@ async function deployCommand(environment, options2) {
192416
192553
 
192417
192554
  // src/commands/exec.tsx
192418
192555
  import { spawn as spawn6 } from "child_process";
192419
- import * as fs24 from "fs";
192420
- import * as path22 from "path";
192556
+ import * as fs25 from "fs";
192557
+ import * as path23 from "path";
192421
192558
  async function execCommand(serviceName, command, instanceKey = "default") {
192422
192559
  if (command.length === 0) {
192423
192560
  console.error(
@@ -192445,14 +192582,14 @@ async function execCommand(serviceName, command, instanceKey = "default") {
192445
192582
  }
192446
192583
  }
192447
192584
  };
192448
- const configPath = path22.join(process.cwd(), "specific.hcl");
192449
- if (!fs24.existsSync(configPath)) {
192585
+ const configPath = path23.join(process.cwd(), "specific.hcl");
192586
+ if (!fs25.existsSync(configPath)) {
192450
192587
  console.error("Error: No specific.hcl found in current directory");
192451
192588
  process.exit(1);
192452
192589
  }
192453
192590
  let config;
192454
192591
  try {
192455
- const hcl = fs24.readFileSync(configPath, "utf-8");
192592
+ const hcl = fs25.readFileSync(configPath, "utf-8");
192456
192593
  config = await parseConfig(hcl);
192457
192594
  } catch (err) {
192458
192595
  console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
@@ -192568,11 +192705,11 @@ async function execCommand(serviceName, command, instanceKey = "default") {
192568
192705
  process.on("SIGTERM", () => handleSignal("SIGTERM"));
192569
192706
  let effectiveCwd = process.cwd();
192570
192707
  if (service.root) {
192571
- effectiveCwd = path22.resolve(process.cwd(), service.root);
192708
+ effectiveCwd = path23.resolve(process.cwd(), service.root);
192572
192709
  } else if (service.build) {
192573
192710
  const build = config.builds.find((b) => b.name === service.build.name);
192574
192711
  if (build?.root) {
192575
- effectiveCwd = path22.resolve(process.cwd(), build.root);
192712
+ effectiveCwd = path23.resolve(process.cwd(), build.root);
192576
192713
  }
192577
192714
  }
192578
192715
  child = spawn6(command[0], command.slice(1), {
@@ -192600,8 +192737,8 @@ async function execCommand(serviceName, command, instanceKey = "default") {
192600
192737
 
192601
192738
  // src/commands/psql.tsx
192602
192739
  import { spawn as spawn7 } from "child_process";
192603
- import * as fs25 from "fs";
192604
- import * as path23 from "path";
192740
+ import * as fs26 from "fs";
192741
+ import * as path24 from "path";
192605
192742
  async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []) {
192606
192743
  let startedResources = [];
192607
192744
  let ownsInstances = false;
@@ -192618,14 +192755,14 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
192618
192755
  }
192619
192756
  }
192620
192757
  };
192621
- const configPath = path23.join(process.cwd(), "specific.hcl");
192622
- if (!fs25.existsSync(configPath)) {
192758
+ const configPath = path24.join(process.cwd(), "specific.hcl");
192759
+ if (!fs26.existsSync(configPath)) {
192623
192760
  console.error("Error: No specific.hcl found in current directory");
192624
192761
  process.exit(1);
192625
192762
  }
192626
192763
  let config;
192627
192764
  try {
192628
- const hcl = fs25.readFileSync(configPath, "utf-8");
192765
+ const hcl = fs26.readFileSync(configPath, "utf-8");
192629
192766
  config = await parseConfig(hcl);
192630
192767
  } catch (err) {
192631
192768
  console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
@@ -192747,8 +192884,8 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
192747
192884
 
192748
192885
  // src/commands/reshape.tsx
192749
192886
  import { spawn as spawn8 } from "child_process";
192750
- import * as fs26 from "fs";
192751
- import * as path24 from "path";
192887
+ import * as fs27 from "fs";
192888
+ import * as path25 from "path";
192752
192889
  var VALID_ACTIONS = ["start", "complete", "status", "abort", "check"];
192753
192890
  var MIGRATION_SUBCOMMANDS = ["start", "complete", "abort"];
192754
192891
  var OFFLINE_ACTIONS = ["check"];
@@ -192760,13 +192897,13 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
192760
192897
  process.exit(1);
192761
192898
  }
192762
192899
  const isOfflineAction = OFFLINE_ACTIONS.includes(action);
192763
- const configPath = path24.join(process.cwd(), "specific.hcl");
192900
+ const configPath = path25.join(process.cwd(), "specific.hcl");
192764
192901
  let config;
192765
192902
  let migrationsDir = "migrations";
192766
192903
  let targetDb;
192767
192904
  try {
192768
- if (fs26.existsSync(configPath)) {
192769
- const configContent = fs26.readFileSync(configPath, "utf-8");
192905
+ if (fs27.existsSync(configPath)) {
192906
+ const configContent = fs27.readFileSync(configPath, "utf-8");
192770
192907
  config = await parseConfig(configContent);
192771
192908
  if (databaseName) {
192772
192909
  const postgresConfig = config.postgres.find((p) => p.name === databaseName);
@@ -192899,9 +193036,9 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
192899
193036
  }
192900
193037
  const isMigrationSubcommand = MIGRATION_SUBCOMMANDS.includes(action);
192901
193038
  const reshapeArgs = isMigrationSubcommand ? ["migration", action] : [action];
192902
- const fullMigrationsPath = path24.join(process.cwd(), migrationsDir);
193039
+ const fullMigrationsPath = path25.join(process.cwd(), migrationsDir);
192903
193040
  if (action === "check" || action === "start") {
192904
- if (fs26.existsSync(fullMigrationsPath)) {
193041
+ if (fs27.existsSync(fullMigrationsPath)) {
192905
193042
  reshapeArgs.push("--dirs", fullMigrationsPath);
192906
193043
  } else if (action === "check") {
192907
193044
  console.error(`Error: Migrations directory not found: ${fullMigrationsPath}`);
@@ -192951,21 +193088,21 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
192951
193088
  import React8, { useState as useState7, useEffect as useEffect5 } from "react";
192952
193089
  import { render as render6, Text as Text8, Box as Box8 } from "ink";
192953
193090
  import Spinner6 from "ink-spinner";
192954
- import * as fs27 from "fs";
192955
- import * as path25 from "path";
193091
+ import * as fs28 from "fs";
193092
+ import * as path26 from "path";
192956
193093
  function CleanUI({ instanceKey }) {
192957
193094
  const [state, setState] = useState7({ status: "checking" });
192958
193095
  useEffect5(() => {
192959
193096
  async function clean() {
192960
193097
  const projectRoot = process.cwd();
192961
- const specificDir = path25.join(projectRoot, ".specific");
192962
- if (!fs27.existsSync(specificDir)) {
193098
+ const specificDir = path26.join(projectRoot, ".specific");
193099
+ if (!fs28.existsSync(specificDir)) {
192963
193100
  setState({ status: "nothing" });
192964
193101
  return;
192965
193102
  }
192966
193103
  if (instanceKey) {
192967
- const keyDir = path25.join(specificDir, "keys", instanceKey);
192968
- if (!fs27.existsSync(keyDir)) {
193104
+ const keyDir = path26.join(specificDir, "keys", instanceKey);
193105
+ if (!fs28.existsSync(keyDir)) {
192969
193106
  setState({ status: "nothing" });
192970
193107
  return;
192971
193108
  }
@@ -192981,7 +193118,7 @@ function CleanUI({ instanceKey }) {
192981
193118
  await stateManager.cleanStaleState();
192982
193119
  setState({ status: "cleaning" });
192983
193120
  try {
192984
- fs27.rmSync(keyDir, { recursive: true, force: true });
193121
+ fs28.rmSync(keyDir, { recursive: true, force: true });
192985
193122
  setState({ status: "success" });
192986
193123
  } catch (err) {
192987
193124
  setState({
@@ -192990,13 +193127,13 @@ function CleanUI({ instanceKey }) {
192990
193127
  });
192991
193128
  }
192992
193129
  } else {
192993
- const keysDir = path25.join(specificDir, "keys");
192994
- if (!fs27.existsSync(keysDir)) {
193130
+ const keysDir = path26.join(specificDir, "keys");
193131
+ if (!fs28.existsSync(keysDir)) {
192995
193132
  setState({ status: "nothing" });
192996
193133
  return;
192997
193134
  }
192998
- const keys = fs27.readdirSync(keysDir).filter(
192999
- (f) => fs27.statSync(path25.join(keysDir, f)).isDirectory()
193135
+ const keys = fs28.readdirSync(keysDir).filter(
193136
+ (f) => fs28.statSync(path26.join(keysDir, f)).isDirectory()
193000
193137
  );
193001
193138
  for (const key of keys) {
193002
193139
  const stateManager2 = new InstanceStateManager(projectRoot, key);
@@ -193020,7 +193157,7 @@ function CleanUI({ instanceKey }) {
193020
193157
  }
193021
193158
  setState({ status: "cleaning" });
193022
193159
  try {
193023
- fs27.rmSync(keysDir, { recursive: true, force: true });
193160
+ fs28.rmSync(keysDir, { recursive: true, force: true });
193024
193161
  setState({ status: "success" });
193025
193162
  } catch (err) {
193026
193163
  setState({
@@ -193183,7 +193320,7 @@ function betaCommand() {
193183
193320
  var program = new Command();
193184
193321
  var env = "production";
193185
193322
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
193186
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.60").enablePositionalOptions();
193323
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.62").enablePositionalOptions();
193187
193324
  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));
193188
193325
  program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
193189
193326
  program.command("check").description("Validate specific.hcl configuration").action(checkCommand);