@specific.dev/cli 0.1.81 → 0.1.83

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 (70) 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/admin/mail/__next.!KGRlZmF1bHQp.mail.__PAGE__.txt +1 -1
  46. package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.txt +1 -1
  47. package/dist/admin/mail/__next.!KGRlZmF1bHQp.txt +1 -1
  48. package/dist/admin/mail/__next._full.txt +1 -1
  49. package/dist/admin/mail/__next._head.txt +1 -1
  50. package/dist/admin/mail/__next._index.txt +1 -1
  51. package/dist/admin/mail/__next._tree.txt +1 -1
  52. package/dist/admin/mail/index.html +1 -1
  53. package/dist/admin/mail/index.txt +1 -1
  54. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.txt +1 -1
  55. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.__PAGE__.txt +1 -1
  56. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.txt +1 -1
  57. package/dist/admin/workflows/__next._full.txt +1 -1
  58. package/dist/admin/workflows/__next._head.txt +1 -1
  59. package/dist/admin/workflows/__next._index.txt +1 -1
  60. package/dist/admin/workflows/__next._tree.txt +1 -1
  61. package/dist/admin/workflows/index.html +1 -1
  62. package/dist/admin/workflows/index.txt +1 -1
  63. package/dist/cli.js +494 -323
  64. package/dist/docs/integrations/drizzle.md +5 -3
  65. package/dist/docs/postgres.md +7 -0
  66. package/dist/postinstall.js +1 -1
  67. package/package.json +1 -1
  68. /package/dist/admin/_next/static/{z1dgCOMTsxqy8ZR3zdNfr → ZUhVyT91xXNDSM1fLwCBF}/_buildManifest.js +0 -0
  69. /package/dist/admin/_next/static/{z1dgCOMTsxqy8ZR3zdNfr → ZUhVyT91xXNDSM1fLwCBF}/_clientMiddlewareManifest.json +0 -0
  70. /package/dist/admin/_next/static/{z1dgCOMTsxqy8ZR3zdNfr → ZUhVyT91xXNDSM1fLwCBF}/_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 fs5 from "node:fs";
