@specific.dev/cli 0.1.81 → 0.1.82

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 (69) 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 +490 -322
  64. package/dist/docs/postgres.md +6 -0
  65. package/dist/postinstall.js +1 -1
  66. package/package.json +1 -1
  67. /package/dist/admin/_next/static/{z1dgCOMTsxqy8ZR3zdNfr → hBmPSh2M5utps8oJiVAZU}/_buildManifest.js +0 -0
  68. /package/dist/admin/_next/static/{z1dgCOMTsxqy8ZR3zdNfr → hBmPSh2M5utps8oJiVAZU}/_clientMiddlewareManifest.json +0 -0
  69. /package/dist/admin/_next/static/{z1dgCOMTsxqy8ZR3zdNfr → hBmPSh2M5utps8oJiVAZU}/_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.82",
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,8 @@ 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 * as fs24 from "fs";
186532
+ import * as os11 from "os";
186388
186533
  import * as path20 from "path";
186389
186534
 
186390
186535
  // node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
@@ -188139,7 +188284,7 @@ var PortAllocator = class {
188139
188284
  };
188140
188285
 
188141
188286
  // src/lib/dev/stable-port-allocator.ts
188142
- import * as fs14 from "fs";
188287
+ import * as fs15 from "fs";
188143
188288
  import * as path10 from "path";
188144
188289
  var PORT_RANGE_START2 = 4e4;
188145
188290
  var PORT_RANGE_END2 = 49999;
@@ -188154,11 +188299,11 @@ var StablePortAllocator = class {
188154
188299
  this.loadPorts();
188155
188300
  }