42
+ import fs6 from "node:fs";
43
43
  function hasDockerEnv() {
44
44
  try {
45
- fs5.statSync("/.dockerenv");
45
+ fs6.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 fs5.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
53
+ return fs6.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 fs6 from "node:fs";
71
+ import fs7 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
- fs6.statSync("/run/.containerenv");
84
+ fs7.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 os4 from "node:os";
96
- import fs7 from "node:fs";
95
+ import os5 from "node:os";
96
+ import fs8 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 (os4.release().toLowerCase().includes("microsoft")) {
105
+ if (os5.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 fs7.readFileSync("/proc/version", "utf8").toLowerCase().includes("microsoft") ? !isInsideContainer() : false;
112
+ return fs8.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 fs8, { constants as fsConstants } from "node:fs/promises";
182
+ import fs9, { 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 fs8.access(configFilePath, fsConstants.F_OK);
201
+ await fs9.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 fs8.readFile(configFilePath, { encoding: "utf8" });
208
+ const configContent = await fs9.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 fs8.access(psPath, fsConstants.X_OK);
227
+ await fs9.access(psPath, fsConstants.X_OK);
228
228
  return true;
229
229
  } catch {
230
230
  return false;
@@ -435,7 +435,7 @@ import process8 from "node:process";
435
435
  import path5 from "node:path";
436
436
  import { fileURLToPath } from "node:url";
437
437
  import childProcess3 from "node:child_process";
438
- import fs9, { constants as fsConstants2 } from "node:fs/promises";
438
+ import fs10, { 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({ [platform3]: platformBinary }, { wsl } = {}) {
449
+ function detectPlatformBinary({ [platform4]: platformBinary }, { wsl } = {}) {
450
450
  if (wsl && is_wsl_default) {
451
451
  return detectArchBinary(wsl);
452
452
  }
453
453
  if (!platformBinary) {
454
- throw new Error(`${platform3} is not supported`);
454
+ throw new Error(`${platform4} is not supported`);
455
455
  }
456
456
  return detectArchBinary(platformBinary);
457
457
  }
458
- var fallbackAttemptSymbol, __dirname, localXdgOpenPath, platform3, arch, tryEachApp, baseOpen, open, openApp, apps, open_default;
458
+ var fallbackAttemptSymbol, __dirname, localXdgOpenPath, platform4, 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();
@@ -467,7 +467,7 @@ var init_open = __esm({
467
467
  fallbackAttemptSymbol = Symbol("fallbackAttempt");
468
468
  __dirname = import.meta.url ? path5.dirname(fileURLToPath(import.meta.url)) : "";
469
469
  localXdgOpenPath = path5.join(__dirname, "xdg-open");
470
- ({ platform: platform3, arch } = process8);
470
+ ({ platform: platform4, 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 (platform3 === "darwin") {
567
+ if (platform4 === "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 (platform3 === "win32" || shouldUseWindowsInWsl) {
581
+ } else if (platform4 === "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 fs9.access(localXdgOpenPath, fsConstants2.X_OK);
617
+ await fs10.access(localXdgOpenPath, fsConstants2.X_OK);
618
618
  exeLocalXdgOpen = true;
619
619
  } catch {
620
620
  }
621
- const useSystemXdgOpen = process8.versions.electron ?? (platform3 === "android" || isBundled || !exeLocalXdgOpen);
621
+ const useSystemXdgOpen = process8.versions.electron ?? (platform4 === "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 (platform3 === "darwin" && appArguments.length > 0) {
632
+ if (platform4 === "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 fs32 = $global.require("fs");
758
- "object" == typeof fs32 && null !== fs32 && 0 !== Object.keys(fs32).length && ($global.fs = fs32);
757
+ var fs33 = $global.require("fs");
758
+ "object" == typeof fs33 && null !== fs33 && 0 !== Object.keys(fs33).length && ($global.fs = fs33);
759
759
  } catch (e) {
760
760
  }
761
761
  if (!$global.fs) {
@@ -183430,11 +183430,14 @@ import { Command } from "commander";
183430
183430
  import React2, { useState, useEffect } from "react";
183431
183431
  import { render as render2, Text as Text2, Box as Box2, useInput, useApp } from "ink";
183432
183432
  import "ink-spinner";
183433
- import * as fs11 from "fs";
183433
+ import * as fs12 from "fs";
183434
183434
  import * as path7 from "path";
183435
+ import * as os8 from "os";
183435
183436
 
183436
183437
  // src/lib/dev/system-setup.ts
183437
183438
  import { execSync as execSync2 } from "child_process";
183439
+ import * as fs4 from "fs";
183440
+ import * as os4 from "os";
183438
183441
  import * as path3 from "path";
183439
183442
 
183440
183443
  // src/lib/dev/local-ca.ts
@@ -183470,17 +183473,17 @@ function caInstalledInTrustStore() {
183470
183473
  if (!caFilesExist()) {
183471
183474
  return false;
183472
183475
  }
183473
- const platform5 = os.platform();
183476
+ const platform8 = os.platform();
183474
183477
  const certPath = path.join(getCADir(), "ca.crt");
183475
183478
  const diskCert = fs.readFileSync(certPath, "utf-8").replace(/\r\n/g, "\n").trim();
183476
183479
  try {
183477
- if (platform5 === "darwin") {
183480
+ if (platform8 === "darwin") {
183478
183481
  const keychainCert = execSync(
183479
- 'security find-certificate -c "Specific Local Development CA" -p /Library/Keychains/System.keychain',
183482
+ `security find-certificate -c "Specific Local Development CA" -p "${getLoginKeychainPath()}"`,
183480
183483
  { encoding: "utf-8" }
183481
183484
  ).replace(/\r\n/g, "\n").trim();
183482
183485
  return keychainCert === diskCert;
183483
- } else if (platform5 === "linux") {
183486
+ } else if (platform8 === "linux") {
183484
183487
  const trustPaths = [
183485
183488
  "/usr/local/share/ca-certificates/specific-local-ca.crt",
183486
183489
  // Debian/Ubuntu
@@ -183591,16 +183594,23 @@ function removeCA() {
183591
183594
  fs.unlinkSync(certPath);
183592
183595
  }
183593
183596
  }
183597
+ function getLoginKeychainPath() {
183598
+ return path.join(os.homedir(), "Library", "Keychains", "login.keychain-db");
183599
+ }
183600
+ function getCADeleteCommand() {
183601
+ return `security delete-certificate -c "Specific Local Development CA" "${getLoginKeychainPath()}"`;
183602
+ }
183603
+ function getCATrustCommand(certPath) {
183604
+ return `security add-trusted-cert -d -r trustRoot -k "${getLoginKeychainPath()}" "${certPath}"`;
183605
+ }
183594
183606
  function getCAInstallCommands(certPath) {
183595
- const platform5 = os.platform();
183596
- if (platform5 === "darwin") {
183607
+ const platform8 = os.platform();
183608
+ if (platform8 === "darwin") {
183597
183609
  return [
183598
- // Remove any existing cert with the same CN first — add-trusted-cert
183599
- // silently does nothing if a cert with the same subject already exists.
183600
- 'security delete-certificate -c "Specific Local Development CA" /Library/Keychains/System.keychain 2>/dev/null || true',
183601
- `security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${certPath}"`
183610
+ `${getCADeleteCommand()} 2>/dev/null || true`,
183611
+ getCATrustCommand(certPath)
183602
183612
  ];
183603
- } else if (platform5 === "linux") {
183613
+ } else if (platform8 === "linux") {
183604
183614
  if (fs.existsSync("/usr/local/share/ca-certificates")) {
183605
183615
  return [
183606
183616
  `cp "${certPath}" /usr/local/share/ca-certificates/specific-local-ca.crt`,
@@ -183619,7 +183629,7 @@ function getCAInstallCommands(certPath) {
183619
183629
  }
183620
183630
  throw new Error("Could not detect Linux certificate trust mechanism");
183621
183631
  }
183622
- throw new Error(`Unsupported platform: ${platform5}`);
183632
+ throw new Error(`Unsupported platform: ${platform8}`);
183623
183633
  }
183624
183634
  function generateCertificate(domain, keys = []) {
183625
183635
  const caDir = getCADir();
@@ -183665,23 +183675,23 @@ var RESOLVER_FILE_MACOS = "/etc/resolver/spcf.localhost";
183665
183675
  var OLD_RESOLVER_FILE_MACOS = "/etc/resolver/local.spcf.app";
183666
183676
  var RESOLVER_FILE_LINUX = "/etc/systemd/resolved.conf.d/specific-local.conf";
183667
183677
  function resolverConfigExists() {
183668
- const platform5 = os2.platform();
183669
- if (platform5 === "darwin") {
183678
+ const platform8 = os2.platform();
183679
+ if (platform8 === "darwin") {
183670
183680
  return fs2.existsSync(RESOLVER_FILE_MACOS);
183671
- } else if (platform5 === "linux") {
183681
+ } else if (platform8 === "linux") {
183672
183682
  return fs2.existsSync(RESOLVER_FILE_LINUX);
183673
183683
  }
183674
183684
  return false;
183675
183685
  }
183676
183686
  function getResolverInstallCommands(port) {
183677
- const platform5 = os2.platform();
183678
- if (platform5 === "darwin") {
183687
+ const platform8 = os2.platform();
183688
+ if (platform8 === "darwin") {
183679
183689
  return [
183680
183690
  "mkdir -p /etc/resolver",
183681
183691
  `rm -f ${OLD_RESOLVER_FILE_MACOS}`,
183682
183692
  `printf "nameserver 127.0.0.1\\nport ${port}\\n" > ${RESOLVER_FILE_MACOS}`
183683
183693
  ];
183684
- } else if (platform5 === "linux") {
183694
+ } else if (platform8 === "linux") {
183685
183695
  if (fs2.existsSync("/etc/systemd/resolved.conf.d") || fs2.existsSync("/etc/systemd")) {
183686
183696
  return [
183687
183697
  "mkdir -p /etc/systemd/resolved.conf.d",
@@ -183775,19 +183785,60 @@ async function startDnsServer(port = DNS_PORT) {
183775
183785
 
183776
183786
  // src/lib/dev/system-setup.ts
183777
183787
  function systemSetupNeeded() {
183778
- return !caInstalledInTrustStore() || !resolverConfigExists();
183788
+ return dnsSetupNeeded() || tlsSetupNeeded();
183789
+ }
183790
+ function dnsSetupNeeded() {
183791
+ return !resolverConfigExists();
183792
+ }
183793
+ function tlsSetupNeeded() {
183794
+ return !caInstalledInTrustStore();
183795
+ }
183796
+ function ensureCAGenerated() {
183797
+ if (caFilesExist()) {
183798
+ return false;
183799
+ }
183800
+ const { key, cert } = generateRootCA();
183801
+ saveCA(key, cert);
183802
+ return true;
183803
+ }
183804
+ function performDnsSetup() {
183805
+ const commands = getResolverInstallCommands(DNS_PORT);
183806
+ if (commands.length === 0) return;
183807
+ if (os4.platform() === "darwin") {
183808
+ execPrivilegedMacOS(commands);
183809
+ } else {
183810
+ execSync2(`sudo sh -c '${commands.join(" && ")}'`, { stdio: "inherit" });
183811
+ }
183812
+ }
183813
+ function performTlsTrust() {
183814
+ const wasGenerated = ensureCAGenerated();
183815
+ const certPath = path3.join(getCADir(), "ca.crt");
183816
+ try {
183817
+ if (os4.platform() === "darwin") {
183818
+ execSync2(`${getCADeleteCommand()} 2>/dev/null || true`, {
183819
+ stdio: "inherit"
183820
+ });
183821
+ execSync2(getCATrustCommand(certPath), { stdio: "inherit" });
183822
+ } else {
183823
+ execSync2(
183824
+ `sudo sh -c '${getCAInstallCommands(certPath).join(" && ")}'`,
183825
+ { stdio: "inherit" }
183826
+ );
183827
+ }
183828
+ } catch (err) {
183829
+ if (wasGenerated) {
183830
+ removeCA();
183831
+ }
183832
+ throw err;
183833
+ }
183779
183834
  }
183780
183835
  function performSystemSetup() {
183781
- const needsGenerate = !caFilesExist();
183836
+ const wasGenerated = ensureCAGenerated();
183782
183837
  const needsInstallCA = !caInstalledInTrustStore();
183783
183838
  const needsResolver = !resolverConfigExists();
183784
183839
  if (!needsInstallCA && !needsResolver) {
183785
183840
  return;
183786
183841
  }
183787
- if (needsGenerate) {
183788
- const { key, cert } = generateRootCA();
183789
- saveCA(key, cert);
183790
- }
183791
183842
  const certPath = path3.join(getCADir(), "ca.crt");
183792
183843
  const commands = [];
183793
183844
  if (needsInstallCA) {
@@ -183802,20 +183853,45 @@ function performSystemSetup() {
183802
183853
  try {
183803
183854
  execSync2(`sudo sh -c '${commands.join(" && ")}'`, { stdio: "inherit" });
183804
183855
  } catch (err) {
183805
- if (needsGenerate) {
183856
+ if (wasGenerated) {
183806
183857
  removeCA();
183807
183858
  }
183808
183859
  throw err;
183809
183860
  }
183810
183861
  }
183862
+ function execPrivilegedMacOS(commands) {
183863
+ if (commands.length === 0) return;
183864
+ const scriptPath = path3.join(os4.tmpdir(), "specific-setup.sh");
183865
+ const appPath = path3.join(os4.tmpdir(), "Specific.app");
183866
+ try {
183867
+ fs4.writeFileSync(
183868
+ scriptPath,
183869
+ "#!/bin/sh\nset -e\n" + commands.join("\n") + "\n",
183870
+ { mode: 448 }
183871
+ );
183872
+ execSync2(
183873
+ `osacompile -o '${appPath}' -e 'do shell script "sh ${scriptPath}" with administrator privileges'`
183874
+ );
183875
+ execSync2(`'${appPath}/Contents/MacOS/applet'`, { stdio: "pipe" });
183876
+ } finally {
183877
+ try {
183878
+ fs4.unlinkSync(scriptPath);
183879
+ } catch {
183880
+ }
183881
+ try {
183882
+ fs4.rmSync(appPath, { recursive: true });
183883
+ } catch {
183884
+ }
183885
+ }
183886
+ }
183811
183887
 
183812
183888
  // src/lib/analytics/index.ts
183813
183889
  import { PostHog } from "posthog-node";
183814
- import * as os6 from "os";
183890
+ import * as os7 from "os";
183815
183891
  import * as crypto from "crypto";
183816
183892
 
183817
183893
  // src/lib/project/config.ts
183818
- import * as fs4 from "fs";
183894
+ import * as fs5 from "fs";
183819
183895
  import * as path4 from "path";
183820
183896
  var PROJECT_ID_FILE = ".specific/project_id";
183821
183897
  var ProjectNotLinkedError = class extends Error {
@@ -183832,10 +183908,10 @@ Run: specific deploy`
183832
183908
  };
183833
183909
  function readProjectId(projectDir = process.cwd()) {
183834
183910
  const projectIdPath = path4.join(projectDir, PROJECT_ID_FILE);
183835
- if (!fs4.existsSync(projectIdPath)) {
183911
+ if (!fs5.existsSync(projectIdPath)) {
183836
183912
  throw new ProjectNotLinkedError();
183837
183913
  }
183838
- const projectId = fs4.readFileSync(projectIdPath, "utf-8").trim();
183914
+ const projectId = fs5.readFileSync(projectIdPath, "utf-8").trim();
183839
183915
  if (!projectId) {
183840
183916
  throw new Error(`${PROJECT_ID_FILE} is empty`);
183841
183917
  }
@@ -183843,20 +183919,20 @@ function readProjectId(projectDir = process.cwd()) {
183843
183919
  }
183844
183920
  function hasProjectId(projectDir = process.cwd()) {
183845
183921
  const projectIdPath = path4.join(projectDir, PROJECT_ID_FILE);
183846
- return fs4.existsSync(projectIdPath);
183922
+ return fs5.existsSync(projectIdPath);
183847
183923
  }
183848
183924
  function writeProjectId(projectId, projectDir = process.cwd()) {
183849
183925
  const specificDir = path4.join(projectDir, ".specific");
183850
- if (!fs4.existsSync(specificDir)) {
183851
- fs4.mkdirSync(specificDir, { recursive: true });
183926
+ if (!fs5.existsSync(specificDir)) {
183927
+ fs5.mkdirSync(specificDir, { recursive: true });
183852
183928
  }
183853
- fs4.writeFileSync(path4.join(specificDir, "project_id"), projectId + "\n");
183929
+ fs5.writeFileSync(path4.join(specificDir, "project_id"), projectId + "\n");
183854
183930
  }
183855
183931
 
183856
183932
  // src/lib/auth/credentials.ts
183857
- import * as fs10 from "fs";
183933
+ import * as fs11 from "fs";
183858
183934
  import * as path6 from "path";
183859
- import * as os5 from "os";
183935
+ import * as os6 from "os";
183860
183936
 
183861
183937
  // src/lib/auth/errors.ts
183862
183938
  var RefreshTokenExpiredError = class extends Error {
@@ -184437,18 +184513,18 @@ var ApiClient = class {
184437
184513
 
184438
184514
  // src/lib/auth/credentials.ts
184439
184515
  function getUserCredentialsDir() {
184440
- return path6.join(os5.homedir(), ".specific");
184516
+ return path6.join(os6.homedir(), ".specific");
184441
184517
  }
184442
184518
  function getCredentialsPath() {
184443
184519
  return path6.join(getUserCredentialsDir(), "credentials.json");
184444
184520
  }
184445
184521
  function readUserCredentials() {
184446
184522
  const credentialsPath = getCredentialsPath();
184447
- if (!fs10.existsSync(credentialsPath)) {
184523
+ if (!fs11.existsSync(credentialsPath)) {
184448
184524
  return null;
184449
184525
  }
184450
184526
  try {
184451
- const content = fs10.readFileSync(credentialsPath, "utf-8");
184527
+ const content = fs11.readFileSync(credentialsPath, "utf-8");
184452
184528
  return JSON.parse(content);
184453
184529
  } catch {
184454
184530
  return null;
@@ -184456,18 +184532,18 @@ function readUserCredentials() {
184456
184532
  }
184457
184533
  function writeUserCredentials(credentials) {
184458
184534
  const dir = getUserCredentialsDir();
184459
- if (!fs10.existsSync(dir)) {
184460
- fs10.mkdirSync(dir, { recursive: true, mode: 448 });
184535
+ if (!fs11.existsSync(dir)) {
184536
+ fs11.mkdirSync(dir, { recursive: true, mode: 448 });
184461
184537
  }
184462
184538
  const credentialsPath = getCredentialsPath();
184463
- fs10.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {
184539
+ fs11.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {
184464
184540
  mode: 384
184465
184541
  });
184466
184542
  }
184467
184543
  function clearUserCredentials() {
184468
184544
  const credentialsPath = getCredentialsPath();
184469
- if (fs10.existsSync(credentialsPath)) {
184470
- fs10.unlinkSync(credentialsPath);
184545
+ if (fs11.existsSync(credentialsPath)) {
184546
+ fs11.unlinkSync(credentialsPath);
184471
184547
  }
184472
184548
  }
184473
184549
  function isLoggedIn() {
@@ -184546,7 +184622,7 @@ function isEnabled() {
184546
184622
  }
184547
184623
  function getAnonymousId() {
184548
184624
  if (anonymousId) return anonymousId;
184549
- const machineId = `${os6.hostname()}-${os6.userInfo().username}`;
184625
+ const machineId = `${os7.hostname()}-${os7.userInfo().username}`;
184550
184626
  anonymousId = crypto.createHash("sha256").update(machineId).digest("hex").slice(0, 16);
184551
184627
  return anonymousId;
184552
184628
  }
@@ -184593,7 +184669,7 @@ function trackEvent(event, properties) {
184593
184669
  event,
184594
184670
  properties: {
184595
184671
  ...properties,
184596
- cli_version: "0.1.81",
184672
+ cli_version: "0.1.83",
184597
184673
  platform: process.platform,
184598
184674
  node_version: process.version,
184599
184675
  project_id: getProjectId()
@@ -184630,57 +184706,57 @@ var options = [
184630
184706
  ];
184631
184707
  function isGitProject() {
184632
184708
  const gitPath = path7.join(process.cwd(), ".git");
184633
- return fs11.existsSync(gitPath);
184709
+ return fs12.existsSync(gitPath);
184634
184710
  }
184635
184711
  function detectExistingAgents() {
184636
184712
  const detected = {};
184637
184713
  const cursorDir = path7.join(process.cwd(), ".cursor");
184638
- if (fs11.existsSync(cursorDir)) {
184714
+ if (fs12.existsSync(cursorDir)) {
184639
184715
  detected["cursor"] = true;
184640
184716
  }
184641
184717
  const claudeDir = path7.join(process.cwd(), ".claude");
184642
184718
  const claudeMd = path7.join(process.cwd(), "CLAUDE.md");
184643
- if (fs11.existsSync(claudeDir) || fs11.existsSync(claudeMd)) {
184719
+ if (fs12.existsSync(claudeDir) || fs12.existsSync(claudeMd)) {
184644
184720
  detected["claude"] = true;
184645
184721
  }
184646
184722
  const agentsMd = path7.join(process.cwd(), "AGENTS.md");
184647
- if (fs11.existsSync(agentsMd)) {
184723
+ if (fs12.existsSync(agentsMd)) {
184648
184724
  detected["codex"] = true;
184649
184725
  }
184650
184726
  return detected;
184651
184727
  }
184652
184728
  function appendOrCreateFile(filePath, content) {
184653
- if (fs11.existsSync(filePath)) {
184654
- const existing = fs11.readFileSync(filePath, "utf-8");
184729
+ if (fs12.existsSync(filePath)) {
184730
+ const existing = fs12.readFileSync(filePath, "utf-8");
184655
184731
  if (existing.includes("specific docs") || existing.includes("specific check")) {
184656
184732
  return "unchanged";
184657
184733
  }
184658
184734
  const separator = existing.endsWith("\n") ? "\n" : "\n\n";
184659
- fs11.writeFileSync(filePath, existing + separator + content + "\n");
184735
+ fs12.writeFileSync(filePath, existing + separator + content + "\n");
184660
184736
  return "modified";
184661
184737
  } else {
184662
- fs11.writeFileSync(filePath, content + "\n");
184738
+ fs12.writeFileSync(filePath, content + "\n");
184663
184739
  return "created";
184664
184740
  }
184665
184741
  }
184666
184742
  function addToGitignore() {
184667
184743
  const gitignorePath = path7.join(process.cwd(), ".gitignore");
184668
184744
  const entries = [".specific", "specific.local"];
184669
- if (fs11.existsSync(gitignorePath)) {
184670
- const existing = fs11.readFileSync(gitignorePath, "utf-8");
184745
+ if (fs12.existsSync(gitignorePath)) {
184746
+ const existing = fs12.readFileSync(gitignorePath, "utf-8");
184671
184747
  const lines = existing.split("\n").map((l) => l.trim());
184672
184748
  const missingEntries = entries.filter((entry) => !lines.includes(entry));
184673
184749
  if (missingEntries.length === 0) {
184674
184750
  return "unchanged";
184675
184751
  }
184676
184752
  const separator = existing.endsWith("\n") ? "" : "\n";
184677
- fs11.writeFileSync(
184753
+ fs12.writeFileSync(
184678
184754
  gitignorePath,
184679
184755
  existing + separator + missingEntries.join("\n") + "\n"
184680
184756
  );
184681
184757
  return "modified";
184682
184758
  } else {
184683
- fs11.writeFileSync(gitignorePath, entries.join("\n") + "\n");
184759
+ fs12.writeFileSync(gitignorePath, entries.join("\n") + "\n");
184684
184760
  return "created";
184685
184761
  }
184686
184762
  }
@@ -184688,8 +184764,8 @@ function configureClaudeCodePermissions() {
184688
184764
  const claudeDir = path7.join(process.cwd(), ".claude");
184689
184765
  const settingsPath = path7.join(claudeDir, "settings.local.json");
184690
184766
  const permissions = ["Bash(specific docs:*)", "Bash(specific check:*)"];
184691
- if (fs11.existsSync(settingsPath)) {
184692
- const existing = JSON.parse(fs11.readFileSync(settingsPath, "utf-8"));
184767
+ if (fs12.existsSync(settingsPath)) {
184768
+ const existing = JSON.parse(fs12.readFileSync(settingsPath, "utf-8"));
184693
184769
  const allowList = existing?.permissions?.allow || [];
184694
184770
  const missingPermissions = permissions.filter(
184695
184771
  (p) => !allowList.includes(p)
@@ -184704,39 +184780,39 @@ function configureClaudeCodePermissions() {
184704
184780
  existing.permissions.allow = [];
184705
184781
  }
184706
184782
  existing.permissions.allow.push(...missingPermissions);
184707
- fs11.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + "\n");
184783
+ fs12.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + "\n");
184708
184784
  return "modified";
184709
184785
  }
184710
- if (!fs11.existsSync(claudeDir)) {
184711
- fs11.mkdirSync(claudeDir);
184786
+ if (!fs12.existsSync(claudeDir)) {
184787
+ fs12.mkdirSync(claudeDir);
184712
184788
  }
184713
184789
  const settings = {
184714
184790
  permissions: {
184715
184791
  allow: permissions
184716
184792
  }
184717
184793
  };
184718
- fs11.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
184794
+ fs12.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
184719
184795
  return "created";
184720
184796
  }
184721
184797
  function createCursorRule() {
184722
184798
  const cursorDir = path7.join(process.cwd(), ".cursor");
184723
184799
  const rulesDir = path7.join(cursorDir, "rules");
184724
184800
  const mdcPath = path7.join(rulesDir, "specific.mdc");
184725
- if (fs11.existsSync(mdcPath)) {
184726
- const existing = fs11.readFileSync(mdcPath, "utf-8");
184801
+ if (fs12.existsSync(mdcPath)) {
184802
+ const existing = fs12.readFileSync(mdcPath, "utf-8");
184727
184803
  if (existing.includes("specific docs") || existing.includes("specific check")) {
184728
184804
  return "unchanged";
184729
184805
  }
184730
- fs11.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
184806
+ fs12.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
184731
184807
  return "modified";
184732
184808
  }
184733
- if (!fs11.existsSync(cursorDir)) {
184734
- fs11.mkdirSync(cursorDir);
184809
+ if (!fs12.existsSync(cursorDir)) {
184810
+ fs12.mkdirSync(cursorDir);
184735
184811
  }
184736
- if (!fs11.existsSync(rulesDir)) {
184737
- fs11.mkdirSync(rulesDir);
184812
+ if (!fs12.existsSync(rulesDir)) {
184813
+ fs12.mkdirSync(rulesDir);
184738
184814
  }
184739
- fs11.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
184815
+ fs12.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
184740
184816
  return "created";
184741
184817
  }
184742
184818
  function configureAgents(checked) {
@@ -184777,6 +184853,19 @@ function configureAgents(checked) {
184777
184853
  }
184778
184854
  return { agents, git, showManualInstructions: !!checked["other"] };
184779
184855
  }
184856
+ function getInitialSetupPhase() {
184857
+ if (!systemSetupNeeded()) return "agents";
184858
+ if (os8.platform() === "darwin") {
184859
+ if (dnsSetupNeeded()) return "setup-dns";
184860
+ if (tlsSetupNeeded()) return "setup-tls";
184861
+ return "agents";
184862
+ }
184863
+ return "setup-combined";
184864
+ }
184865
+ function getNextPhase(current) {
184866
+ if (current === "setup-dns" && tlsSetupNeeded()) return "setup-tls";
184867
+ return "agents";
184868
+ }
184780
184869
  function InitUI() {
184781
184870
  const { exit } = useApp();
184782
184871
  const [initialState] = useState(() => {
@@ -184785,45 +184874,92 @@ function InitUI() {
184785
184874
  return {
184786
184875
  detected,
184787
184876
  focusedIndex: hasDetected ? options.length : 0
184788
- // Focus submit if any detected
184789
184877
  };
184790
184878
  });
184791
- const [phase, setPhase] = useState(
184792
- () => !systemSetupNeeded() ? "agents" : "installing-ca"
184879
+ const [phase, setPhase] = useState(getInitialSetupPhase);
184880
+ const [stepPhase, setStepPhase] = useState(
184881
+ () => getInitialSetupPhase() === "agents" ? "done" : "prompt"
184793
184882
  );
184794
- const [caInstallPhase, setCaInstallPhase] = useState(() => !systemSetupNeeded() ? "done" : "prompt");
184883
+ const [stepError, setStepError] = useState();
184795
184884
  const [focusedIndex, setFocusedIndex] = useState(initialState.focusedIndex);
184796
184885
  const [checked, setChecked] = useState(
184797
184886
  initialState.detected
184798
184887
  );
184799
184888
  const [result, setResult] = useState(null);
184800
- const [tlsResult, setTlsResult] = useState(
184801
- !systemSetupNeeded() ? { status: "success" } : null
184889
+ const [setupResult, setSetupResult] = useState(
184890
+ () => !systemSetupNeeded() ? { dns: { status: "success" }, tls: { status: "success" } } : {}
184802
184891
  );
184803
184892
  useEffect(() => {
184804
- if (phase === "installing-ca" && caInstallPhase === "installing") {
184805
- installCA();
184893
+ if (stepPhase !== "installing") return;
184894
+ if (phase === "setup-dns") {
184895
+ runSetupStep(
184896
+ () => {
184897
+ ensureCAGenerated();
184898
+ performDnsSetup();
184899
+ },
184900
+ "dns"
184901
+ );
184902
+ } else if (phase === "setup-tls") {
184903
+ runSetupStep(
184904
+ () => {
184905
+ ensureCAGenerated();
184906
+ performTlsTrust();
184907
+ },
184908
+ "tls"
184909
+ );
184910
+ } else if (phase === "setup-combined") {
184911
+ runSetupStep(
184912
+ () => performSystemSetup(),
184913
+ "combined"
184914
+ );
184806
184915
  }
184807
- }, [phase, caInstallPhase]);
184808
- async function installCA() {
184916
+ }, [phase, stepPhase]);
184917
+ function runSetupStep(fn, type) {
184809
184918
  try {
184810
- performSystemSetup();
184811
- setTlsResult({ status: "success" });
184812
- setCaInstallPhase("done");
184813
- setPhase("agents");
184919
+ fn();
184920
+ if (type === "combined") {
184921
+ setSetupResult((r) => ({
184922
+ ...r,
184923
+ dns: { status: "success" },
184924
+ tls: { status: "success" }
184925
+ }));
184926
+ } else {
184927
+ setSetupResult((r) => ({ ...r, [type]: { status: "success" } }));
184928
+ }
184929
+ setStepPhase("done");
184930
+ const next = getNextPhase(phase);
184931
+ setPhase(next);
184932
+ if (next !== "agents") {
184933
+ setStepPhase("prompt");
184934
+ }
184814
184935
  } catch (err) {
184815
- setTlsResult({
184816
- status: "error",
184817
- error: err instanceof Error ? err.message : String(err)
184818
- });
184819
- setCaInstallPhase("error");
184820
- setPhase("agents");
184936
+ const errorMsg = err instanceof Error ? err.message : String(err);
184937
+ if (type === "combined") {
184938
+ setSetupResult((r) => ({
184939
+ ...r,
184940
+ dns: { status: "error", error: errorMsg },
184941
+ tls: { status: "error", error: errorMsg }
184942
+ }));
184943
+ } else {
184944
+ setSetupResult((r) => ({
184945
+ ...r,
184946
+ [type]: { status: "error", error: errorMsg }
184947
+ }));
184948
+ }
184949
+ setStepError(errorMsg);
184950
+ setStepPhase("error");
184951
+ const next = getNextPhase(phase);
184952
+ setPhase(next);
184953
+ if (next !== "agents") {
184954
+ setStepPhase("prompt");
184955
+ setStepError(void 0);
184956
+ }
184821
184957
  }
184822
184958
  }
184823
184959
  const isSubmitFocused = focusedIndex === options.length;
184824
184960
  useInput((input, key) => {
184825
- if (phase === "installing-ca" && caInstallPhase === "prompt" && key.return) {
184826
- setCaInstallPhase("installing");
184961
+ if ((phase === "setup-dns" || phase === "setup-tls" || phase === "setup-combined") && stepPhase === "prompt" && key.return) {
184962
+ setStepPhase("installing");
184827
184963
  return;
184828
184964
  }
184829
184965
  if (phase !== "agents") return;
@@ -184854,11 +184990,13 @@ function InitUI() {
184854
184990
  return () => clearTimeout(timer);
184855
184991
  }
184856
184992
  }, [phase, exit]);
184857
- if (phase === "installing-ca") {
184858
- if (caInstallPhase === "prompt") {
184859
- return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Configure development environment"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "We need to do a one-time setup for all your Specific projects."), /* @__PURE__ */ React2.createElement(Text2, null, "This is so we can run your apps locally with secure URLs:"), /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "green" }, "https://your-app.spcf.localhost"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "You will be prompted to authorize with your password."), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "Press ", /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Enter"), " to authorize."));
184993
+ if (phase === "setup-dns" || phase === "setup-tls" || phase === "setup-combined") {
184994
+ if (stepPhase === "prompt") {
184995
+ const isDns = phase === "setup-dns";
184996
+ const isTls = phase === "setup-tls";
184997
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Configure development environment"), /* @__PURE__ */ React2.createElement(Text2, null, " "), (isDns || phase === "setup-combined") && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, "We need to do a one-time setup for all your Specific projects."), /* @__PURE__ */ React2.createElement(Text2, null, "This is so we can run your apps locally with secure URLs:"), /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "green" }, "https://your-app.spcf.localhost"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "You will be prompted to authorize with your password.")), isTls && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, "We need to trust a development certificate for HTTPS."), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "You will be prompted to authorize.")), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "Press ", /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Enter"), " to continue."));
184860
184998
  }
184861
- if (caInstallPhase === "installing") {
184999
+ if (stepPhase === "installing") {
184862
185000
  return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Configure development environment"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "Awaiting authorization\u2026"));
184863
185001
  }
184864
185002
  }
@@ -184866,10 +185004,16 @@ function InitUI() {
184866
185004
  const selectedAgents = Object.entries(checked).filter(([, v]) => v).length;
184867
185005
  const agentChanges = result && (result.agents.filesCreated.length > 0 || result.agents.filesModified.length > 0);
184868
185006
  const gitChanges = result?.git && (result.git.filesCreated.length > 0 || result.git.filesModified.length > 0);
184869
- 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.")));
185007
+ const setupSuccess2 = (!setupResult.dns || setupResult.dns.status === "success") && (!setupResult.tls || setupResult.tls.status === "success");
185008
+ const setupError2 = setupResult.dns?.status === "error" || setupResult.tls?.status === "error";
185009
+ const setupErrorMsg2 = setupResult.dns?.error || setupResult.tls?.error;
185010
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, setupSuccess2 && /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " TLS and DNS configured"), setupError2 && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "red" }, "\u2717"), " Failed to set up TLS and DNS"), setupErrorMsg2 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", setupErrorMsg2)), result && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Coding agents configured"), result.agents.filesCreated.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Created: ", result.agents.filesCreated.join(", ")), result.agents.filesModified.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Modified: ", result.agents.filesModified.join(", ")), !agentChanges && selectedAgents > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "No changes needed (files already configured)"), selectedAgents === 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " No agents selected")), result?.git && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Git configured"), result.git.filesCreated.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Created: ", result.git.filesCreated.join(", ")), result.git.filesModified.length > 0 && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "Modified: ", result.git.filesModified.join(", ")), !gitChanges && /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, " ", "No changes needed (.gitignore already configured)")), result?.showManualInstructions && /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " Manual configuration selected"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, " Add this to your agent's system prompt:"), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"), /* @__PURE__ */ React2.createElement(Text2, null, SPECIFIC_INSTRUCTIONS), /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, " ", "We also recommend allowing your agent to run `specific docs *`"), /* @__PURE__ */ React2.createElement(Text2, null, " and `specific check *` without confirmation.")));
184870
185011
  }
184871
- 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;
184872
- 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) => {
185012
+ const setupSuccess = (!setupResult.dns || setupResult.dns.status === "success") && (!setupResult.tls || setupResult.tls.status === "success");
185013
+ const setupError = setupResult.dns?.status === "error" || setupResult.tls?.status === "error";
185014
+ const setupErrorMsg = setupResult.dns?.error || setupResult.tls?.error;
185015
+ const setupLine = setupSuccess ? /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "green" }, "\u2713"), " TLS and DNS configured") : setupError ? /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { color: "red" }, "\u2717"), " TLS and DNS setup failed", " ", /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "(", setupErrorMsg, ")")) : null;
185016
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, setupLine, /* @__PURE__ */ React2.createElement(Text2, null, /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "\u25CF"), " Configure coding agents", " ", /* @__PURE__ */ React2.createElement(Text2, { dimColor: true }, "(space to select)")), /* @__PURE__ */ React2.createElement(Text2, null, " "), options.map((option, index) => {
184873
185017
  const isFocused = focusedIndex === index;
184874
185018
  const isChecked = checked[option.id];
184875
185019
  return /* @__PURE__ */ React2.createElement(Box2, { key: option.id }, /* @__PURE__ */ React2.createElement(Text2, { ...isFocused && { color: "cyan" } }, isFocused ? "\u276F " : " ", isChecked ? "[\u2713]" : "[ ]", " ", option.label));
@@ -184979,7 +185123,7 @@ var BETA_REGISTRY = [
184979
185123
  ];
184980
185124
 
184981
185125
  // src/lib/beta/storage.ts
184982
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync6 } from "fs";
185126
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync6, existsSync as existsSync6, mkdirSync as mkdirSync6 } from "fs";
184983
185127
  import { join as join7 } from "path";
184984
185128
  var BETAS_FILE = ".specific/betas.json";
184985
185129
  function loadEnabledBetas(projectDir = process.cwd()) {
@@ -185012,7 +185156,7 @@ function saveBetas(enabled, projectDir) {
185012
185156
  mkdirSync6(specificDir, { recursive: true });
185013
185157
  }
185014
185158
  const data = { enabled };
185015
- writeFileSync5(
185159
+ writeFileSync6(
185016
185160
  join7(projectDir, BETAS_FILE),
185017
185161
  JSON.stringify(data, null, 2) + "\n"
185018
185162
  );
@@ -185079,7 +185223,7 @@ function resolveFilesystemDoc(path30) {
185079
185223
  import React3, { useState as useState2, useEffect as useEffect2 } from "react";
185080
185224
  import { render as render3, Text as Text3, Box as Box3 } from "ink";
185081
185225
  import Spinner3 from "ink-spinner";
185082
- import * as fs13 from "fs";
185226
+ import * as fs14 from "fs";
185083
185227
  import * as path9 from "path";
185084
185228
  import { execFile as execFile7 } from "child_process";
185085
185229
 
@@ -185951,32 +186095,32 @@ var ExtractionError = class extends Error {
185951
186095
  };
185952
186096
 
185953
186097
  // src/lib/bin/manager.ts
185954
- import * as fs12 from "fs";
186098
+ import * as fs13 from "fs";
185955
186099
  import * as path8 from "path";
185956
- import * as os7 from "os";
186100
+ import * as os9 from "os";
185957
186101
  import { createReadStream } from "fs";
185958
186102
  import { createTarExtractor, extractTo } from "tar-vern";
185959
186103
  function getLibraryEnv(binary) {
185960
186104
  if (!binary.libraryPath) {
185961
186105
  return {};
185962
186106
  }
185963
- const platform5 = os7.platform();
185964
- if (platform5 === "darwin") {
186107
+ const platform8 = os9.platform();
186108
+ if (platform8 === "darwin") {
185965
186109
  return { DYLD_LIBRARY_PATH: binary.libraryPath };
185966
- } else if (platform5 === "linux") {
186110
+ } else if (platform8 === "linux") {
185967
186111
  return { LD_LIBRARY_PATH: binary.libraryPath };
185968
186112
  }
185969
186113
  return {};
185970
186114
  }
185971
186115
  function getBinBaseDir() {
185972
- return path8.join(os7.homedir(), ".specific", "bin");
186116
+ return path8.join(os9.homedir(), ".specific", "bin");
185973
186117
  }
185974
186118
  function getPlatformInfo() {
185975
- const platform5 = os7.platform();
185976
- const arch3 = os7.arch();
185977
- if (platform5 !== "darwin" && platform5 !== "linux") {
186119
+ const platform8 = os9.platform();
186120
+ const arch3 = os9.arch();
186121
+ if (platform8 !== "darwin" && platform8 !== "linux") {
185978
186122
  throw new Error(
185979
- `Unsupported platform: ${platform5}. Only macOS and Linux are supported.`
186123
+ `Unsupported platform: ${platform8}. Only macOS and Linux are supported.`
185980
186124
  );
185981
186125
  }
185982
186126
  const archStr = arch3;
@@ -185990,7 +186134,7 @@ function getPlatformInfo() {
185990
186134
  `Unsupported architecture: ${arch3}. Only x64 and arm64 are supported.`
185991
186135
  );
185992
186136
  }
185993
- return { platform: platform5, arch: mappedArch };
186137
+ return { platform: platform8, arch: mappedArch };
185994
186138
  }
185995
186139
  function getBinaryDir(definition, version, platformInfo) {
185996
186140
  return path8.join(
@@ -186004,7 +186148,7 @@ function isBinaryInstalled(definition, version, platformInfo) {
186004
186148
  const binDir = getBinaryDir(definition, version, platformInfo);
186005
186149
  for (const execPath of definition.executables) {
186006
186150
  const fullPath = path8.join(binDir, execPath);
186007
- if (!fs12.existsSync(fullPath)) {
186151
+ if (!fs13.existsSync(fullPath)) {
186008
186152
  return false;
186009
186153
  }
186010
186154
  }
@@ -186033,11 +186177,11 @@ async function downloadFile(url, destPath, onProgress) {
186033
186177
  );
186034
186178
  let bytesDownloaded = 0;
186035
186179
  const parentDir = path8.dirname(destPath);
186036
- if (!fs12.existsSync(parentDir)) {
186037
- fs12.mkdirSync(parentDir, { recursive: true });
186180
+ if (!fs13.existsSync(parentDir)) {
186181
+ fs13.mkdirSync(parentDir, { recursive: true });
186038
186182
  }
186039
186183
  const partPath = destPath + ".part";
186040
- const fileStream = fs12.createWriteStream(partPath);
186184
+ const fileStream = fs13.createWriteStream(partPath);
186041
186185
  try {
186042
186186
  const reader = response.body.getReader();
186043
186187
  while (true) {
@@ -186060,12 +186204,12 @@ async function downloadFile(url, destPath, onProgress) {
186060
186204
  else resolve9();
186061
186205
  });
186062
186206
  });
186063
- fs12.renameSync(partPath, destPath);
186207
+ fs13.renameSync(partPath, destPath);
186064
186208
  } catch (error) {
186065
186209
  try {
186066
186210
  fileStream.close();
186067
- if (fs12.existsSync(partPath)) {
186068
- fs12.unlinkSync(partPath);
186211
+ if (fs13.existsSync(partPath)) {
186212
+ fs13.unlinkSync(partPath);
186069
186213
  }
186070
186214
  } catch {
186071
186215
  }
@@ -186074,8 +186218,8 @@ async function downloadFile(url, destPath, onProgress) {
186074
186218
  }
186075
186219
  async function extractTarball(archivePath, destDir, definition, onProgress) {
186076
186220
  onProgress?.({ phase: "extracting" });
186077
- if (!fs12.existsSync(destDir)) {
186078
- fs12.mkdirSync(destDir, { recursive: true });
186221
+ if (!fs13.existsSync(destDir)) {
186222
+ fs13.mkdirSync(destDir, { recursive: true });
186079
186223
  }
186080
186224
  try {
186081
186225
  const fileStream = createReadStream(archivePath);
@@ -186084,8 +186228,8 @@ async function extractTarball(archivePath, destDir, definition, onProgress) {
186084
186228
  onProgress?.({ phase: "finalizing" });
186085
186229
  for (const execPath of definition.executables) {
186086
186230
  const fullPath = path8.join(destDir, execPath);
186087
- if (fs12.existsSync(fullPath)) {
186088
- fs12.chmodSync(fullPath, 493);
186231
+ if (fs13.existsSync(fullPath)) {
186232
+ fs13.chmodSync(fullPath, 493);
186089
186233
  }
186090
186234
  }
186091
186235
  } catch (error) {
@@ -186109,12 +186253,12 @@ async function ensureBinary(definition, version, onProgress) {
186109
186253
  `Binary type definitions must have exactly one executable, got ${definition.executables.length}`
186110
186254
  );
186111
186255
  }
186112
- if (!fs12.existsSync(binDir)) {
186113
- fs12.mkdirSync(binDir, { recursive: true });
186256
+ if (!fs13.existsSync(binDir)) {
186257
+ fs13.mkdirSync(binDir, { recursive: true });
186114
186258
  }
186115
186259
  const execPath = path8.join(binDir, definition.executables[0]);
186116
186260
  await downloadFile(url, execPath, onProgress);
186117
- fs12.chmodSync(execPath, 493);
186261
+ fs13.chmodSync(execPath, 493);
186118
186262
  onProgress?.({ phase: "finalizing" });
186119
186263
  } else {
186120
186264
  const downloadDir = path8.join(getBinBaseDir(), "downloads");
@@ -186125,8 +186269,8 @@ async function ensureBinary(definition, version, onProgress) {
186125
186269
  await extractTarball(archivePath, binDir, definition, onProgress);
186126
186270
  } finally {
186127
186271
  try {
186128
- if (fs12.existsSync(archivePath)) {
186129
- fs12.unlinkSync(archivePath);
186272
+ if (fs13.existsSync(archivePath)) {
186273
+ fs13.unlinkSync(archivePath);
186130
186274
  }
186131
186275
  } catch {
186132
186276
  }
@@ -186302,20 +186446,20 @@ function CheckUI() {
186302
186446
  useEffect2(() => {
186303
186447
  async function load() {
186304
186448
  const configPath = path9.join(process.cwd(), "specific.hcl");
186305
- if (!fs13.existsSync(configPath)) {
186449
+ if (!fs14.existsSync(configPath)) {
186306
186450
  setState({
186307
186451
  status: "error",
186308
186452
  error: "No specific.hcl found in current directory"
186309
186453
  });
186310
186454
  return;
186311
186455
  }
186312
- const hcl = fs13.readFileSync(configPath, "utf-8");
186456
+ const hcl = fs14.readFileSync(configPath, "utf-8");
186313
186457
  try {
186314
186458
  const config2 = await parseConfig(hcl);
186315
186459
  for (const build of config2.builds) {
186316
186460
  if (build.dockerfile) {
186317
186461
  const dockerfilePath = path9.resolve(process.cwd(), build.dockerfile);
186318
- if (!fs13.existsSync(dockerfilePath)) {
186462
+ if (!fs14.existsSync(dockerfilePath)) {
186319
186463
  setState({
186320
186464
  status: "error",
186321
186465
  error: `Build "${build.name}": Dockerfile not found at "${build.dockerfile}" (resolved to ${dockerfilePath})`
@@ -186331,7 +186475,7 @@ function CheckUI() {
186331
186475
  process.cwd(),
186332
186476
  pg.reshape.migrations_dir ?? "migrations"
186333
186477
  );
186334
- if (!fs13.existsSync(migrationsDir)) {
186478
+ if (!fs14.existsSync(migrationsDir)) {
186335
186479
  reshapeChecks2.push({
186336
186480
  databaseName: pg.name,
186337
186481
  migrationsDir: pg.reshape.migrations_dir ?? "migrations",
@@ -186384,7 +186528,9 @@ function checkCommand() {
186384
186528
  import React6, { useState as useState5, useEffect as useEffect3, useRef } from "react";
186385
186529
  import { render as render4, Text as Text6, Box as Box6, useApp as useApp2, Static, useInput as useInput4 } from "ink";
186386
186530
  import Spinner4 from "ink-spinner";
186387
- import * as fs23 from "fs";
186531
+ import { Readable as Readable2 } from "stream";
186532
+ import * as fs24 from "fs";
186533
+ import * as os11 from "os";
186388
186534
  import * as path20 from "path";
186389
186535
 
186390
186536
  // node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
@@ -188139,7 +188285,7 @@ var PortAllocator = class {
188139
188285
  };
188140
188286
 
188141
188287
  // src/lib/dev/stable-port-allocator.ts
188142
- import * as fs14 from "fs";
188288
+ import * as fs15 from "fs";
188143
188289
  import * as path10 from "path";
188144
188290
  var PORT_RANGE_START2 = 4e4;
188145
188291
  var PORT_RANGE_END2 = 49999;
@@ -188154,11 +188300,11 @@ var StablePortAllocator = class {
188154
188300
  this.loadPorts();
188155
188301
  }
188156
188302
  loadPorts() {
188157
- if (!fs14.existsSync(this.portsFilePath)) {
188303
+ if (!fs15.existsSync(this.portsFilePath)) {
188158
188304
  return;
188159
188305
  }
188160
188306
  try {
188161
- const content = fs14.readFileSync(this.portsFilePath, "utf-8");
188307
+ const content = fs15.readFileSync(this.portsFilePath, "utf-8");
188162
188308
  const data = JSON.parse(content);
188163
188309
  if (data.version === 1 && data.ports) {
188164
188310
  this.savedPorts = data.ports;
@@ -188171,14 +188317,14 @@ var StablePortAllocator = class {
188171
188317
  }
188172
188318
  }
188173
188319
  savePorts() {
188174
- if (!fs14.existsSync(this.portsDir)) {
188175
- fs14.mkdirSync(this.portsDir, { recursive: true });
188320
+ if (!fs15.existsSync(this.portsDir)) {
188321
+ fs15.mkdirSync(this.portsDir, { recursive: true });
188176
188322
  }
188177
188323
  const data = {
188178
188324
  version: 1,
188179
188325
  ports: this.savedPorts
188180
188326
  };
188181
- fs14.writeFileSync(this.portsFilePath, JSON.stringify(data, null, 2));
188327
+ fs15.writeFileSync(this.portsFilePath, JSON.stringify(data, null, 2));
188182
188328
  }
188183
188329
  allocateRandom() {
188184
188330
  const rangeSize = PORT_RANGE_END2 - PORT_RANGE_START2 + 1;
@@ -188205,7 +188351,7 @@ var StablePortAllocator = class {
188205
188351
  };
188206
188352
 
188207
188353
  // src/lib/dev/database-manager.ts
188208
- import * as fs15 from "fs";
188354
+ import * as fs16 from "fs";
188209
188355
  import * as path11 from "path";
188210
188356
  import * as net from "net";
188211
188357
  import { spawn } from "child_process";
@@ -188217,9 +188363,9 @@ async function startPostgres(pg, port, dataDir, onProgress) {
188217
188363
  const password = "postgres";
188218
188364
  const libraryEnv = getLibraryEnv(binary);
188219
188365
  const env2 = { ...process.env, ...libraryEnv };
188220
- const dataExists = fs15.existsSync(dbDataPath);
188366
+ const dataExists = fs16.existsSync(dbDataPath);
188221
188367
  if (!dataExists) {
188222
- fs15.mkdirSync(dbDataPath, { recursive: true });
188368
+ fs16.mkdirSync(dbDataPath, { recursive: true });
188223
188369
  await runCommand(
188224
188370
  binary.executables["initdb"],
188225
188371
  ["-D", dbDataPath, "-U", user, "--auth=trust", "--no-locale", "-E", "UTF8"],
@@ -188296,8 +188442,8 @@ async function startRedis(redis, port, onProgress) {
188296
188442
  async function startStorage(storage, port, dataDir) {
188297
188443
  const S3rver = (await import("s3rver")).default;
188298
188444
  const storageDataPath = path11.join(process.cwd(), dataDir, storage.name);
188299
- if (!fs15.existsSync(storageDataPath)) {
188300
- fs15.mkdirSync(storageDataPath, { recursive: true });
188445
+ if (!fs16.existsSync(storageDataPath)) {
188446
+ fs16.mkdirSync(storageDataPath, { recursive: true });
188301
188447
  }
188302
188448
  const host = "127.0.0.1";
188303
188449
  const accessKey = "S3RVER";
@@ -188950,7 +189096,7 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
188950
189096
  }
188951
189097
 
188952
189098
  // src/lib/dev/instance-state.ts
188953
- import * as fs16 from "fs";
189099
+ import * as fs17 from "fs";
188954
189100
  import * as path12 from "path";
188955
189101
  var InstanceStateManager = class {
188956
189102
  stateDir;
@@ -188968,8 +189114,8 @@ var InstanceStateManager = class {
188968
189114
  return this.key;
188969
189115
  }
188970
189116
  ensureStateDir() {
188971
- if (!fs16.existsSync(this.stateDir)) {
188972
- fs16.mkdirSync(this.stateDir, { recursive: true });
189117
+ if (!fs17.existsSync(this.stateDir)) {
189118
+ fs17.mkdirSync(this.stateDir, { recursive: true });
188973
189119
  }
188974
189120
  }
188975
189121
  isProcessRunning(pid) {
@@ -188986,15 +189132,15 @@ var InstanceStateManager = class {
188986
189132
  const startTime = Date.now();
188987
189133
  while (Date.now() - startTime < timeoutMs) {
188988
189134
  try {
188989
- const fd = fs16.openSync(
189135
+ const fd = fs17.openSync(
188990
189136
  this.lockPath,
188991
- fs16.constants.O_CREAT | fs16.constants.O_EXCL | fs16.constants.O_WRONLY
189137
+ fs17.constants.O_CREAT | fs17.constants.O_EXCL | fs17.constants.O_WRONLY
188992
189138
  );
188993
- fs16.writeSync(fd, String(process.pid));
188994
- fs16.closeSync(fd);
189139
+ fs17.writeSync(fd, String(process.pid));
189140
+ fs17.closeSync(fd);
188995
189141
  return () => {
188996
189142
  try {
188997
- fs16.unlinkSync(this.lockPath);
189143
+ fs17.unlinkSync(this.lockPath);
188998
189144
  } catch {
188999
189145
  }
189000
189146
  };
@@ -189003,16 +189149,16 @@ var InstanceStateManager = class {
189003
189149
  if (err.code === "EEXIST") {
189004
189150
  try {
189005
189151
  const lockPid = parseInt(
189006
- fs16.readFileSync(this.lockPath, "utf-8").trim(),
189152
+ fs17.readFileSync(this.lockPath, "utf-8").trim(),
189007
189153
  10
189008
189154
  );
189009
189155
  if (!this.isProcessRunning(lockPid)) {
189010
- fs16.unlinkSync(this.lockPath);
189156
+ fs17.unlinkSync(this.lockPath);
189011
189157
  continue;
189012
189158
  }
189013
189159
  } catch {
189014
189160
  try {
189015
- fs16.unlinkSync(this.lockPath);
189161
+ fs17.unlinkSync(this.lockPath);
189016
189162
  } catch {
189017
189163
  }
189018
189164
  continue;
@@ -189026,12 +189172,12 @@ var InstanceStateManager = class {
189026
189172
  throw new Error("Failed to acquire state lock (timeout)");
189027
189173
  }
189028
189174
  async getExistingInstances() {
189029
- if (!fs16.existsSync(this.statePath)) {
189175
+ if (!fs17.existsSync(this.statePath)) {
189030
189176
  return null;
189031
189177
  }
189032
189178
  const releaseLock = await this.acquireLock();
189033
189179
  try {
189034
- const content = fs16.readFileSync(this.statePath, "utf-8");
189180
+ const content = fs17.readFileSync(this.statePath, "utf-8");
189035
189181
  const state = JSON.parse(content);
189036
189182
  if (!this.isProcessRunning(state.owner.pid)) {
189037
189183
  return null;
@@ -189044,21 +189190,21 @@ var InstanceStateManager = class {
189044
189190
  }
189045
189191
  }
189046
189192
  async cleanStaleState() {
189047
- if (!fs16.existsSync(this.statePath)) {
189193
+ if (!fs17.existsSync(this.statePath)) {
189048
189194
  return false;
189049
189195
  }
189050
189196
  const releaseLock = await this.acquireLock();
189051
189197
  try {
189052
- const content = fs16.readFileSync(this.statePath, "utf-8");
189198
+ const content = fs17.readFileSync(this.statePath, "utf-8");
189053
189199
  const state = JSON.parse(content);
189054
189200
  if (!this.isProcessRunning(state.owner.pid)) {
189055
- fs16.unlinkSync(this.statePath);
189201
+ fs17.unlinkSync(this.statePath);
189056
189202
  return true;
189057
189203
  }
189058
189204
  return false;
189059
189205
  } catch {
189060
189206
  try {
189061
- fs16.unlinkSync(this.statePath);
189207
+ fs17.unlinkSync(this.statePath);
189062
189208
  return true;
189063
189209
  } catch {
189064
189210
  }
@@ -189070,8 +189216,8 @@ var InstanceStateManager = class {
189070
189216
  async claimOwnership(command) {
189071
189217
  const releaseLock = await this.acquireLock();
189072
189218
  try {
189073
- if (fs16.existsSync(this.statePath)) {
189074
- const content = fs16.readFileSync(this.statePath, "utf-8");
189219
+ if (fs17.existsSync(this.statePath)) {
189220
+ const content = fs17.readFileSync(this.statePath, "utf-8");
189075
189221
  const state2 = JSON.parse(content);
189076
189222
  if (this.isProcessRunning(state2.owner.pid)) {
189077
189223
  throw new Error(`Instances already owned by PID ${state2.owner.pid}`);
@@ -189140,8 +189286,8 @@ var InstanceStateManager = class {
189140
189286
  }
189141
189287
  const releaseLock = await this.acquireLock();
189142
189288
  try {
189143
- if (fs16.existsSync(this.statePath)) {
189144
- fs16.unlinkSync(this.statePath);
189289
+ if (fs17.existsSync(this.statePath)) {
189290
+ fs17.unlinkSync(this.statePath);
189145
189291
  }
189146
189292
  this.ownsInstances = false;
189147
189293
  } finally {
@@ -189149,21 +189295,21 @@ var InstanceStateManager = class {
189149
189295
  }
189150
189296
  }
189151
189297
  readState() {
189152
- const content = fs16.readFileSync(this.statePath, "utf-8");
189298
+ const content = fs17.readFileSync(this.statePath, "utf-8");
189153
189299
  return JSON.parse(content);
189154
189300
  }
189155
189301
  writeStateAtomic(state) {
189156
189302
  this.ensureStateDir();
189157
189303
  const tmpPath = this.statePath + ".tmp";
189158
- fs16.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
189159
- fs16.renameSync(tmpPath, this.statePath);
189304
+ fs17.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
189305
+ fs17.renameSync(tmpPath, this.statePath);
189160
189306
  }
189161
189307
  };
189162
189308
 
189163
189309
  // src/lib/dev/http-proxy.ts
189164
189310
  import * as http from "http";
189165
189311
  import * as https from "https";
189166
- import * as fs17 from "fs";
189312
+ import * as fs18 from "fs";
189167
189313
  import * as path13 from "path";
189168
189314
  import { fileURLToPath as fileURLToPath3 } from "url";
189169
189315
  import httpProxy from "http-proxy";
@@ -189586,10 +189732,10 @@ function serveFilesystemFile(res, pathname) {
189586
189732
  res.end("<h1>Forbidden</h1>");
189587
189733
  return;
189588
189734
  }
189589
- if (fs17.existsSync(resolvedPath)) {
189590
- if (fs17.statSync(resolvedPath).isDirectory()) {
189735
+ if (fs18.existsSync(resolvedPath)) {
189736
+ if (fs18.statSync(resolvedPath).isDirectory()) {
189591
189737
  const indexPath2 = path13.join(resolvedPath, "index.html");
189592
- if (fs17.existsSync(indexPath2)) {
189738
+ if (fs18.existsSync(indexPath2)) {
189593
189739
  return serveFile(res, indexPath2);
189594
189740
  }
189595
189741
  } else {
@@ -189597,15 +189743,15 @@ function serveFilesystemFile(res, pathname) {
189597
189743
  }
189598
189744
  }
189599
189745
  const htmlPath = resolvedPath + ".html";
189600
- if (fs17.existsSync(htmlPath)) {
189746
+ if (fs18.existsSync(htmlPath)) {
189601
189747
  return serveFile(res, htmlPath);
189602
189748
  }
189603
189749
  const indexPath = path13.join(resolvedPath, "index.html");
189604
- if (fs17.existsSync(indexPath)) {
189750
+ if (fs18.existsSync(indexPath)) {
189605
189751
  return serveFile(res, indexPath);
189606
189752
  }
189607
189753
  const notFoundPath = path13.join(adminDir, "404.html");
189608
- if (fs17.existsSync(notFoundPath)) {
189754
+ if (fs18.existsSync(notFoundPath)) {
189609
189755
  return serveFileContent(res, notFoundPath, "text/html", 404);
189610
189756
  }
189611
189757
  res.writeHead(404, { "Content-Type": "text/html" });
@@ -189618,7 +189764,7 @@ function serveFile(res, filePath) {
189618
189764
  }
189619
189765
  function serveFileContent(res, filePath, contentType, statusCode = 200) {
189620
189766
  try {
189621
- const content = fs17.readFileSync(filePath);
189767
+ const content = fs18.readFileSync(filePath);
189622
189768
  res.writeHead(statusCode, { "Content-Type": contentType });
189623
189769
  res.end(content);
189624
189770
  } catch (err) {
@@ -189993,7 +190139,7 @@ async function startMailServer(mail, smtpPort, apiPort) {
189993
190139
 
189994
190140
  // src/lib/dev/drizzle-gateway-manager.ts
189995
190141
  import * as net3 from "net";
189996
- import * as fs18 from "fs";
190142
+ import * as fs19 from "fs";
189997
190143
  import * as path15 from "path";
189998
190144
  import { spawn as spawn4 } from "child_process";
189999
190145
  import { randomUUID as randomUUID2 } from "crypto";
@@ -190027,12 +190173,12 @@ async function startDrizzleGateway(postgresInstances, port, configDir, options2)
190027
190173
  );
190028
190174
  const host = "127.0.0.1";
190029
190175
  const drizzleConfigDir = path15.join(configDir, "drizzle-gateway");
190030
- if (!fs18.existsSync(drizzleConfigDir)) {
190031
- fs18.mkdirSync(drizzleConfigDir, { recursive: true });
190176
+ if (!fs19.existsSync(drizzleConfigDir)) {
190177
+ fs19.mkdirSync(drizzleConfigDir, { recursive: true });
190032
190178
  }
190033
190179
  const storeJson = generateStoreJson(postgresInstances);
190034
190180
  const storeJsonPath = path15.join(drizzleConfigDir, "store.json");
190035
- fs18.writeFileSync(storeJsonPath, JSON.stringify(storeJson, null, 2));
190181
+ fs19.writeFileSync(storeJsonPath, JSON.stringify(storeJson, null, 2));
190036
190182
  writeLog("drizzle-gateway", `Starting Drizzle Gateway`);
190037
190183
  writeLog("drizzle-gateway", `STORE_PATH: ${drizzleConfigDir}`);
190038
190184
  writeLog("drizzle-gateway", `PORT: ${port}`);
@@ -190134,16 +190280,16 @@ function detectSyncDatabases(config) {
190134
190280
  }
190135
190281
 
190136
190282
  // src/lib/dev/reshape-watcher.ts
190137
- import * as fs19 from "fs";
190283
+ import * as fs20 from "fs";
190138
190284
  import * as path16 from "path";
190139
190285
  import { spawnSync } from "child_process";
190140
190286
  function getMigrationFiles(dir, log) {
190141
190287
  log(`Scanning migrations directory: ${dir}`);
190142
- if (!fs19.existsSync(dir)) {
190288
+ if (!fs20.existsSync(dir)) {
190143
190289
  log(`Migrations directory does not exist: ${dir}`);
190144
190290
  return [];
190145
190291
  }
190146
- const files = fs19.readdirSync(dir);
190292
+ const files = fs20.readdirSync(dir);
190147
190293
  log(`Found ${files.length} files in directory`);
190148
190294
  const tomlFiles = files.filter((f) => f.endsWith(".toml")).sort((a, b) => a.localeCompare(b));
190149
190295
  log(`Found ${tomlFiles.length} .toml migration files: ${tomlFiles.join(", ") || "(none)"}`);
@@ -190193,7 +190339,7 @@ function runReshape(args, databaseUrl, migrationsDir, reshapeBinaryPath, log) {
190193
190339
  }
190194
190340
  function makeReadOnly(filePath, log) {
190195
190341
  try {
190196
- fs19.chmodSync(filePath, 292);
190342
+ fs20.chmodSync(filePath, 292);
190197
190343
  log(`Set file permissions to read-only (444): ${filePath}`);
190198
190344
  } catch (err) {
190199
190345
  const message = err instanceof Error ? err.message : String(err);
@@ -190279,9 +190425,9 @@ function createReshapeWatcher(options2) {
190279
190425
  };
190280
190426
  const startWatching = () => {
190281
190427
  log(`Starting file watcher for migrations directory...`);
190282
- if (!fs19.existsSync(migrationsDir)) {
190428
+ if (!fs20.existsSync(migrationsDir)) {
190283
190429
  log(`Migrations directory does not exist, creating: ${migrationsDir}`);
190284
- fs19.mkdirSync(migrationsDir, { recursive: true });
190430
+ fs20.mkdirSync(migrationsDir, { recursive: true });
190285
190431
  }
190286
190432
  log(`Watching directory: ${migrationsDir}`);
190287
190433
  watcher = chokidar_default.watch(migrationsDir, {
@@ -190416,7 +190562,7 @@ function createReshapeWatcher(options2) {
190416
190562
  }
190417
190563
 
190418
190564
  // src/lib/dev/temporal-manager.ts
190419
- import * as fs20 from "fs";
190565
+ import * as fs21 from "fs";
190420
190566
  import * as path17 from "path";
190421
190567
  import * as net4 from "net";
190422
190568
  import { spawn as spawn5 } from "child_process";
@@ -190424,8 +190570,8 @@ async function startTemporalDevServer(temporals, grpcPort, uiPort, dataDir, onPr
190424
190570
  const binary = await ensureBinary(temporalBinary, void 0, onProgress);
190425
190571
  const dbPath = path17.join(process.cwd(), dataDir, "temporal.db");
190426
190572
  const dbDir = path17.dirname(dbPath);
190427
- if (!fs20.existsSync(dbDir)) {
190428
- fs20.mkdirSync(dbDir, { recursive: true });
190573
+ if (!fs21.existsSync(dbDir)) {
190574
+ fs21.mkdirSync(dbDir, { recursive: true });
190429
190575
  }
190430
190576
  const host = "127.0.0.1";
190431
190577
  const namespaceArgs = temporals.flatMap((t) => ["--namespace", t.name]);
@@ -190754,7 +190900,7 @@ function watchConfigFile(configPath, debounceMs, onChange) {
190754
190900
  }
190755
190901
 
190756
190902
  // src/lib/dev/subdomain-generator.ts
190757
- import * as fs21 from "fs";
190903
+ import * as fs22 from "fs";
190758
190904
  import * as path18 from "path";
190759
190905
  import { generateSlug } from "random-word-slugs";
190760
190906
  var StableSubdomainAllocator = class {
@@ -190767,11 +190913,11 @@ var StableSubdomainAllocator = class {
190767
190913
  this.loadTunnels();
190768
190914
  }
190769
190915
  loadTunnels() {
190770
- if (!fs21.existsSync(this.tunnelsFilePath)) {
190916
+ if (!fs22.existsSync(this.tunnelsFilePath)) {
190771
190917
  return;
190772
190918
  }
190773
190919
  try {
190774
- const content = fs21.readFileSync(this.tunnelsFilePath, "utf-8");
190920
+ const content = fs22.readFileSync(this.tunnelsFilePath, "utf-8");
190775
190921
  const data = JSON.parse(content);
190776
190922
  if (data.version === 1 && data.baseSlug) {
190777
190923
  this.baseSlug = data.baseSlug;
@@ -190781,14 +190927,14 @@ var StableSubdomainAllocator = class {
190781
190927
  }
190782
190928
  }
190783
190929
  saveTunnels() {
190784
- if (!fs21.existsSync(this.tunnelsDir)) {
190785
- fs21.mkdirSync(this.tunnelsDir, { recursive: true });
190930
+ if (!fs22.existsSync(this.tunnelsDir)) {
190931
+ fs22.mkdirSync(this.tunnelsDir, { recursive: true });
190786
190932
  }
190787
190933
  const data = {
190788
190934
  version: 1,
190789
190935
  baseSlug: this.baseSlug
190790
190936
  };
190791
- fs21.writeFileSync(this.tunnelsFilePath, JSON.stringify(data, null, 2));
190937
+ fs22.writeFileSync(this.tunnelsFilePath, JSON.stringify(data, null, 2));
190792
190938
  }
190793
190939
  generateBaseSlug() {
190794
190940
  return generateSlug(2, {
@@ -190944,9 +191090,9 @@ async function startTunnel(serviceName, endpointName, port, subdomain, callbacks
190944
191090
  }
190945
191091
 
190946
191092
  // src/lib/dev/proxy-registry.ts
190947
- import * as fs22 from "fs";
191093
+ import * as fs23 from "fs";
190948
191094
  import * as path19 from "path";
190949
- import * as os8 from "os";
191095
+ import * as os10 from "os";
190950
191096
  import * as net6 from "net";
190951
191097
  var ProxyRegistryManager = class {
190952
191098
  proxyDir;
@@ -190956,14 +191102,14 @@ var ProxyRegistryManager = class {
190956
191102
  isOwner = false;
190957
191103
  registryWatcher = null;
190958
191104
  constructor() {
190959
- this.proxyDir = path19.join(os8.homedir(), ".specific", "proxy");
191105
+ this.proxyDir = path19.join(os10.homedir(), ".specific", "proxy");
190960
191106
  this.ownerPath = path19.join(this.proxyDir, "owner.json");
190961
191107
  this.registryPath = path19.join(this.proxyDir, "registry.json");
190962
191108
  this.lockPath = path19.join(this.proxyDir, "registry.lock");
190963
191109
  }
190964
191110
  ensureProxyDir() {
190965
- if (!fs22.existsSync(this.proxyDir)) {
190966
- fs22.mkdirSync(this.proxyDir, { recursive: true });
191111
+ if (!fs23.existsSync(this.proxyDir)) {
191112
+ fs23.mkdirSync(this.proxyDir, { recursive: true });
190967
191113
  }
190968
191114
  }
190969
191115
  isProcessRunning(pid) {
@@ -191020,15 +191166,15 @@ var ProxyRegistryManager = class {
191020
191166
  const startTime = Date.now();
191021
191167
  while (Date.now() - startTime < timeoutMs) {
191022
191168
  try {
191023
- const fd = fs22.openSync(
191169
+ const fd = fs23.openSync(
191024
191170
  this.lockPath,
191025
- fs22.constants.O_CREAT | fs22.constants.O_EXCL | fs22.constants.O_WRONLY
191171
+ fs23.constants.O_CREAT | fs23.constants.O_EXCL | fs23.constants.O_WRONLY
191026
191172
  );
191027
- fs22.writeSync(fd, String(process.pid));
191028
- fs22.closeSync(fd);
191173
+ fs23.writeSync(fd, String(process.pid));
191174
+ fs23.closeSync(fd);
191029
191175
  return () => {
191030
191176
  try {
191031
- fs22.unlinkSync(this.lockPath);
191177
+ fs23.unlinkSync(this.lockPath);
191032
191178
  } catch {
191033
191179
  }
191034
191180
  };
@@ -191037,16 +191183,16 @@ var ProxyRegistryManager = class {
191037
191183
  if (err.code === "EEXIST") {
191038
191184
  try {
191039
191185
  const lockPid = parseInt(
191040
- fs22.readFileSync(this.lockPath, "utf-8").trim(),
191186
+ fs23.readFileSync(this.lockPath, "utf-8").trim(),
191041
191187
  10
191042
191188
  );
191043
191189
  if (!this.isProcessRunning(lockPid)) {
191044
- fs22.unlinkSync(this.lockPath);
191190
+ fs23.unlinkSync(this.lockPath);
191045
191191
  continue;
191046
191192
  }
191047
191193
  } catch {
191048
191194
  try {
191049
- fs22.unlinkSync(this.lockPath);
191195
+ fs23.unlinkSync(this.lockPath);
191050
191196
  } catch {
191051
191197
  }
191052
191198
  continue;
@@ -191066,8 +191212,8 @@ var ProxyRegistryManager = class {
191066
191212
  async claimProxyOwnership(key) {
191067
191213
  const releaseLock = await this.acquireLock();
191068
191214
  try {
191069
- if (fs22.existsSync(this.ownerPath)) {
191070
- const content = fs22.readFileSync(this.ownerPath, "utf-8");
191215
+ if (fs23.existsSync(this.ownerPath)) {
191216
+ const content = fs23.readFileSync(this.ownerPath, "utf-8");
191071
191217
  const ownerFile2 = JSON.parse(content);
191072
191218
  if (await this.isProxyOwnerHealthy(ownerFile2.owner.pid)) {
191073
191219
  return false;
@@ -191097,11 +191243,11 @@ var ProxyRegistryManager = class {
191097
191243
  }
191098
191244
  const releaseLock = await this.acquireLock();
191099
191245
  try {
191100
- if (fs22.existsSync(this.ownerPath)) {
191101
- const content = fs22.readFileSync(this.ownerPath, "utf-8");
191246
+ if (fs23.existsSync(this.ownerPath)) {
191247
+ const content = fs23.readFileSync(this.ownerPath, "utf-8");
191102
191248
  const ownerFile = JSON.parse(content);
191103
191249
  if (ownerFile.owner.pid === process.pid) {
191104
- fs22.unlinkSync(this.ownerPath);
191250
+ fs23.unlinkSync(this.ownerPath);
191105
191251
  }
191106
191252
  }
191107
191253
  this.isOwner = false;
@@ -191113,12 +191259,12 @@ var ProxyRegistryManager = class {
191113
191259
  * Get the current proxy owner.
191114
191260
  */
191115
191261
  async getProxyOwner() {
191116
- if (!fs22.existsSync(this.ownerPath)) {
191262
+ if (!fs23.existsSync(this.ownerPath)) {
191117
191263
  return null;
191118
191264
  }
191119
191265
  const releaseLock = await this.acquireLock();
191120
191266
  try {
191121
- const content = fs22.readFileSync(this.ownerPath, "utf-8");
191267
+ const content = fs23.readFileSync(this.ownerPath, "utf-8");
191122
191268
  const ownerFile = JSON.parse(content);
191123
191269
  if (!await this.isProxyOwnerHealthy(ownerFile.owner.pid)) {
191124
191270
  return null;
@@ -191215,7 +191361,7 @@ var ProxyRegistryManager = class {
191215
191361
  */
191216
191362
  watchRegistry(onChange) {
191217
191363
  this.ensureProxyDir();
191218
- if (!fs22.existsSync(this.registryPath)) {
191364
+ if (!fs23.existsSync(this.registryPath)) {
191219
191365
  const emptyRegistry = {
191220
191366
  version: 1,
191221
191367
  keys: {},
@@ -191258,13 +191404,13 @@ var ProxyRegistryManager = class {
191258
191404
  async attemptElection(key) {
191259
191405
  const releaseLock = await this.acquireLock();
191260
191406
  try {
191261
- if (fs22.existsSync(this.ownerPath)) {
191262
- const content = fs22.readFileSync(this.ownerPath, "utf-8");
191407
+ if (fs23.existsSync(this.ownerPath)) {
191408
+ const content = fs23.readFileSync(this.ownerPath, "utf-8");
191263
191409
  const ownerFile2 = JSON.parse(content);
191264
191410
  if (await this.isProxyOwnerHealthy(ownerFile2.owner.pid)) {
191265
191411
  return false;
191266
191412
  }
191267
- fs22.unlinkSync(this.ownerPath);
191413
+ fs23.unlinkSync(this.ownerPath);
191268
191414
  }
191269
191415
  const ownerFile = {
191270
191416
  version: 1,
@@ -191282,7 +191428,7 @@ var ProxyRegistryManager = class {
191282
191428
  }
191283
191429
  }
191284
191430
  readRegistry() {
191285
- if (!fs22.existsSync(this.registryPath)) {
191431
+ if (!fs23.existsSync(this.registryPath)) {
191286
191432
  return {
191287
191433
  version: 1,
191288
191434
  keys: {},
@@ -191290,7 +191436,7 @@ var ProxyRegistryManager = class {
191290
191436
  };
191291
191437
  }
191292
191438
  try {
191293
- const content = fs22.readFileSync(this.registryPath, "utf-8");
191439
+ const content = fs23.readFileSync(this.registryPath, "utf-8");
191294
191440
  return JSON.parse(content);
191295
191441
  } catch {
191296
191442
  return {
@@ -191303,8 +191449,8 @@ var ProxyRegistryManager = class {
191303
191449
  writeFileAtomic(filePath, data) {
191304
191450
  this.ensureProxyDir();
191305
191451
  const tmpPath = filePath + ".tmp";
191306
- fs22.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
191307
- fs22.renameSync(tmpPath, filePath);
191452
+ fs23.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
191453
+ fs23.renameSync(tmpPath, filePath);
191308
191454
  }
191309
191455
  };
191310
191456
 
@@ -191411,9 +191557,17 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191411
191557
  const { exit } = useApp2();
191412
191558
  const [state, setState] = useState5(() => {
191413
191559
  const setupDone = tunnelEnabled || !systemSetupNeeded();
191560
+ let setupStep;
191561
+ if (!setupDone) {
191562
+ if (os11.platform() === "darwin") {
191563
+ setupStep = dnsSetupNeeded() ? "setup-dns" : "setup-tls";
191564
+ } else {
191565
+ setupStep = "setup-combined";
191566
+ }
191567
+ }
191414
191568
  return {
191415
- status: setupDone ? "loading" : "installing-ca",
191416
- ...setupDone ? {} : { caInstallPhase: "prompt" },
191569
+ status: setupDone ? "loading" : "setup",
191570
+ ...setupStep ? { setupStep, setupStepPhase: "prompt" } : {},
191417
191571
  resources: /* @__PURE__ */ new Map(),
191418
191572
  resourceStatus: /* @__PURE__ */ new Map(),
191419
191573
  services: [],
@@ -191425,28 +191579,41 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191425
191579
  };
191426
191580
  });
191427
191581
  useEffect3(() => {
191428
- if (state.status === "installing-ca" && state.caInstallPhase === "installing") {
191429
- installSystemConfig();
191430
- }
191431
- }, [state.status, state.caInstallPhase]);
191432
- useInput4((_input, key) => {
191433
- if (key.return) {
191434
- setState((s) => ({ ...s, caInstallPhase: "installing" }));
191435
- }
191436
- }, { isActive: state.status === "installing-ca" && state.caInstallPhase === "prompt" });
191437
- async function installSystemConfig() {
191582
+ if (state.status !== "setup" || state.setupStepPhase !== "installing") return;
191438
191583
  try {
191439
- performSystemSetup();
191440
- setState((s) => ({ ...s, status: "loading", caInstallPhase: "done" }));
191441
- setReadyToStart(true);
191584
+ if (state.setupStep === "setup-dns") {
191585
+ ensureCAGenerated();
191586
+ performDnsSetup();
191587
+ } else if (state.setupStep === "setup-tls") {
191588
+ ensureCAGenerated();
191589
+ performTlsTrust();
191590
+ } else if (state.setupStep === "setup-combined") {
191591
+ performSystemSetup();
191592
+ }
191593
+ const nextStep = state.setupStep === "setup-dns" && tlsSetupNeeded() ? "setup-tls" : void 0;
191594
+ if (nextStep) {
191595
+ setState((s) => ({
191596
+ ...s,
191597
+ setupStep: nextStep,
191598
+ setupStepPhase: "prompt"
191599
+ }));
191600
+ } else {
191601
+ setState((s) => ({ ...s, status: "loading", setupStepPhase: "done" }));
191602
+ setReadyToStart(true);
191603
+ }
191442
191604
  } catch (err) {
191443
191605
  setState((s) => ({
191444
191606
  ...s,
191445
- caInstallPhase: "error",
191446
- caError: err instanceof Error ? err.message : String(err)
191607
+ setupStepPhase: "error",
191608
+ setupError: err instanceof Error ? err.message : String(err)
191447
191609
  }));
191448
191610
  }
191449
- }
191611
+ }, [state.status, state.setupStepPhase]);
191612
+ useInput4((_input, key) => {
191613
+ if (state.status === "setup" && state.setupStepPhase === "prompt" && key.return) {
191614
+ setState((s) => ({ ...s, setupStepPhase: "installing" }));
191615
+ }
191616
+ });
191450
191617
  const shuttingDown = useRef(false);
191451
191618
  const startTimeRef = useRef(null);
191452
191619
  const stateManagerRef = useRef(null);
@@ -191627,7 +191794,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191627
191794
  const configPath = path20.join(process.cwd(), "specific.hcl");
191628
191795
  const watcher = watchConfigFile(configPath, 1e3, () => {
191629
191796
  try {
191630
- const hcl = fs23.readFileSync(configPath, "utf-8");
191797
+ const hcl = fs24.readFileSync(configPath, "utf-8");
191631
191798
  parseConfig(hcl).then(() => {
191632
191799
  triggerReload();
191633
191800
  }).catch((err) => {
@@ -191753,7 +191920,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191753
191920
  return;
191754
191921
  }
191755
191922
  const configPath = path20.join(process.cwd(), "specific.hcl");
191756
- if (!fs23.existsSync(configPath)) {
191923
+ if (!fs24.existsSync(configPath)) {
191757
191924
  writeLog("system", "Waiting for specific.hcl to appear");
191758
191925
  setState((s) => ({
191759
191926
  ...s,
@@ -191771,7 +191938,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191771
191938
  return;
191772
191939
  }
191773
191940
  let config2;
191774
- const hcl = fs23.readFileSync(configPath, "utf-8");
191941
+ const hcl = fs24.readFileSync(configPath, "utf-8");
191775
191942
  try {
191776
191943
  config2 = await parseConfig(hcl);
191777
191944
  } catch (err) {
@@ -192098,7 +192265,7 @@ Add them to the config block in specific.local`);
192098
192265
  if (service.volumes) {
192099
192266
  for (const vol of service.volumes) {
192100
192267
  const volumeDir = path20.resolve(`.specific/keys/${instanceKey}/data/volumes/${service.name}/${vol.name}`);
192101
- fs23.mkdirSync(volumeDir, { recursive: true });
192268
+ fs24.mkdirSync(volumeDir, { recursive: true });
192102
192269
  volumePaths.set(vol.name, volumeDir);
192103
192270
  }
192104
192271
  }
@@ -192154,7 +192321,7 @@ Add them to the config block in specific.local`);
192154
192321
  if (service.volumes) {
192155
192322
  for (const vol of service.volumes) {
192156
192323
  const volumeDir = path20.resolve(`.specific/keys/${instanceKey}/data/volumes/${service.name}/${vol.name}`);
192157
- fs23.mkdirSync(volumeDir, { recursive: true });
192324
+ fs24.mkdirSync(volumeDir, { recursive: true });
192158
192325
  volumePaths.set(vol.name, volumeDir);
192159
192326
  }
192160
192327
  }
@@ -192447,15 +192614,17 @@ Add them to the config block in specific.local`);
192447
192614
  });
192448
192615
  };
192449
192616
  }, [reloadTrigger, readyToStart, instanceKey]);
192450
- if (state.status === "installing-ca") {
192451
- if (state.caInstallPhase === "prompt") {
192452
- return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Configure development environment"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "We need to do a one-time setup for all your Specific projects."), /* @__PURE__ */ React6.createElement(Text6, null, "This is so we can run your apps locally with secure URLs:"), /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "green" }, "https://your-app.spcf.localhost"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "You will be prompted to authorize with your password."), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Press ", /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Enter"), " to authorize."));
192617
+ if (state.status === "setup") {
192618
+ if (state.setupStepPhase === "prompt") {
192619
+ const isDns = state.setupStep === "setup-dns";
192620
+ const isTls = state.setupStep === "setup-tls";
192621
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Configure development environment"), /* @__PURE__ */ React6.createElement(Text6, null, " "), (isDns || state.setupStep === "setup-combined") && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, null, "We need to do a one-time setup for all your Specific projects."), /* @__PURE__ */ React6.createElement(Text6, null, "This is so we can run your apps locally with secure URLs:"), /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "green" }, "https://your-app.spcf.localhost"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "You will be prompted to authorize with your password.")), isTls && /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, null, "We need to trust a development certificate for HTTPS."), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "You will be prompted to authorize.")), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Press ", /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Enter"), " to continue."));
192453
192622
  }
192454
- if (state.caInstallPhase === "installing") {
192623
+ if (state.setupStepPhase === "installing") {
192455
192624
  return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Configure development environment"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Awaiting authorization\u2026"));
192456
192625
  }
192457
- if (state.caInstallPhase === "error") {
192458
- return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Setup failed: ", state.caError));
192626
+ if (state.setupStepPhase === "error") {
192627
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Setup failed: ", state.setupError));
192459
192628
  }
192460
192629
  }
192461
192630
  if (state.status === "loading") {
@@ -192687,7 +192856,9 @@ Add them to the config block in specific.local`);
192687
192856
  return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Static, { items: staticItems }, (item) => /* @__PURE__ */ React6.createElement(Box6, { key: item.key }, item.content)));
192688
192857
  }
192689
192858
  function devCommand(instanceKey, tunnelEnabled = false) {
192690
- render4(/* @__PURE__ */ React6.createElement(DevUI, { instanceKey, tunnelEnabled }));
192859
+ const options2 = isInteractive() ? {} : { stdin: new Readable2({ read() {
192860
+ } }) };
192861
+ render4(/* @__PURE__ */ React6.createElement(DevUI, { instanceKey, tunnelEnabled }), options2);
192691
192862
  }
192692
192863
 
192693
192864
  // src/lib/dev/git-worktree.ts
@@ -192734,12 +192905,12 @@ init_open();
192734
192905
  import React7, { useState as useState6, useEffect as useEffect4, useCallback } from "react";
192735
192906
  import { render as render5, Text as Text7, Box as Box7, useApp as useApp3, useInput as useInput5 } from "ink";
192736
192907
  import Spinner5 from "ink-spinner";
192737
- import * as fs25 from "fs";
192908
+ import * as fs26 from "fs";
192738
192909
  import * as path23 from "path";
192739
192910
 
192740
192911
  // src/lib/tarball/create.ts
192741
192912
  import { execSync as execSync4 } from "child_process";
192742
- import * as fs24 from "fs";
192913
+ import * as fs25 from "fs";
192743
192914
  import * as path22 from "path";
192744
192915
  import { createTarPacker, createEntryItemGenerator } from "tar-vern";
192745
192916
  function isInsideGitRepository(dir) {
@@ -192797,7 +192968,7 @@ var EXCLUDED_DIRS = [
192797
192968
  ];
192798
192969
  async function collectPaths(baseDir, currentDir, exclude) {
192799
192970
  const results = [];
192800
- const entries = await fs24.promises.readdir(currentDir, { withFileTypes: true });
192971
+ const entries = await fs25.promises.readdir(currentDir, { withFileTypes: true });
192801
192972
  for (const entry of entries) {
192802
192973
  const fullPath = path22.join(currentDir, entry.name);
192803
192974
  const relativePath = path22.relative(baseDir, fullPath);
@@ -192816,7 +192987,7 @@ async function collectPaths(baseDir, currentDir, exclude) {
192816
192987
  async function createTarArchive(projectDir) {
192817
192988
  writeLog("tarball", "Creating tarball using tar-vern (non-git project)");
192818
192989
  const configPath = path22.join(projectDir, "specific.hcl");
192819
- if (!fs24.existsSync(configPath)) {
192990
+ if (!fs25.existsSync(configPath)) {
192820
192991
  throw new Error("specific.hcl not found in project directory");
192821
192992
  }
192822
192993
  const relativePaths = await collectPaths(projectDir, projectDir, EXCLUDED_DIRS);
@@ -193738,12 +193909,12 @@ function DeployUI({ environment, config }) {
193738
193909
  }
193739
193910
  async function deployCommand(environment) {
193740
193911
  const configPath = path23.join(process.cwd(), "specific.hcl");
193741
- if (!fs25.existsSync(configPath)) {
193912
+ if (!fs26.existsSync(configPath)) {
193742
193913
  console.error("Error: No specific.hcl found in current directory");
193743
193914
  process.exit(1);
193744
193915
  }
193745
193916
  let config;
193746
- const hcl = fs25.readFileSync(configPath, "utf-8");
193917
+ const hcl = fs26.readFileSync(configPath, "utf-8");
193747
193918
  try {
193748
193919
  config = await parseConfig(hcl);
193749
193920
  } catch (err) {
@@ -193764,7 +193935,7 @@ async function deployCommand(environment) {
193764
193935
 
193765
193936
  // src/commands/exec.tsx
193766
193937
  import { spawn as spawn6 } from "child_process";
193767
- import * as fs26 from "fs";
193938
+ import * as fs27 from "fs";
193768
193939
  import * as path24 from "path";
193769
193940
  function startSpinner(text) {
193770
193941
  const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
@@ -193807,12 +193978,12 @@ async function execCommand(serviceName, command, instanceKey = "default") {
193807
193978
  }
193808
193979
  };
193809
193980
  const configPath = path24.join(process.cwd(), "specific.hcl");
193810
- if (!fs26.existsSync(configPath)) {
193981
+ if (!fs27.existsSync(configPath)) {
193811
193982
  console.error("Error: No specific.hcl found in current directory");
193812
193983
  process.exit(1);
193813
193984
  }
193814
193985
  let config;
193815
- const hcl = fs26.readFileSync(configPath, "utf-8");
193986
+ const hcl = fs27.readFileSync(configPath, "utf-8");
193816
193987
  try {
193817
193988
  config = await parseConfig(hcl);
193818
193989
  } catch (err) {
@@ -193982,7 +194153,7 @@ async function execCommand(serviceName, command, instanceKey = "default") {
193982
194153
 
193983
194154
  // src/commands/psql.tsx
193984
194155
  import { spawn as spawn7 } from "child_process";
193985
- import * as fs27 from "fs";
194156
+ import * as fs28 from "fs";
193986
194157
  import * as path25 from "path";
193987
194158
  async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []) {
193988
194159
  let startedResources = [];
@@ -194001,12 +194172,12 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
194001
194172
  }
194002
194173
  };
194003
194174
  const configPath = path25.join(process.cwd(), "specific.hcl");
194004
- if (!fs27.existsSync(configPath)) {
194175
+ if (!fs28.existsSync(configPath)) {
194005
194176
  console.error("Error: No specific.hcl found in current directory");
194006
194177
  process.exit(1);
194007
194178
  }
194008
194179
  let config;
194009
- const hcl = fs27.readFileSync(configPath, "utf-8");
194180
+ const hcl = fs28.readFileSync(configPath, "utf-8");
194010
194181
  try {
194011
194182
  config = await parseConfig(hcl);
194012
194183
  } catch (err) {
@@ -194130,7 +194301,7 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
194130
194301
 
194131
194302
  // src/commands/reshape.tsx
194132
194303
  import { spawn as spawn8 } from "child_process";
194133
- import * as fs28 from "fs";
194304
+ import * as fs29 from "fs";
194134
194305
  import * as path26 from "path";
194135
194306
  var VALID_ACTIONS = ["start", "complete", "status", "abort", "check"];
194136
194307
  var MIGRATION_SUBCOMMANDS = ["start", "complete", "abort"];
@@ -194148,8 +194319,8 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
194148
194319
  let migrationsDir = "migrations";
194149
194320
  let targetDb;
194150
194321
  try {
194151
- if (fs28.existsSync(configPath)) {
194152
- const configContent = fs28.readFileSync(configPath, "utf-8");
194322
+ if (fs29.existsSync(configPath)) {
194323
+ const configContent = fs29.readFileSync(configPath, "utf-8");
194153
194324
  config = await parseConfig(configContent);
194154
194325
  if (databaseName) {
194155
194326
  const postgresConfig = config.postgres.find((p) => p.name === databaseName);
@@ -194285,7 +194456,7 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
194285
194456
  const reshapeArgs = isMigrationSubcommand ? ["migration", action] : [action];
194286
194457
  const fullMigrationsPath = path26.join(process.cwd(), migrationsDir);
194287
194458
  if (action === "check" || action === "start") {
194288
- if (fs28.existsSync(fullMigrationsPath)) {
194459
+ if (fs29.existsSync(fullMigrationsPath)) {
194289
194460
  reshapeArgs.push("--dirs", fullMigrationsPath);
194290
194461
  } else if (action === "check") {
194291
194462
  console.error(`Error: Migrations directory not found: ${fullMigrationsPath}`);
@@ -194335,7 +194506,7 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
194335
194506
  import React8, { useState as useState7, useEffect as useEffect5 } from "react";
194336
194507
  import { render as render6, Text as Text8, Box as Box8 } from "ink";
194337
194508
  import Spinner6 from "ink-spinner";
194338
- import * as fs29 from "fs";
194509
+ import * as fs30 from "fs";
194339
194510
  import * as path27 from "path";
194340
194511
  function CleanUI({ instanceKey }) {
194341
194512
  const [state, setState] = useState7({ status: "checking" });
@@ -194343,13 +194514,13 @@ function CleanUI({ instanceKey }) {
194343
194514
  async function clean() {
194344
194515
  const projectRoot = process.cwd();
194345
194516
  const specificDir = path27.join(projectRoot, ".specific");
194346
- if (!fs29.existsSync(specificDir)) {
194517
+ if (!fs30.existsSync(specificDir)) {
194347
194518
  setState({ status: "nothing" });
194348
194519
  return;
194349
194520
  }
194350
194521
  if (instanceKey) {
194351
194522
  const keyDir = path27.join(specificDir, "keys", instanceKey);
194352
- if (!fs29.existsSync(keyDir)) {
194523
+ if (!fs30.existsSync(keyDir)) {
194353
194524
  setState({ status: "nothing" });
194354
194525
  return;
194355
194526
  }
@@ -194365,7 +194536,7 @@ function CleanUI({ instanceKey }) {
194365
194536
  await stateManager.cleanStaleState();
194366
194537
  setState({ status: "cleaning" });
194367
194538
  try {
194368
- fs29.rmSync(keyDir, { recursive: true, force: true });
194539
+ fs30.rmSync(keyDir, { recursive: true, force: true });
194369
194540
  setState({ status: "success" });
194370
194541
  } catch (err) {
194371
194542
  setState({
@@ -194375,12 +194546,12 @@ function CleanUI({ instanceKey }) {
194375
194546
  }
194376
194547
  } else {
194377
194548
  const keysDir = path27.join(specificDir, "keys");
194378
- if (!fs29.existsSync(keysDir)) {
194549
+ if (!fs30.existsSync(keysDir)) {
194379
194550
  setState({ status: "nothing" });
194380
194551
  return;
194381
194552
  }
194382
- const keys = fs29.readdirSync(keysDir).filter(
194383
- (f) => fs29.statSync(path27.join(keysDir, f)).isDirectory()
194553
+ const keys = fs30.readdirSync(keysDir).filter(
194554
+ (f) => fs30.statSync(path27.join(keysDir, f)).isDirectory()
194384
194555
  );
194385
194556
  for (const key of keys) {
194386
194557
  const stateManager2 = new InstanceStateManager(projectRoot, key);
@@ -194404,7 +194575,7 @@ function CleanUI({ instanceKey }) {
194404
194575
  }
194405
194576
  setState({ status: "cleaning" });
194406
194577
  try {
194407
- fs29.rmSync(keysDir, { recursive: true, force: true });
194578
+ fs30.rmSync(keysDir, { recursive: true, force: true });
194408
194579
  setState({ status: "success" });
194409
194580
  } catch (err) {
194410
194581
  setState({
@@ -194569,7 +194740,7 @@ import { render as render9, Text as Text11, Box as Box10, useApp as useApp6 } fr
194569
194740
  import Spinner7 from "ink-spinner";
194570
194741
 
194571
194742
  // src/lib/update.ts
194572
- import * as fs30 from "fs";
194743
+ import * as fs31 from "fs";
194573
194744
  import * as path28 from "path";
194574
194745
  var BINARIES_BASE_URL = "https://binaries.specific.dev/cli";
194575
194746
  function compareVersions(a, b) {
@@ -194584,7 +194755,7 @@ function compareVersions(a, b) {
194584
194755
  return 0;
194585
194756
  }
194586
194757
  async function checkForUpdate() {
194587
- const currentVersion = "0.1.81";
194758
+ const currentVersion = "0.1.83";
194588
194759
  const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
194589
194760
  if (!response.ok) {
194590
194761
  throw new Error(`Failed to check for updates: HTTP ${response.status}`);
@@ -194600,7 +194771,7 @@ function isBinaryWritable() {
194600
194771
  const binaryPath = getCurrentBinaryPath();
194601
194772
  const dir = path28.dirname(binaryPath);
194602
194773
  try {
194603
- fs30.accessSync(dir, fs30.constants.W_OK);
194774
+ fs31.accessSync(dir, fs31.constants.W_OK);
194604
194775
  return true;
194605
194776
  } catch {
194606
194777
  return false;
@@ -194611,21 +194782,21 @@ async function performUpdate(version, onProgress) {
194611
194782
  const binaryDir = path28.dirname(binaryPath);
194612
194783
  const tempPath = path28.join(binaryDir, `.specific-update-${process.pid}`);
194613
194784
  try {
194614
- const { platform: platform5, arch: arch3 } = getPlatformInfo();
194615
- const url = `${BINARIES_BASE_URL}/${version}/specific-${platform5}-${arch3}`;
194785
+ const { platform: platform8, arch: arch3 } = getPlatformInfo();
194786
+ const url = `${BINARIES_BASE_URL}/${version}/specific-${platform8}-${arch3}`;
194616
194787
  await downloadFile(url, tempPath, onProgress);
194617
- const stat4 = fs30.statSync(tempPath);
194788
+ const stat4 = fs31.statSync(tempPath);
194618
194789
  if (stat4.size === 0) {
194619
194790
  throw new Error("Downloaded binary is empty");
194620
194791
  }
194621
- fs30.chmodSync(tempPath, 493);
194792
+ fs31.chmodSync(tempPath, 493);
194622
194793
  onProgress?.({ phase: "finalizing" });
194623
- fs30.unlinkSync(binaryPath);
194624
- fs30.renameSync(tempPath, binaryPath);
194794
+ fs31.unlinkSync(binaryPath);
194795
+ fs31.renameSync(tempPath, binaryPath);
194625
194796
  } catch (error) {
194626
194797
  try {
194627
- if (fs30.existsSync(tempPath)) {
194628
- fs30.unlinkSync(tempPath);
194798
+ if (fs31.existsSync(tempPath)) {
194799
+ fs31.unlinkSync(tempPath);
194629
194800
  }
194630
194801
  } catch {
194631
194802
  }
@@ -194635,21 +194806,21 @@ async function performUpdate(version, onProgress) {
194635
194806
 
194636
194807
  // src/lib/background-update.ts
194637
194808
  import { spawn as spawn9 } from "child_process";
194638
- import * as fs31 from "fs";
194809
+ import * as fs32 from "fs";
194639
194810
  import * as path29 from "path";
194640
- import * as os9 from "os";
194641
- var SPECIFIC_DIR = path29.join(os9.homedir(), ".specific");
194811
+ import * as os12 from "os";
194812
+ var SPECIFIC_DIR = path29.join(os12.homedir(), ".specific");
194642
194813
  var RATE_LIMIT_FILE = path29.join(SPECIFIC_DIR, "last-update-check");
194643
194814
  var LOCK_FILE = path29.join(SPECIFIC_DIR, "update.lock");
194644
194815
  var RATE_LIMIT_MS = 60 * 60 * 1e3;
194645
194816
  var STALE_LOCK_MS = 10 * 60 * 1e3;
194646
194817
  function writeCheckTimestamp() {
194647
- fs31.mkdirSync(SPECIFIC_DIR, { recursive: true });
194648
- fs31.writeFileSync(RATE_LIMIT_FILE, String(Date.now()), "utf-8");
194818
+ fs32.mkdirSync(SPECIFIC_DIR, { recursive: true });
194819
+ fs32.writeFileSync(RATE_LIMIT_FILE, String(Date.now()), "utf-8");
194649
194820
  }
194650
194821
  function isRateLimited() {
194651
194822
  try {
194652
- const content = fs31.readFileSync(RATE_LIMIT_FILE, "utf-8").trim();
194823
+ const content = fs32.readFileSync(RATE_LIMIT_FILE, "utf-8").trim();
194653
194824
  const lastCheck = parseInt(content, 10);
194654
194825
  if (isNaN(lastCheck)) return false;
194655
194826
  return Date.now() - lastCheck < RATE_LIMIT_MS;
@@ -194783,7 +194954,7 @@ function updateCommand() {
194783
194954
  var program = new Command();
194784
194955
  var env = "production";
194785
194956
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
194786
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.81").enablePositionalOptions();
194957
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.83").enablePositionalOptions();
194787
194958
  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));
194788
194959
  program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
194789
194960
  program.command("check").description("Validate specific.hcl configuration").action(checkCommand);