188156
188301
  loadPorts() {
188157
- if (!fs14.existsSync(this.portsFilePath)) {
188302
+ if (!fs15.existsSync(this.portsFilePath)) {
188158
188303
  return;
188159
188304
  }
188160
188305
  try {
188161
- const content = fs14.readFileSync(this.portsFilePath, "utf-8");
188306
+ const content = fs15.readFileSync(this.portsFilePath, "utf-8");
188162
188307
  const data = JSON.parse(content);
188163
188308
  if (data.version === 1 && data.ports) {
188164
188309
  this.savedPorts = data.ports;
@@ -188171,14 +188316,14 @@ var StablePortAllocator = class {
188171
188316
  }
188172
188317
  }
188173
188318
  savePorts() {
188174
- if (!fs14.existsSync(this.portsDir)) {
188175
- fs14.mkdirSync(this.portsDir, { recursive: true });
188319
+ if (!fs15.existsSync(this.portsDir)) {
188320
+ fs15.mkdirSync(this.portsDir, { recursive: true });
188176
188321
  }
188177
188322
  const data = {
188178
188323
  version: 1,
188179
188324
  ports: this.savedPorts
188180
188325
  };
188181
- fs14.writeFileSync(this.portsFilePath, JSON.stringify(data, null, 2));
188326
+ fs15.writeFileSync(this.portsFilePath, JSON.stringify(data, null, 2));
188182
188327
  }
188183
188328
  allocateRandom() {
188184
188329
  const rangeSize = PORT_RANGE_END2 - PORT_RANGE_START2 + 1;
@@ -188205,7 +188350,7 @@ var StablePortAllocator = class {
188205
188350
  };
188206
188351
 
188207
188352
  // src/lib/dev/database-manager.ts
188208
- import * as fs15 from "fs";
188353
+ import * as fs16 from "fs";
188209
188354
  import * as path11 from "path";
188210
188355
  import * as net from "net";
188211
188356
  import { spawn } from "child_process";
@@ -188217,9 +188362,9 @@ async function startPostgres(pg, port, dataDir, onProgress) {
188217
188362
  const password = "postgres";
188218
188363
  const libraryEnv = getLibraryEnv(binary);
188219
188364
  const env2 = { ...process.env, ...libraryEnv };
188220
- const dataExists = fs15.existsSync(dbDataPath);
188365
+ const dataExists = fs16.existsSync(dbDataPath);
188221
188366
  if (!dataExists) {
188222
- fs15.mkdirSync(dbDataPath, { recursive: true });
188367
+ fs16.mkdirSync(dbDataPath, { recursive: true });
188223
188368
  await runCommand(
188224
188369
  binary.executables["initdb"],
188225
188370
  ["-D", dbDataPath, "-U", user, "--auth=trust", "--no-locale", "-E", "UTF8"],
@@ -188296,8 +188441,8 @@ async function startRedis(redis, port, onProgress) {
188296
188441
  async function startStorage(storage, port, dataDir) {
188297
188442
  const S3rver = (await import("s3rver")).default;
188298
188443
  const storageDataPath = path11.join(process.cwd(), dataDir, storage.name);
188299
- if (!fs15.existsSync(storageDataPath)) {
188300
- fs15.mkdirSync(storageDataPath, { recursive: true });
188444
+ if (!fs16.existsSync(storageDataPath)) {
188445
+ fs16.mkdirSync(storageDataPath, { recursive: true });
188301
188446
  }
188302
188447
  const host = "127.0.0.1";
188303
188448
  const accessKey = "S3RVER";
@@ -188950,7 +189095,7 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
188950
189095
  }
188951
189096
 
188952
189097
  // src/lib/dev/instance-state.ts
188953
- import * as fs16 from "fs";
189098
+ import * as fs17 from "fs";
188954
189099
  import * as path12 from "path";
188955
189100
  var InstanceStateManager = class {
188956
189101
  stateDir;
@@ -188968,8 +189113,8 @@ var InstanceStateManager = class {
188968
189113
  return this.key;
188969
189114
  }
188970
189115
  ensureStateDir() {
188971
- if (!fs16.existsSync(this.stateDir)) {
188972
- fs16.mkdirSync(this.stateDir, { recursive: true });
189116
+ if (!fs17.existsSync(this.stateDir)) {
189117
+ fs17.mkdirSync(this.stateDir, { recursive: true });
188973
189118
  }
188974
189119
  }
188975
189120
  isProcessRunning(pid) {
@@ -188986,15 +189131,15 @@ var InstanceStateManager = class {
188986
189131
  const startTime = Date.now();
188987
189132
  while (Date.now() - startTime < timeoutMs) {
188988
189133
  try {
188989
- const fd = fs16.openSync(
189134
+ const fd = fs17.openSync(
188990
189135
  this.lockPath,
188991
- fs16.constants.O_CREAT | fs16.constants.O_EXCL | fs16.constants.O_WRONLY
189136
+ fs17.constants.O_CREAT | fs17.constants.O_EXCL | fs17.constants.O_WRONLY
188992
189137
  );
188993
- fs16.writeSync(fd, String(process.pid));
188994
- fs16.closeSync(fd);
189138
+ fs17.writeSync(fd, String(process.pid));
189139
+ fs17.closeSync(fd);
188995
189140
  return () => {
188996
189141
  try {
188997
- fs16.unlinkSync(this.lockPath);
189142
+ fs17.unlinkSync(this.lockPath);
188998
189143
  } catch {
188999
189144
  }
189000
189145
  };
@@ -189003,16 +189148,16 @@ var InstanceStateManager = class {
189003
189148
  if (err.code === "EEXIST") {
189004
189149
  try {
189005
189150
  const lockPid = parseInt(
189006
- fs16.readFileSync(this.lockPath, "utf-8").trim(),
189151
+ fs17.readFileSync(this.lockPath, "utf-8").trim(),
189007
189152
  10
189008
189153
  );
189009
189154
  if (!this.isProcessRunning(lockPid)) {
189010
- fs16.unlinkSync(this.lockPath);
189155
+ fs17.unlinkSync(this.lockPath);
189011
189156
  continue;
189012
189157
  }
189013
189158
  } catch {
189014
189159
  try {
189015
- fs16.unlinkSync(this.lockPath);
189160
+ fs17.unlinkSync(this.lockPath);
189016
189161
  } catch {
189017
189162
  }
189018
189163
  continue;
@@ -189026,12 +189171,12 @@ var InstanceStateManager = class {
189026
189171
  throw new Error("Failed to acquire state lock (timeout)");
189027
189172
  }
189028
189173
  async getExistingInstances() {
189029
- if (!fs16.existsSync(this.statePath)) {
189174
+ if (!fs17.existsSync(this.statePath)) {
189030
189175
  return null;
189031
189176
  }
189032
189177
  const releaseLock = await this.acquireLock();
189033
189178
  try {
189034
- const content = fs16.readFileSync(this.statePath, "utf-8");
189179
+ const content = fs17.readFileSync(this.statePath, "utf-8");
189035
189180
  const state = JSON.parse(content);
189036
189181
  if (!this.isProcessRunning(state.owner.pid)) {
189037
189182
  return null;
@@ -189044,21 +189189,21 @@ var InstanceStateManager = class {
189044
189189
  }
189045
189190
  }
189046
189191
  async cleanStaleState() {
189047
- if (!fs16.existsSync(this.statePath)) {
189192
+ if (!fs17.existsSync(this.statePath)) {
189048
189193
  return false;
189049
189194
  }
189050
189195
  const releaseLock = await this.acquireLock();
189051
189196
  try {
189052
- const content = fs16.readFileSync(this.statePath, "utf-8");
189197
+ const content = fs17.readFileSync(this.statePath, "utf-8");
189053
189198
  const state = JSON.parse(content);
189054
189199
  if (!this.isProcessRunning(state.owner.pid)) {
189055
- fs16.unlinkSync(this.statePath);
189200
+ fs17.unlinkSync(this.statePath);
189056
189201
  return true;
189057
189202
  }
189058
189203
  return false;
189059
189204
  } catch {
189060
189205
  try {
189061
- fs16.unlinkSync(this.statePath);
189206
+ fs17.unlinkSync(this.statePath);
189062
189207
  return true;
189063
189208
  } catch {
189064
189209
  }
@@ -189070,8 +189215,8 @@ var InstanceStateManager = class {
189070
189215
  async claimOwnership(command) {
189071
189216
  const releaseLock = await this.acquireLock();
189072
189217
  try {
189073
- if (fs16.existsSync(this.statePath)) {
189074
- const content = fs16.readFileSync(this.statePath, "utf-8");
189218
+ if (fs17.existsSync(this.statePath)) {
189219
+ const content = fs17.readFileSync(this.statePath, "utf-8");
189075
189220
  const state2 = JSON.parse(content);
189076
189221
  if (this.isProcessRunning(state2.owner.pid)) {
189077
189222
  throw new Error(`Instances already owned by PID ${state2.owner.pid}`);
@@ -189140,8 +189285,8 @@ var InstanceStateManager = class {
189140
189285
  }
189141
189286
  const releaseLock = await this.acquireLock();
189142
189287
  try {
189143
- if (fs16.existsSync(this.statePath)) {
189144
- fs16.unlinkSync(this.statePath);
189288
+ if (fs17.existsSync(this.statePath)) {
189289
+ fs17.unlinkSync(this.statePath);
189145
189290
  }
189146
189291
  this.ownsInstances = false;
189147
189292
  } finally {
@@ -189149,21 +189294,21 @@ var InstanceStateManager = class {
189149
189294
  }
189150
189295
  }
189151
189296
  readState() {
189152
- const content = fs16.readFileSync(this.statePath, "utf-8");
189297
+ const content = fs17.readFileSync(this.statePath, "utf-8");
189153
189298
  return JSON.parse(content);
189154
189299
  }
189155
189300
  writeStateAtomic(state) {
189156
189301
  this.ensureStateDir();
189157
189302
  const tmpPath = this.statePath + ".tmp";
189158
- fs16.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
189159
- fs16.renameSync(tmpPath, this.statePath);
189303
+ fs17.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
189304
+ fs17.renameSync(tmpPath, this.statePath);
189160
189305
  }
189161
189306
  };
189162
189307
 
189163
189308
  // src/lib/dev/http-proxy.ts
189164
189309
  import * as http from "http";
189165
189310
  import * as https from "https";
189166
- import * as fs17 from "fs";
189311
+ import * as fs18 from "fs";
189167
189312
  import * as path13 from "path";
189168
189313
  import { fileURLToPath as fileURLToPath3 } from "url";
189169
189314
  import httpProxy from "http-proxy";
@@ -189586,10 +189731,10 @@ function serveFilesystemFile(res, pathname) {
189586
189731
  res.end("<h1>Forbidden</h1>");
189587
189732
  return;
189588
189733
  }
189589
- if (fs17.existsSync(resolvedPath)) {
189590
- if (fs17.statSync(resolvedPath).isDirectory()) {
189734
+ if (fs18.existsSync(resolvedPath)) {
189735
+ if (fs18.statSync(resolvedPath).isDirectory()) {
189591
189736
  const indexPath2 = path13.join(resolvedPath, "index.html");
189592
- if (fs17.existsSync(indexPath2)) {
189737
+ if (fs18.existsSync(indexPath2)) {
189593
189738
  return serveFile(res, indexPath2);
189594
189739
  }
189595
189740
  } else {
@@ -189597,15 +189742,15 @@ function serveFilesystemFile(res, pathname) {
189597
189742
  }
189598
189743
  }
189599
189744
  const htmlPath = resolvedPath + ".html";
189600
- if (fs17.existsSync(htmlPath)) {
189745
+ if (fs18.existsSync(htmlPath)) {
189601
189746
  return serveFile(res, htmlPath);
189602
189747
  }
189603
189748
  const indexPath = path13.join(resolvedPath, "index.html");
189604
- if (fs17.existsSync(indexPath)) {
189749
+ if (fs18.existsSync(indexPath)) {
189605
189750
  return serveFile(res, indexPath);
189606
189751
  }
189607
189752
  const notFoundPath = path13.join(adminDir, "404.html");
189608
- if (fs17.existsSync(notFoundPath)) {
189753
+ if (fs18.existsSync(notFoundPath)) {
189609
189754
  return serveFileContent(res, notFoundPath, "text/html", 404);
189610
189755
  }
189611
189756
  res.writeHead(404, { "Content-Type": "text/html" });
@@ -189618,7 +189763,7 @@ function serveFile(res, filePath) {
189618
189763
  }
189619
189764
  function serveFileContent(res, filePath, contentType, statusCode = 200) {
189620
189765
  try {
189621
- const content = fs17.readFileSync(filePath);
189766
+ const content = fs18.readFileSync(filePath);
189622
189767
  res.writeHead(statusCode, { "Content-Type": contentType });
189623
189768
  res.end(content);
189624
189769
  } catch (err) {
@@ -189993,7 +190138,7 @@ async function startMailServer(mail, smtpPort, apiPort) {
189993
190138
 
189994
190139
  // src/lib/dev/drizzle-gateway-manager.ts
189995
190140
  import * as net3 from "net";
189996
- import * as fs18 from "fs";
190141
+ import * as fs19 from "fs";
189997
190142
  import * as path15 from "path";
189998
190143
  import { spawn as spawn4 } from "child_process";
189999
190144
  import { randomUUID as randomUUID2 } from "crypto";
@@ -190027,12 +190172,12 @@ async function startDrizzleGateway(postgresInstances, port, configDir, options2)
190027
190172
  );
190028
190173
  const host = "127.0.0.1";
190029
190174
  const drizzleConfigDir = path15.join(configDir, "drizzle-gateway");
190030
- if (!fs18.existsSync(drizzleConfigDir)) {
190031
- fs18.mkdirSync(drizzleConfigDir, { recursive: true });
190175
+ if (!fs19.existsSync(drizzleConfigDir)) {
190176
+ fs19.mkdirSync(drizzleConfigDir, { recursive: true });
190032
190177
  }
190033
190178
  const storeJson = generateStoreJson(postgresInstances);
190034
190179
  const storeJsonPath = path15.join(drizzleConfigDir, "store.json");
190035
- fs18.writeFileSync(storeJsonPath, JSON.stringify(storeJson, null, 2));
190180
+ fs19.writeFileSync(storeJsonPath, JSON.stringify(storeJson, null, 2));
190036
190181
  writeLog("drizzle-gateway", `Starting Drizzle Gateway`);
190037
190182
  writeLog("drizzle-gateway", `STORE_PATH: ${drizzleConfigDir}`);
190038
190183
  writeLog("drizzle-gateway", `PORT: ${port}`);
@@ -190134,16 +190279,16 @@ function detectSyncDatabases(config) {
190134
190279
  }
190135
190280
 
190136
190281
  // src/lib/dev/reshape-watcher.ts
190137
- import * as fs19 from "fs";
190282
+ import * as fs20 from "fs";
190138
190283
  import * as path16 from "path";
190139
190284
  import { spawnSync } from "child_process";
190140
190285
  function getMigrationFiles(dir, log) {
190141
190286
  log(`Scanning migrations directory: ${dir}`);
190142
- if (!fs19.existsSync(dir)) {
190287
+ if (!fs20.existsSync(dir)) {
190143
190288
  log(`Migrations directory does not exist: ${dir}`);
190144
190289
  return [];
190145
190290
  }
190146
- const files = fs19.readdirSync(dir);
190291
+ const files = fs20.readdirSync(dir);
190147
190292
  log(`Found ${files.length} files in directory`);
190148
190293
  const tomlFiles = files.filter((f) => f.endsWith(".toml")).sort((a, b) => a.localeCompare(b));
190149
190294
  log(`Found ${tomlFiles.length} .toml migration files: ${tomlFiles.join(", ") || "(none)"}`);
@@ -190193,7 +190338,7 @@ function runReshape(args, databaseUrl, migrationsDir, reshapeBinaryPath, log) {
190193
190338
  }
190194
190339
  function makeReadOnly(filePath, log) {
190195
190340
  try {
190196
- fs19.chmodSync(filePath, 292);
190341
+ fs20.chmodSync(filePath, 292);
190197
190342
  log(`Set file permissions to read-only (444): ${filePath}`);
190198
190343
  } catch (err) {
190199
190344
  const message = err instanceof Error ? err.message : String(err);
@@ -190279,9 +190424,9 @@ function createReshapeWatcher(options2) {
190279
190424
  };
190280
190425
  const startWatching = () => {
190281
190426
  log(`Starting file watcher for migrations directory...`);
190282
- if (!fs19.existsSync(migrationsDir)) {
190427
+ if (!fs20.existsSync(migrationsDir)) {
190283
190428
  log(`Migrations directory does not exist, creating: ${migrationsDir}`);
190284
- fs19.mkdirSync(migrationsDir, { recursive: true });
190429
+ fs20.mkdirSync(migrationsDir, { recursive: true });
190285
190430
  }
190286
190431
  log(`Watching directory: ${migrationsDir}`);
190287
190432
  watcher = chokidar_default.watch(migrationsDir, {
@@ -190416,7 +190561,7 @@ function createReshapeWatcher(options2) {
190416
190561
  }
190417
190562
 
190418
190563
  // src/lib/dev/temporal-manager.ts
190419
- import * as fs20 from "fs";
190564
+ import * as fs21 from "fs";
190420
190565
  import * as path17 from "path";
190421
190566
  import * as net4 from "net";
190422
190567
  import { spawn as spawn5 } from "child_process";
@@ -190424,8 +190569,8 @@ async function startTemporalDevServer(temporals, grpcPort, uiPort, dataDir, onPr
190424
190569
  const binary = await ensureBinary(temporalBinary, void 0, onProgress);
190425
190570
  const dbPath = path17.join(process.cwd(), dataDir, "temporal.db");
190426
190571
  const dbDir = path17.dirname(dbPath);
190427
- if (!fs20.existsSync(dbDir)) {
190428
- fs20.mkdirSync(dbDir, { recursive: true });
190572
+ if (!fs21.existsSync(dbDir)) {
190573
+ fs21.mkdirSync(dbDir, { recursive: true });
190429
190574
  }
190430
190575
  const host = "127.0.0.1";
190431
190576
  const namespaceArgs = temporals.flatMap((t) => ["--namespace", t.name]);
@@ -190754,7 +190899,7 @@ function watchConfigFile(configPath, debounceMs, onChange) {
190754
190899
  }
190755
190900
 
190756
190901
  // src/lib/dev/subdomain-generator.ts
190757
- import * as fs21 from "fs";
190902
+ import * as fs22 from "fs";
190758
190903
  import * as path18 from "path";
190759
190904
  import { generateSlug } from "random-word-slugs";
190760
190905
  var StableSubdomainAllocator = class {
@@ -190767,11 +190912,11 @@ var StableSubdomainAllocator = class {
190767
190912
  this.loadTunnels();
190768
190913
  }
190769
190914
  loadTunnels() {
190770
- if (!fs21.existsSync(this.tunnelsFilePath)) {
190915
+ if (!fs22.existsSync(this.tunnelsFilePath)) {
190771
190916
  return;
190772
190917
  }
190773
190918
  try {
190774
- const content = fs21.readFileSync(this.tunnelsFilePath, "utf-8");
190919
+ const content = fs22.readFileSync(this.tunnelsFilePath, "utf-8");
190775
190920
  const data = JSON.parse(content);
190776
190921
  if (data.version === 1 && data.baseSlug) {
190777
190922
  this.baseSlug = data.baseSlug;
@@ -190781,14 +190926,14 @@ var StableSubdomainAllocator = class {
190781
190926
  }
190782
190927
  }
190783
190928
  saveTunnels() {
190784
- if (!fs21.existsSync(this.tunnelsDir)) {
190785
- fs21.mkdirSync(this.tunnelsDir, { recursive: true });
190929
+ if (!fs22.existsSync(this.tunnelsDir)) {
190930
+ fs22.mkdirSync(this.tunnelsDir, { recursive: true });
190786
190931
  }
190787
190932
  const data = {
190788
190933
  version: 1,
190789
190934
  baseSlug: this.baseSlug
190790
190935
  };
190791
- fs21.writeFileSync(this.tunnelsFilePath, JSON.stringify(data, null, 2));
190936
+ fs22.writeFileSync(this.tunnelsFilePath, JSON.stringify(data, null, 2));
190792
190937
  }
190793
190938
  generateBaseSlug() {
190794
190939
  return generateSlug(2, {
@@ -190944,9 +191089,9 @@ async function startTunnel(serviceName, endpointName, port, subdomain, callbacks
190944
191089
  }
190945
191090
 
190946
191091
  // src/lib/dev/proxy-registry.ts
190947
- import * as fs22 from "fs";
191092
+ import * as fs23 from "fs";
190948
191093
  import * as path19 from "path";
190949
- import * as os8 from "os";
191094
+ import * as os10 from "os";
190950
191095
  import * as net6 from "net";
190951
191096
  var ProxyRegistryManager = class {
190952
191097
  proxyDir;
@@ -190956,14 +191101,14 @@ var ProxyRegistryManager = class {
190956
191101
  isOwner = false;
190957
191102
  registryWatcher = null;
190958
191103
  constructor() {
190959
- this.proxyDir = path19.join(os8.homedir(), ".specific", "proxy");
191104
+ this.proxyDir = path19.join(os10.homedir(), ".specific", "proxy");
190960
191105
  this.ownerPath = path19.join(this.proxyDir, "owner.json");
190961
191106
  this.registryPath = path19.join(this.proxyDir, "registry.json");
190962
191107
  this.lockPath = path19.join(this.proxyDir, "registry.lock");
190963
191108
  }
190964
191109
  ensureProxyDir() {
190965
- if (!fs22.existsSync(this.proxyDir)) {
190966
- fs22.mkdirSync(this.proxyDir, { recursive: true });
191110
+ if (!fs23.existsSync(this.proxyDir)) {
191111
+ fs23.mkdirSync(this.proxyDir, { recursive: true });
190967
191112
  }
190968
191113
  }
190969
191114
  isProcessRunning(pid) {
@@ -191020,15 +191165,15 @@ var ProxyRegistryManager = class {
191020
191165
  const startTime = Date.now();
191021
191166
  while (Date.now() - startTime < timeoutMs) {
191022
191167
  try {
191023
- const fd = fs22.openSync(
191168
+ const fd = fs23.openSync(
191024
191169
  this.lockPath,
191025
- fs22.constants.O_CREAT | fs22.constants.O_EXCL | fs22.constants.O_WRONLY
191170
+ fs23.constants.O_CREAT | fs23.constants.O_EXCL | fs23.constants.O_WRONLY
191026
191171
  );
191027
- fs22.writeSync(fd, String(process.pid));
191028
- fs22.closeSync(fd);
191172
+ fs23.writeSync(fd, String(process.pid));
191173
+ fs23.closeSync(fd);
191029
191174
  return () => {
191030
191175
  try {
191031
- fs22.unlinkSync(this.lockPath);
191176
+ fs23.unlinkSync(this.lockPath);
191032
191177
  } catch {
191033
191178
  }
191034
191179
  };
@@ -191037,16 +191182,16 @@ var ProxyRegistryManager = class {
191037
191182
  if (err.code === "EEXIST") {
191038
191183
  try {
191039
191184
  const lockPid = parseInt(
191040
- fs22.readFileSync(this.lockPath, "utf-8").trim(),
191185
+ fs23.readFileSync(this.lockPath, "utf-8").trim(),
191041
191186
  10
191042
191187
  );
191043
191188
  if (!this.isProcessRunning(lockPid)) {
191044
- fs22.unlinkSync(this.lockPath);
191189
+ fs23.unlinkSync(this.lockPath);
191045
191190
  continue;
191046
191191
  }
191047
191192
  } catch {
191048
191193
  try {
191049
- fs22.unlinkSync(this.lockPath);
191194
+ fs23.unlinkSync(this.lockPath);
191050
191195
  } catch {
191051
191196
  }
191052
191197
  continue;
@@ -191066,8 +191211,8 @@ var ProxyRegistryManager = class {
191066
191211
  async claimProxyOwnership(key) {
191067
191212
  const releaseLock = await this.acquireLock();
191068
191213
  try {
191069
- if (fs22.existsSync(this.ownerPath)) {
191070
- const content = fs22.readFileSync(this.ownerPath, "utf-8");
191214
+ if (fs23.existsSync(this.ownerPath)) {
191215
+ const content = fs23.readFileSync(this.ownerPath, "utf-8");
191071
191216
  const ownerFile2 = JSON.parse(content);
191072
191217
  if (await this.isProxyOwnerHealthy(ownerFile2.owner.pid)) {
191073
191218
  return false;
@@ -191097,11 +191242,11 @@ var ProxyRegistryManager = class {
191097
191242
  }
191098
191243
  const releaseLock = await this.acquireLock();
191099
191244
  try {
191100
- if (fs22.existsSync(this.ownerPath)) {
191101
- const content = fs22.readFileSync(this.ownerPath, "utf-8");
191245
+ if (fs23.existsSync(this.ownerPath)) {
191246
+ const content = fs23.readFileSync(this.ownerPath, "utf-8");
191102
191247
  const ownerFile = JSON.parse(content);
191103
191248
  if (ownerFile.owner.pid === process.pid) {
191104
- fs22.unlinkSync(this.ownerPath);
191249
+ fs23.unlinkSync(this.ownerPath);
191105
191250
  }
191106
191251
  }
191107
191252
  this.isOwner = false;
@@ -191113,12 +191258,12 @@ var ProxyRegistryManager = class {
191113
191258
  * Get the current proxy owner.
191114
191259
  */
191115
191260
  async getProxyOwner() {
191116
- if (!fs22.existsSync(this.ownerPath)) {
191261
+ if (!fs23.existsSync(this.ownerPath)) {
191117
191262
  return null;
191118
191263
  }
191119
191264
  const releaseLock = await this.acquireLock();
191120
191265
  try {
191121
- const content = fs22.readFileSync(this.ownerPath, "utf-8");
191266
+ const content = fs23.readFileSync(this.ownerPath, "utf-8");
191122
191267
  const ownerFile = JSON.parse(content);
191123
191268
  if (!await this.isProxyOwnerHealthy(ownerFile.owner.pid)) {
191124
191269
  return null;
@@ -191215,7 +191360,7 @@ var ProxyRegistryManager = class {
191215
191360
  */
191216
191361
  watchRegistry(onChange) {
191217
191362
  this.ensureProxyDir();
191218
- if (!fs22.existsSync(this.registryPath)) {
191363
+ if (!fs23.existsSync(this.registryPath)) {
191219
191364
  const emptyRegistry = {
191220
191365
  version: 1,
191221
191366
  keys: {},
@@ -191258,13 +191403,13 @@ var ProxyRegistryManager = class {
191258
191403
  async attemptElection(key) {
191259
191404
  const releaseLock = await this.acquireLock();
191260
191405
  try {
191261
- if (fs22.existsSync(this.ownerPath)) {
191262
- const content = fs22.readFileSync(this.ownerPath, "utf-8");
191406
+ if (fs23.existsSync(this.ownerPath)) {
191407
+ const content = fs23.readFileSync(this.ownerPath, "utf-8");
191263
191408
  const ownerFile2 = JSON.parse(content);
191264
191409
  if (await this.isProxyOwnerHealthy(ownerFile2.owner.pid)) {
191265
191410
  return false;
191266
191411
  }
191267
- fs22.unlinkSync(this.ownerPath);
191412
+ fs23.unlinkSync(this.ownerPath);
191268
191413
  }
191269
191414
  const ownerFile = {
191270
191415
  version: 1,
@@ -191282,7 +191427,7 @@ var ProxyRegistryManager = class {
191282
191427
  }
191283
191428
  }
191284
191429
  readRegistry() {
191285
- if (!fs22.existsSync(this.registryPath)) {
191430
+ if (!fs23.existsSync(this.registryPath)) {
191286
191431
  return {
191287
191432
  version: 1,
191288
191433
  keys: {},
@@ -191290,7 +191435,7 @@ var ProxyRegistryManager = class {
191290
191435
  };
191291
191436
  }
191292
191437
  try {
191293
- const content = fs22.readFileSync(this.registryPath, "utf-8");
191438
+ const content = fs23.readFileSync(this.registryPath, "utf-8");
191294
191439
  return JSON.parse(content);
191295
191440
  } catch {
191296
191441
  return {
@@ -191303,8 +191448,8 @@ var ProxyRegistryManager = class {
191303
191448
  writeFileAtomic(filePath, data) {
191304
191449
  this.ensureProxyDir();
191305
191450
  const tmpPath = filePath + ".tmp";
191306
- fs22.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
191307
- fs22.renameSync(tmpPath, filePath);
191451
+ fs23.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
191452
+ fs23.renameSync(tmpPath, filePath);
191308
191453
  }
191309
191454
  };
191310
191455
 
@@ -191411,9 +191556,17 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191411
191556
  const { exit } = useApp2();
191412
191557
  const [state, setState] = useState5(() => {
191413
191558
  const setupDone = tunnelEnabled || !systemSetupNeeded();
191559
+ let setupStep;
191560
+ if (!setupDone) {
191561
+ if (os11.platform() === "darwin") {
191562
+ setupStep = dnsSetupNeeded() ? "setup-dns" : "setup-tls";
191563
+ } else {
191564
+ setupStep = "setup-combined";
191565
+ }
191566
+ }
191414
191567
  return {
191415
- status: setupDone ? "loading" : "installing-ca",
191416
- ...setupDone ? {} : { caInstallPhase: "prompt" },
191568
+ status: setupDone ? "loading" : "setup",
191569
+ ...setupStep ? { setupStep, setupStepPhase: "prompt" } : {},
191417
191570
  resources: /* @__PURE__ */ new Map(),
191418
191571
  resourceStatus: /* @__PURE__ */ new Map(),
191419
191572
  services: [],
@@ -191425,28 +191578,41 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191425
191578
  };
191426
191579
  });
191427
191580
  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() {
191581
+ if (state.status !== "setup" || state.setupStepPhase !== "installing") return;
191438
191582
  try {
191439
- performSystemSetup();
191440
- setState((s) => ({ ...s, status: "loading", caInstallPhase: "done" }));
191441
- setReadyToStart(true);
191583
+ if (state.setupStep === "setup-dns") {
191584
+ ensureCAGenerated();
191585
+ performDnsSetup();
191586
+ } else if (state.setupStep === "setup-tls") {
191587
+ ensureCAGenerated();
191588
+ performTlsTrust();
191589
+ } else if (state.setupStep === "setup-combined") {
191590
+ performSystemSetup();
191591
+ }
191592
+ const nextStep = state.setupStep === "setup-dns" && tlsSetupNeeded() ? "setup-tls" : void 0;
191593
+ if (nextStep) {
191594
+ setState((s) => ({
191595
+ ...s,
191596
+ setupStep: nextStep,
191597
+ setupStepPhase: "prompt"
191598
+ }));
191599
+ } else {
191600
+ setState((s) => ({ ...s, status: "loading", setupStepPhase: "done" }));
191601
+ setReadyToStart(true);
191602
+ }
191442
191603
  } catch (err) {
191443
191604
  setState((s) => ({
191444
191605
  ...s,
191445
- caInstallPhase: "error",
191446
- caError: err instanceof Error ? err.message : String(err)
191606
+ setupStepPhase: "error",
191607
+ setupError: err instanceof Error ? err.message : String(err)
191447
191608
  }));
191448
191609
  }
191449
- }
191610
+ }, [state.status, state.setupStepPhase]);
191611
+ useInput4((_input, key) => {
191612
+ if (state.status === "setup" && state.setupStepPhase === "prompt" && key.return) {
191613
+ setState((s) => ({ ...s, setupStepPhase: "installing" }));
191614
+ }
191615
+ });
191450
191616
  const shuttingDown = useRef(false);
191451
191617
  const startTimeRef = useRef(null);
191452
191618
  const stateManagerRef = useRef(null);
@@ -191627,7 +191793,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191627
191793
  const configPath = path20.join(process.cwd(), "specific.hcl");
191628
191794
  const watcher = watchConfigFile(configPath, 1e3, () => {
191629
191795
  try {
191630
- const hcl = fs23.readFileSync(configPath, "utf-8");
191796
+ const hcl = fs24.readFileSync(configPath, "utf-8");
191631
191797
  parseConfig(hcl).then(() => {
191632
191798
  triggerReload();
191633
191799
  }).catch((err) => {
@@ -191753,7 +191919,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191753
191919
  return;
191754
191920
  }
191755
191921
  const configPath = path20.join(process.cwd(), "specific.hcl");
191756
- if (!fs23.existsSync(configPath)) {
191922
+ if (!fs24.existsSync(configPath)) {
191757
191923
  writeLog("system", "Waiting for specific.hcl to appear");
191758
191924
  setState((s) => ({
191759
191925
  ...s,
@@ -191771,7 +191937,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191771
191937
  return;
191772
191938
  }
191773
191939
  let config2;
191774
- const hcl = fs23.readFileSync(configPath, "utf-8");
191940
+ const hcl = fs24.readFileSync(configPath, "utf-8");
191775
191941
  try {
191776
191942
  config2 = await parseConfig(hcl);
191777
191943
  } catch (err) {
@@ -192098,7 +192264,7 @@ Add them to the config block in specific.local`);
192098
192264
  if (service.volumes) {
192099
192265
  for (const vol of service.volumes) {
192100
192266
  const volumeDir = path20.resolve(`.specific/keys/${instanceKey}/data/volumes/${service.name}/${vol.name}`);
192101
- fs23.mkdirSync(volumeDir, { recursive: true });
192267
+ fs24.mkdirSync(volumeDir, { recursive: true });
192102
192268
  volumePaths.set(vol.name, volumeDir);
192103
192269
  }
192104
192270
  }
@@ -192154,7 +192320,7 @@ Add them to the config block in specific.local`);
192154
192320
  if (service.volumes) {
192155
192321
  for (const vol of service.volumes) {
192156
192322
  const volumeDir = path20.resolve(`.specific/keys/${instanceKey}/data/volumes/${service.name}/${vol.name}`);
192157
- fs23.mkdirSync(volumeDir, { recursive: true });
192323
+ fs24.mkdirSync(volumeDir, { recursive: true });
192158
192324
  volumePaths.set(vol.name, volumeDir);
192159
192325
  }
192160
192326
  }
@@ -192447,15 +192613,17 @@ Add them to the config block in specific.local`);
192447
192613
  });
192448
192614
  };
192449
192615
  }, [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."));
192616
+ if (state.status === "setup") {
192617
+ if (state.setupStepPhase === "prompt") {
192618
+ const isDns = state.setupStep === "setup-dns";
192619
+ const isTls = state.setupStep === "setup-tls";
192620
+ 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
192621
  }
192454
- if (state.caInstallPhase === "installing") {
192622
+ if (state.setupStepPhase === "installing") {
192455
192623
  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
192624
  }
192457
- if (state.caInstallPhase === "error") {
192458
- return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Setup failed: ", state.caError));
192625
+ if (state.setupStepPhase === "error") {
192626
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Setup failed: ", state.setupError));
192459
192627
  }
192460
192628
  }
192461
192629
  if (state.status === "loading") {
@@ -192734,12 +192902,12 @@ init_open();
192734
192902
  import React7, { useState as useState6, useEffect as useEffect4, useCallback } from "react";
192735
192903
  import { render as render5, Text as Text7, Box as Box7, useApp as useApp3, useInput as useInput5 } from "ink";
192736
192904
  import Spinner5 from "ink-spinner";
192737
- import * as fs25 from "fs";
192905
+ import * as fs26 from "fs";
192738
192906
  import * as path23 from "path";
192739
192907
 
192740
192908
  // src/lib/tarball/create.ts
192741
192909
  import { execSync as execSync4 } from "child_process";
192742
- import * as fs24 from "fs";
192910
+ import * as fs25 from "fs";
192743
192911
  import * as path22 from "path";
192744
192912
  import { createTarPacker, createEntryItemGenerator } from "tar-vern";
192745
192913
  function isInsideGitRepository(dir) {
@@ -192797,7 +192965,7 @@ var EXCLUDED_DIRS = [
192797
192965
  ];
192798
192966
  async function collectPaths(baseDir, currentDir, exclude) {
192799
192967
  const results = [];
192800
- const entries = await fs24.promises.readdir(currentDir, { withFileTypes: true });
192968
+ const entries = await fs25.promises.readdir(currentDir, { withFileTypes: true });
192801
192969
  for (const entry of entries) {
192802
192970
  const fullPath = path22.join(currentDir, entry.name);
192803
192971
  const relativePath = path22.relative(baseDir, fullPath);
@@ -192816,7 +192984,7 @@ async function collectPaths(baseDir, currentDir, exclude) {
192816
192984
  async function createTarArchive(projectDir) {
192817
192985
  writeLog("tarball", "Creating tarball using tar-vern (non-git project)");
192818
192986
  const configPath = path22.join(projectDir, "specific.hcl");
192819
- if (!fs24.existsSync(configPath)) {
192987
+ if (!fs25.existsSync(configPath)) {
192820
192988
  throw new Error("specific.hcl not found in project directory");
192821
192989
  }
192822
192990
  const relativePaths = await collectPaths(projectDir, projectDir, EXCLUDED_DIRS);
@@ -193738,12 +193906,12 @@ function DeployUI({ environment, config }) {
193738
193906
  }
193739
193907
  async function deployCommand(environment) {
193740
193908
  const configPath = path23.join(process.cwd(), "specific.hcl");
193741
- if (!fs25.existsSync(configPath)) {
193909
+ if (!fs26.existsSync(configPath)) {
193742
193910
  console.error("Error: No specific.hcl found in current directory");
193743
193911
  process.exit(1);
193744
193912
  }
193745
193913
  let config;
193746
- const hcl = fs25.readFileSync(configPath, "utf-8");
193914
+ const hcl = fs26.readFileSync(configPath, "utf-8");
193747
193915
  try {
193748
193916
  config = await parseConfig(hcl);
193749
193917
  } catch (err) {
@@ -193764,7 +193932,7 @@ async function deployCommand(environment) {
193764
193932
 
193765
193933
  // src/commands/exec.tsx
193766
193934
  import { spawn as spawn6 } from "child_process";
193767
- import * as fs26 from "fs";
193935
+ import * as fs27 from "fs";
193768
193936
  import * as path24 from "path";
193769
193937
  function startSpinner(text) {
193770
193938
  const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
@@ -193807,12 +193975,12 @@ async function execCommand(serviceName, command, instanceKey = "default") {
193807
193975
  }
193808
193976
  };
193809
193977
  const configPath = path24.join(process.cwd(), "specific.hcl");
193810
- if (!fs26.existsSync(configPath)) {
193978
+ if (!fs27.existsSync(configPath)) {
193811
193979
  console.error("Error: No specific.hcl found in current directory");
193812
193980
  process.exit(1);
193813
193981
  }
193814
193982
  let config;
193815
- const hcl = fs26.readFileSync(configPath, "utf-8");
193983
+ const hcl = fs27.readFileSync(configPath, "utf-8");
193816
193984
  try {
193817
193985
  config = await parseConfig(hcl);
193818
193986
  } catch (err) {
@@ -193982,7 +194150,7 @@ async function execCommand(serviceName, command, instanceKey = "default") {
193982
194150
 
193983
194151
  // src/commands/psql.tsx
193984
194152
  import { spawn as spawn7 } from "child_process";
193985
- import * as fs27 from "fs";
194153
+ import * as fs28 from "fs";
193986
194154
  import * as path25 from "path";
193987
194155
  async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []) {
193988
194156
  let startedResources = [];
@@ -194001,12 +194169,12 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
194001
194169
  }
194002
194170
  };
194003
194171
  const configPath = path25.join(process.cwd(), "specific.hcl");
194004
- if (!fs27.existsSync(configPath)) {
194172
+ if (!fs28.existsSync(configPath)) {
194005
194173
  console.error("Error: No specific.hcl found in current directory");
194006
194174
  process.exit(1);
194007
194175
  }
194008
194176
  let config;
194009
- const hcl = fs27.readFileSync(configPath, "utf-8");
194177
+ const hcl = fs28.readFileSync(configPath, "utf-8");
194010
194178
  try {
194011
194179
  config = await parseConfig(hcl);
194012
194180
  } catch (err) {
@@ -194130,7 +194298,7 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
194130
194298
 
194131
194299
  // src/commands/reshape.tsx
194132
194300
  import { spawn as spawn8 } from "child_process";
194133
- import * as fs28 from "fs";
194301
+ import * as fs29 from "fs";
194134
194302
  import * as path26 from "path";
194135
194303
  var VALID_ACTIONS = ["start", "complete", "status", "abort", "check"];
194136
194304
  var MIGRATION_SUBCOMMANDS = ["start", "complete", "abort"];
@@ -194148,8 +194316,8 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
194148
194316
  let migrationsDir = "migrations";
194149
194317
  let targetDb;
194150
194318
  try {
194151
- if (fs28.existsSync(configPath)) {
194152
- const configContent = fs28.readFileSync(configPath, "utf-8");
194319
+ if (fs29.existsSync(configPath)) {
194320
+ const configContent = fs29.readFileSync(configPath, "utf-8");
194153
194321
  config = await parseConfig(configContent);
194154
194322
  if (databaseName) {
194155
194323
  const postgresConfig = config.postgres.find((p) => p.name === databaseName);
@@ -194285,7 +194453,7 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
194285
194453
  const reshapeArgs = isMigrationSubcommand ? ["migration", action] : [action];
194286
194454
  const fullMigrationsPath = path26.join(process.cwd(), migrationsDir);
194287
194455
  if (action === "check" || action === "start") {
194288
- if (fs28.existsSync(fullMigrationsPath)) {
194456
+ if (fs29.existsSync(fullMigrationsPath)) {
194289
194457
  reshapeArgs.push("--dirs", fullMigrationsPath);
194290
194458
  } else if (action === "check") {
194291
194459
  console.error(`Error: Migrations directory not found: ${fullMigrationsPath}`);
@@ -194335,7 +194503,7 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
194335
194503
  import React8, { useState as useState7, useEffect as useEffect5 } from "react";
194336
194504
  import { render as render6, Text as Text8, Box as Box8 } from "ink";
194337
194505
  import Spinner6 from "ink-spinner";
194338
- import * as fs29 from "fs";
194506
+ import * as fs30 from "fs";
194339
194507
  import * as path27 from "path";
194340
194508
  function CleanUI({ instanceKey }) {
194341
194509
  const [state, setState] = useState7({ status: "checking" });
@@ -194343,13 +194511,13 @@ function CleanUI({ instanceKey }) {
194343
194511
  async function clean() {
194344
194512
  const projectRoot = process.cwd();
194345
194513
  const specificDir = path27.join(projectRoot, ".specific");
194346
- if (!fs29.existsSync(specificDir)) {
194514
+ if (!fs30.existsSync(specificDir)) {
194347
194515
  setState({ status: "nothing" });
194348
194516
  return;
194349
194517
  }
194350
194518
  if (instanceKey) {
194351
194519
  const keyDir = path27.join(specificDir, "keys", instanceKey);
194352
- if (!fs29.existsSync(keyDir)) {
194520
+ if (!fs30.existsSync(keyDir)) {
194353
194521
  setState({ status: "nothing" });
194354
194522
  return;
194355
194523
  }
@@ -194365,7 +194533,7 @@ function CleanUI({ instanceKey }) {
194365
194533
  await stateManager.cleanStaleState();
194366
194534
  setState({ status: "cleaning" });
194367
194535
  try {
194368
- fs29.rmSync(keyDir, { recursive: true, force: true });
194536
+ fs30.rmSync(keyDir, { recursive: true, force: true });
194369
194537
  setState({ status: "success" });
194370
194538
  } catch (err) {
194371
194539
  setState({
@@ -194375,12 +194543,12 @@ function CleanUI({ instanceKey }) {
194375
194543
  }
194376
194544
  } else {
194377
194545
  const keysDir = path27.join(specificDir, "keys");
194378
- if (!fs29.existsSync(keysDir)) {
194546
+ if (!fs30.existsSync(keysDir)) {
194379
194547
  setState({ status: "nothing" });
194380
194548
  return;
194381
194549
  }
194382
- const keys = fs29.readdirSync(keysDir).filter(
194383
- (f) => fs29.statSync(path27.join(keysDir, f)).isDirectory()
194550
+ const keys = fs30.readdirSync(keysDir).filter(
194551
+ (f) => fs30.statSync(path27.join(keysDir, f)).isDirectory()
194384
194552
  );
194385
194553
  for (const key of keys) {
194386
194554
  const stateManager2 = new InstanceStateManager(projectRoot, key);
@@ -194404,7 +194572,7 @@ function CleanUI({ instanceKey }) {
194404
194572
  }
194405
194573
  setState({ status: "cleaning" });
194406
194574
  try {
194407
- fs29.rmSync(keysDir, { recursive: true, force: true });
194575
+ fs30.rmSync(keysDir, { recursive: true, force: true });
194408
194576
  setState({ status: "success" });
194409
194577
  } catch (err) {
194410
194578
  setState({
@@ -194569,7 +194737,7 @@ import { render as render9, Text as Text11, Box as Box10, useApp as useApp6 } fr
194569
194737
  import Spinner7 from "ink-spinner";
194570
194738
 
194571
194739
  // src/lib/update.ts
194572
- import * as fs30 from "fs";
194740
+ import * as fs31 from "fs";
194573
194741
  import * as path28 from "path";
194574
194742
  var BINARIES_BASE_URL = "https://binaries.specific.dev/cli";
194575
194743
  function compareVersions(a, b) {
@@ -194584,7 +194752,7 @@ function compareVersions(a, b) {
194584
194752
  return 0;
194585
194753
  }
194586
194754
  async function checkForUpdate() {
194587
- const currentVersion = "0.1.81";
194755
+ const currentVersion = "0.1.82";
194588
194756
  const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
194589
194757
  if (!response.ok) {
194590
194758
  throw new Error(`Failed to check for updates: HTTP ${response.status}`);
@@ -194600,7 +194768,7 @@ function isBinaryWritable() {
194600
194768
  const binaryPath = getCurrentBinaryPath();
194601
194769
  const dir = path28.dirname(binaryPath);
194602
194770
  try {
194603
- fs30.accessSync(dir, fs30.constants.W_OK);
194771
+ fs31.accessSync(dir, fs31.constants.W_OK);
194604
194772
  return true;
194605
194773
  } catch {
194606
194774
  return false;
@@ -194611,21 +194779,21 @@ async function performUpdate(version, onProgress) {
194611
194779
  const binaryDir = path28.dirname(binaryPath);
194612
194780
  const tempPath = path28.join(binaryDir, `.specific-update-${process.pid}`);
194613
194781
  try {
194614
- const { platform: platform5, arch: arch3 } = getPlatformInfo();
194615
- const url = `${BINARIES_BASE_URL}/${version}/specific-${platform5}-${arch3}`;
194782
+ const { platform: platform8, arch: arch3 } = getPlatformInfo();
194783
+ const url = `${BINARIES_BASE_URL}/${version}/specific-${platform8}-${arch3}`;
194616
194784
  await downloadFile(url, tempPath, onProgress);
194617
- const stat4 = fs30.statSync(tempPath);
194785
+ const stat4 = fs31.statSync(tempPath);
194618
194786
  if (stat4.size === 0) {
194619
194787
  throw new Error("Downloaded binary is empty");
194620
194788
  }
194621
- fs30.chmodSync(tempPath, 493);
194789
+ fs31.chmodSync(tempPath, 493);
194622
194790
  onProgress?.({ phase: "finalizing" });
194623
- fs30.unlinkSync(binaryPath);
194624
- fs30.renameSync(tempPath, binaryPath);
194791
+ fs31.unlinkSync(binaryPath);
194792
+ fs31.renameSync(tempPath, binaryPath);
194625
194793
  } catch (error) {
194626
194794
  try {
194627
- if (fs30.existsSync(tempPath)) {
194628
- fs30.unlinkSync(tempPath);
194795
+ if (fs31.existsSync(tempPath)) {
194796
+ fs31.unlinkSync(tempPath);
194629
194797
  }
194630
194798
  } catch {
194631
194799
  }
@@ -194635,21 +194803,21 @@ async function performUpdate(version, onProgress) {
194635
194803
 
194636
194804
  // src/lib/background-update.ts
194637
194805
  import { spawn as spawn9 } from "child_process";
194638
- import * as fs31 from "fs";
194806
+ import * as fs32 from "fs";
194639
194807
  import * as path29 from "path";
194640
- import * as os9 from "os";
194641
- var SPECIFIC_DIR = path29.join(os9.homedir(), ".specific");
194808
+ import * as os12 from "os";
194809
+ var SPECIFIC_DIR = path29.join(os12.homedir(), ".specific");
194642
194810
  var RATE_LIMIT_FILE = path29.join(SPECIFIC_DIR, "last-update-check");
194643
194811
  var LOCK_FILE = path29.join(SPECIFIC_DIR, "update.lock");
194644
194812
  var RATE_LIMIT_MS = 60 * 60 * 1e3;
194645
194813
  var STALE_LOCK_MS = 10 * 60 * 1e3;
194646
194814
  function writeCheckTimestamp() {
194647
- fs31.mkdirSync(SPECIFIC_DIR, { recursive: true });
194648
- fs31.writeFileSync(RATE_LIMIT_FILE, String(Date.now()), "utf-8");
194815
+ fs32.mkdirSync(SPECIFIC_DIR, { recursive: true });
194816
+ fs32.writeFileSync(RATE_LIMIT_FILE, String(Date.now()), "utf-8");
194649
194817
  }
194650
194818
  function isRateLimited() {
194651
194819
  try {
194652
- const content = fs31.readFileSync(RATE_LIMIT_FILE, "utf-8").trim();
194820
+ const content = fs32.readFileSync(RATE_LIMIT_FILE, "utf-8").trim();
194653
194821
  const lastCheck = parseInt(content, 10);
194654
194822
  if (isNaN(lastCheck)) return false;
194655
194823
  return Date.now() - lastCheck < RATE_LIMIT_MS;
@@ -194783,7 +194951,7 @@ function updateCommand() {
194783
194951
  var program = new Command();
194784
194952
  var env = "production";
194785
194953
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
194786
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.81").enablePositionalOptions();
194954
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.82").enablePositionalOptions();
194787
194955
  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
194956
  program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
194789
194957
  program.command("check").description("Validate specific.hcl configuration").action(checkCommand);