@specific.dev/cli 0.1.70 → 0.1.72

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 (68) 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 +314 -266
  64. package/dist/postinstall.js +1 -1
  65. package/package.json +1 -1
  66. /package/dist/admin/_next/static/{HT8dN-xP5FgfDQtqZwDzf → EdA1eXzy3MBFAW53uuebn}/_buildManifest.js +0 -0
  67. /package/dist/admin/_next/static/{HT8dN-xP5FgfDQtqZwDzf → EdA1eXzy3MBFAW53uuebn}/_clientMiddlewareManifest.json +0 -0
  68. /package/dist/admin/_next/static/{HT8dN-xP5FgfDQtqZwDzf → EdA1eXzy3MBFAW53uuebn}/_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
183435
 
183436
183436
  // src/lib/dev/system-setup.ts
183437
183437
  import { execSync as execSync2 } from "child_process";
183438
+ import * as fs4 from "fs";
183439
+ import * as os4 from "os";
183440
+ import * as path3 from "path";
183438
183441
 
183439
183442
  // src/lib/dev/local-ca.ts
183440
183443
  import * as fs from "fs";
@@ -183453,17 +183456,17 @@ function caInstalledInTrustStore() {
183453
183456
  if (!caFilesExist()) {
183454
183457
  return false;
183455
183458
  }
183456
- const platform5 = os.platform();
183459
+ const platform6 = os.platform();
183457
183460
  const certPath = path.join(getCADir(), "ca.crt");
183458
183461
  const diskCert = fs.readFileSync(certPath, "utf-8").replace(/\r\n/g, "\n").trim();
183459
183462
  try {
183460
- if (platform5 === "darwin") {
183463
+ if (platform6 === "darwin") {
183461
183464
  const keychainCert = execSync(
183462
183465
  'security find-certificate -c "Specific Local Development CA" -p /Library/Keychains/System.keychain',
183463
183466
  { encoding: "utf-8" }
183464
183467
  ).replace(/\r\n/g, "\n").trim();
183465
183468
  return keychainCert === diskCert;
183466
- } else if (platform5 === "linux") {
183469
+ } else if (platform6 === "linux") {
183467
183470
  const trustPaths = [
183468
183471
  "/usr/local/share/ca-certificates/specific-local-ca.crt",
183469
183472
  "/etc/pki/ca-trust/source/anchors/specific-local-ca.crt"
@@ -183571,15 +183574,15 @@ function removeCA() {
183571
183574
  }
183572
183575
  }
183573
183576
  function getCAInstallCommands(certPath) {
183574
- const platform5 = os.platform();
183575
- if (platform5 === "darwin") {
183577
+ const platform6 = os.platform();
183578
+ if (platform6 === "darwin") {
183576
183579
  return [
183577
183580
  // Remove any existing cert with the same CN first — add-trusted-cert
183578
183581
  // silently does nothing if a cert with the same subject already exists.
183579
183582
  'security delete-certificate -c "Specific Local Development CA" /Library/Keychains/System.keychain 2>/dev/null || true',
183580
183583
  `security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "${certPath}"`
183581
183584
  ];
183582
- } else if (platform5 === "linux") {
183585
+ } else if (platform6 === "linux") {
183583
183586
  if (fs.existsSync("/usr/local/share/ca-certificates")) {
183584
183587
  return [
183585
183588
  `cp "${certPath}" /usr/local/share/ca-certificates/specific-local-ca.crt`,
@@ -183593,7 +183596,7 @@ function getCAInstallCommands(certPath) {
183593
183596
  }
183594
183597
  throw new Error("Could not detect Linux certificate trust mechanism");
183595
183598
  }
183596
- throw new Error(`Unsupported platform: ${platform5}`);
183599
+ throw new Error(`Unsupported platform: ${platform6}`);
183597
183600
  }
183598
183601
  function generateCertificate(domain, keys = []) {
183599
183602
  const caDir = getCADir();
@@ -183638,22 +183641,22 @@ import * as os2 from "os";
183638
183641
  var RESOLVER_FILE_MACOS = "/etc/resolver/local.spcf.app";
183639
183642
  var RESOLVER_FILE_LINUX = "/etc/systemd/resolved.conf.d/specific-local.conf";
183640
183643
  function resolverConfigExists() {
183641
- const platform5 = os2.platform();
183642
- if (platform5 === "darwin") {
183644
+ const platform6 = os2.platform();
183645
+ if (platform6 === "darwin") {
183643
183646
  return fs2.existsSync(RESOLVER_FILE_MACOS);
183644
- } else if (platform5 === "linux") {
183647
+ } else if (platform6 === "linux") {
183645
183648
  return fs2.existsSync(RESOLVER_FILE_LINUX);
183646
183649
  }
183647
183650
  return false;
183648
183651
  }
183649
183652
  function getResolverInstallCommands(port) {
183650
- const platform5 = os2.platform();
183651
- if (platform5 === "darwin") {
183653
+ const platform6 = os2.platform();
183654
+ if (platform6 === "darwin") {
183652
183655
  return [
183653
183656
  "mkdir -p /etc/resolver",
183654
183657
  `printf "nameserver 127.0.0.1\\nport ${port}\\n" > ${RESOLVER_FILE_MACOS}`
183655
183658
  ];
183656
- } else if (platform5 === "linux") {
183659
+ } else if (platform6 === "linux") {
183657
183660
  if (fs2.existsSync("/etc/systemd/resolved.conf.d") || fs2.existsSync("/etc/systemd")) {
183658
183661
  return [
183659
183662
  "mkdir -p /etc/systemd/resolved.conf.d",
@@ -183746,7 +183749,6 @@ async function startDnsServer(port = DNS_PORT) {
183746
183749
  }
183747
183750
 
183748
183751
  // src/lib/dev/system-setup.ts
183749
- import * as path3 from "path";
183750
183752
  function systemSetupNeeded() {
183751
183753
  return !caInstalledInTrustStore() || !resolverConfigExists();
183752
183754
  }
@@ -183773,7 +183775,7 @@ function performSystemSetup() {
183773
183775
  return;
183774
183776
  }
183775
183777
  try {
183776
- execSync2(`sudo sh -c '${commands.join(" && ")}'`, { stdio: "inherit" });
183778
+ execPrivileged(commands);
183777
183779
  } catch (err) {
183778
183780
  if (needsGenerate) {
183779
183781
  removeCA();
@@ -183781,14 +183783,43 @@ function performSystemSetup() {
183781
183783
  throw err;
183782
183784
  }
183783
183785
  }
183786
+ function execPrivileged(commands) {
183787
+ if (commands.length === 0) return;
183788
+ if (os4.platform() === "darwin") {
183789
+ const scriptPath = path3.join(os4.tmpdir(), "specific-setup.sh");
183790
+ const appPath = path3.join(os4.tmpdir(), "Specific.app");
183791
+ try {
183792
+ fs4.writeFileSync(
183793
+ scriptPath,
183794
+ "#!/bin/sh\nset -e\n" + commands.join("\n") + "\n",
183795
+ { mode: 448 }
183796
+ );
183797
+ execSync2(
183798
+ `osacompile -o '${appPath}' -e 'do shell script "sh ${scriptPath}" with administrator privileges'`
183799
+ );
183800
+ execSync2(`'${appPath}/Contents/MacOS/applet'`, { stdio: "pipe" });
183801
+ } finally {
183802
+ try {
183803
+ fs4.unlinkSync(scriptPath);
183804
+ } catch {
183805
+ }
183806
+ try {
183807
+ fs4.rmSync(appPath, { recursive: true });
183808
+ } catch {
183809
+ }
183810
+ }
183811
+ } else {
183812
+ execSync2(`sudo sh -c '${commands.join(" && ")}'`, { stdio: "inherit" });
183813
+ }
183814
+ }
183784
183815
 
183785
183816
  // src/lib/analytics/index.ts
183786
183817
  import { PostHog } from "posthog-node";
183787
- import * as os6 from "os";
183818
+ import * as os7 from "os";
183788
183819
  import * as crypto from "crypto";
183789
183820
 
183790
183821
  // src/lib/project/config.ts
183791
- import * as fs4 from "fs";
183822
+ import * as fs5 from "fs";
183792
183823
  import * as path4 from "path";
183793
183824
  var PROJECT_ID_FILE = ".specific/project_id";
183794
183825
  var ProjectNotLinkedError = class extends Error {
@@ -183805,10 +183836,10 @@ Run: specific deploy`
183805
183836
  };
183806
183837
  function readProjectId(projectDir = process.cwd()) {
183807
183838
  const projectIdPath = path4.join(projectDir, PROJECT_ID_FILE);
183808
- if (!fs4.existsSync(projectIdPath)) {
183839
+ if (!fs5.existsSync(projectIdPath)) {
183809
183840
  throw new ProjectNotLinkedError();
183810
183841
  }
183811
- const projectId = fs4.readFileSync(projectIdPath, "utf-8").trim();
183842
+ const projectId = fs5.readFileSync(projectIdPath, "utf-8").trim();
183812
183843
  if (!projectId) {
183813
183844
  throw new Error(`${PROJECT_ID_FILE} is empty`);
183814
183845
  }
@@ -183816,20 +183847,20 @@ function readProjectId(projectDir = process.cwd()) {
183816
183847
  }
183817
183848
  function hasProjectId(projectDir = process.cwd()) {
183818
183849
  const projectIdPath = path4.join(projectDir, PROJECT_ID_FILE);
183819
- return fs4.existsSync(projectIdPath);
183850
+ return fs5.existsSync(projectIdPath);
183820
183851
  }
183821
183852
  function writeProjectId(projectId, projectDir = process.cwd()) {
183822
183853
  const specificDir = path4.join(projectDir, ".specific");
183823
- if (!fs4.existsSync(specificDir)) {
183824
- fs4.mkdirSync(specificDir, { recursive: true });
183854
+ if (!fs5.existsSync(specificDir)) {
183855
+ fs5.mkdirSync(specificDir, { recursive: true });
183825
183856
  }
183826
- fs4.writeFileSync(path4.join(specificDir, "project_id"), projectId + "\n");
183857
+ fs5.writeFileSync(path4.join(specificDir, "project_id"), projectId + "\n");
183827
183858
  }
183828
183859
 
183829
183860
  // src/lib/auth/credentials.ts
183830
- import * as fs10 from "fs";
183861
+ import * as fs11 from "fs";
183831
183862
  import * as path6 from "path";
183832
- import * as os5 from "os";
183863
+ import * as os6 from "os";
183833
183864
 
183834
183865
  // src/lib/auth/errors.ts
183835
183866
  var RefreshTokenExpiredError = class extends Error {
@@ -184382,18 +184413,18 @@ var ApiClient = class {
184382
184413
 
184383
184414
  // src/lib/auth/credentials.ts
184384
184415
  function getUserCredentialsDir() {
184385
- return path6.join(os5.homedir(), ".specific");
184416
+ return path6.join(os6.homedir(), ".specific");
184386
184417
  }
184387
184418
  function getCredentialsPath() {
184388
184419
  return path6.join(getUserCredentialsDir(), "credentials.json");
184389
184420
  }
184390
184421
  function readUserCredentials() {
184391
184422
  const credentialsPath = getCredentialsPath();
184392
- if (!fs10.existsSync(credentialsPath)) {
184423
+ if (!fs11.existsSync(credentialsPath)) {
184393
184424
  return null;
184394
184425
  }
184395
184426
  try {
184396
- const content = fs10.readFileSync(credentialsPath, "utf-8");
184427
+ const content = fs11.readFileSync(credentialsPath, "utf-8");
184397
184428
  return JSON.parse(content);
184398
184429
  } catch {
184399
184430
  return null;
@@ -184401,18 +184432,18 @@ function readUserCredentials() {
184401
184432
  }
184402
184433
  function writeUserCredentials(credentials) {
184403
184434
  const dir = getUserCredentialsDir();
184404
- if (!fs10.existsSync(dir)) {
184405
- fs10.mkdirSync(dir, { recursive: true, mode: 448 });
184435
+ if (!fs11.existsSync(dir)) {
184436
+ fs11.mkdirSync(dir, { recursive: true, mode: 448 });
184406
184437
  }
184407
184438
  const credentialsPath = getCredentialsPath();
184408
- fs10.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {
184439
+ fs11.writeFileSync(credentialsPath, JSON.stringify(credentials, null, 2), {
184409
184440
  mode: 384
184410
184441
  });
184411
184442
  }
184412
184443
  function clearUserCredentials() {
184413
184444
  const credentialsPath = getCredentialsPath();
184414
- if (fs10.existsSync(credentialsPath)) {
184415
- fs10.unlinkSync(credentialsPath);
184445
+ if (fs11.existsSync(credentialsPath)) {
184446
+ fs11.unlinkSync(credentialsPath);
184416
184447
  }
184417
184448
  }
184418
184449
  function isLoggedIn() {
@@ -184491,7 +184522,7 @@ function isEnabled() {
184491
184522
  }
184492
184523
  function getAnonymousId() {
184493
184524
  if (anonymousId) return anonymousId;
184494
- const machineId = `${os6.hostname()}-${os6.userInfo().username}`;
184525
+ const machineId = `${os7.hostname()}-${os7.userInfo().username}`;
184495
184526
  anonymousId = crypto.createHash("sha256").update(machineId).digest("hex").slice(0, 16);
184496
184527
  return anonymousId;
184497
184528
  }
@@ -184538,7 +184569,7 @@ function trackEvent(event, properties) {
184538
184569
  event,
184539
184570
  properties: {
184540
184571
  ...properties,
184541
- cli_version: "0.1.70",
184572
+ cli_version: "0.1.72",
184542
184573
  platform: process.platform,
184543
184574
  node_version: process.version,
184544
184575
  project_id: getProjectId()
@@ -184575,57 +184606,57 @@ var options = [
184575
184606
  ];
184576
184607
  function isGitProject() {
184577
184608
  const gitPath = path7.join(process.cwd(), ".git");
184578
- return fs11.existsSync(gitPath);
184609
+ return fs12.existsSync(gitPath);
184579
184610
  }
184580
184611
  function detectExistingAgents() {
184581
184612
  const detected = {};
184582
184613
  const cursorDir = path7.join(process.cwd(), ".cursor");
184583
- if (fs11.existsSync(cursorDir)) {
184614
+ if (fs12.existsSync(cursorDir)) {
184584
184615
  detected["cursor"] = true;
184585
184616
  }
184586
184617
  const claudeDir = path7.join(process.cwd(), ".claude");
184587
184618
  const claudeMd = path7.join(process.cwd(), "CLAUDE.md");
184588
- if (fs11.existsSync(claudeDir) || fs11.existsSync(claudeMd)) {
184619
+ if (fs12.existsSync(claudeDir) || fs12.existsSync(claudeMd)) {
184589
184620
  detected["claude"] = true;
184590
184621
  }
184591
184622
  const agentsMd = path7.join(process.cwd(), "AGENTS.md");
184592
- if (fs11.existsSync(agentsMd)) {
184623
+ if (fs12.existsSync(agentsMd)) {
184593
184624
  detected["codex"] = true;
184594
184625
  }
184595
184626
  return detected;
184596
184627
  }
184597
184628
  function appendOrCreateFile(filePath, content) {
184598
- if (fs11.existsSync(filePath)) {
184599
- const existing = fs11.readFileSync(filePath, "utf-8");
184629
+ if (fs12.existsSync(filePath)) {
184630
+ const existing = fs12.readFileSync(filePath, "utf-8");
184600
184631
  if (existing.includes("specific docs") || existing.includes("specific check")) {
184601
184632
  return "unchanged";
184602
184633
  }
184603
184634
  const separator = existing.endsWith("\n") ? "\n" : "\n\n";
184604
- fs11.writeFileSync(filePath, existing + separator + content + "\n");
184635
+ fs12.writeFileSync(filePath, existing + separator + content + "\n");
184605
184636
  return "modified";
184606
184637
  } else {
184607
- fs11.writeFileSync(filePath, content + "\n");
184638
+ fs12.writeFileSync(filePath, content + "\n");
184608
184639
  return "created";
184609
184640
  }
184610
184641
  }
184611
184642
  function addToGitignore() {
184612
184643
  const gitignorePath = path7.join(process.cwd(), ".gitignore");
184613
184644
  const entries = [".specific", "specific.local"];
184614
- if (fs11.existsSync(gitignorePath)) {
184615
- const existing = fs11.readFileSync(gitignorePath, "utf-8");
184645
+ if (fs12.existsSync(gitignorePath)) {
184646
+ const existing = fs12.readFileSync(gitignorePath, "utf-8");
184616
184647
  const lines = existing.split("\n").map((l) => l.trim());
184617
184648
  const missingEntries = entries.filter((entry) => !lines.includes(entry));
184618
184649
  if (missingEntries.length === 0) {
184619
184650
  return "unchanged";
184620
184651
  }
184621
184652
  const separator = existing.endsWith("\n") ? "" : "\n";
184622
- fs11.writeFileSync(
184653
+ fs12.writeFileSync(
184623
184654
  gitignorePath,
184624
184655
  existing + separator + missingEntries.join("\n") + "\n"
184625
184656
  );
184626
184657
  return "modified";
184627
184658
  } else {
184628
- fs11.writeFileSync(gitignorePath, entries.join("\n") + "\n");
184659
+ fs12.writeFileSync(gitignorePath, entries.join("\n") + "\n");
184629
184660
  return "created";
184630
184661
  }
184631
184662
  }
@@ -184633,8 +184664,8 @@ function configureClaudeCodePermissions() {
184633
184664
  const claudeDir = path7.join(process.cwd(), ".claude");
184634
184665
  const settingsPath = path7.join(claudeDir, "settings.local.json");
184635
184666
  const permissions = ["Bash(specific docs:*)", "Bash(specific check:*)"];
184636
- if (fs11.existsSync(settingsPath)) {
184637
- const existing = JSON.parse(fs11.readFileSync(settingsPath, "utf-8"));
184667
+ if (fs12.existsSync(settingsPath)) {
184668
+ const existing = JSON.parse(fs12.readFileSync(settingsPath, "utf-8"));
184638
184669
  const allowList = existing?.permissions?.allow || [];
184639
184670
  const missingPermissions = permissions.filter(
184640
184671
  (p) => !allowList.includes(p)
@@ -184649,39 +184680,39 @@ function configureClaudeCodePermissions() {
184649
184680
  existing.permissions.allow = [];
184650
184681
  }
184651
184682
  existing.permissions.allow.push(...missingPermissions);
184652
- fs11.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + "\n");
184683
+ fs12.writeFileSync(settingsPath, JSON.stringify(existing, null, 2) + "\n");
184653
184684
  return "modified";
184654
184685
  }
184655
- if (!fs11.existsSync(claudeDir)) {
184656
- fs11.mkdirSync(claudeDir);
184686
+ if (!fs12.existsSync(claudeDir)) {
184687
+ fs12.mkdirSync(claudeDir);
184657
184688
  }
184658
184689
  const settings = {
184659
184690
  permissions: {
184660
184691
  allow: permissions
184661
184692
  }
184662
184693
  };
184663
- fs11.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
184694
+ fs12.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
184664
184695
  return "created";
184665
184696
  }
184666
184697
  function createCursorRule() {
184667
184698
  const cursorDir = path7.join(process.cwd(), ".cursor");
184668
184699
  const rulesDir = path7.join(cursorDir, "rules");
184669
184700
  const mdcPath = path7.join(rulesDir, "specific.mdc");
184670
- if (fs11.existsSync(mdcPath)) {
184671
- const existing = fs11.readFileSync(mdcPath, "utf-8");
184701
+ if (fs12.existsSync(mdcPath)) {
184702
+ const existing = fs12.readFileSync(mdcPath, "utf-8");
184672
184703
  if (existing.includes("specific docs") || existing.includes("specific check")) {
184673
184704
  return "unchanged";
184674
184705
  }
184675
- fs11.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
184706
+ fs12.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
184676
184707
  return "modified";
184677
184708
  }
184678
- if (!fs11.existsSync(cursorDir)) {
184679
- fs11.mkdirSync(cursorDir);
184709
+ if (!fs12.existsSync(cursorDir)) {
184710
+ fs12.mkdirSync(cursorDir);
184680
184711
  }
184681
- if (!fs11.existsSync(rulesDir)) {
184682
- fs11.mkdirSync(rulesDir);
184712
+ if (!fs12.existsSync(rulesDir)) {
184713
+ fs12.mkdirSync(rulesDir);
184683
184714
  }
184684
- fs11.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
184715
+ fs12.writeFileSync(mdcPath, CURSOR_MDC_CONTENT);
184685
184716
  return "created";
184686
184717
  }
184687
184718
  function configureAgents(checked) {
@@ -184736,7 +184767,7 @@ function InitUI() {
184736
184767
  const [phase, setPhase] = useState(
184737
184768
  () => !systemSetupNeeded() ? "agents" : "installing-ca"
184738
184769
  );
184739
- const [caInstallPhase, setCaInstallPhase] = useState(() => !systemSetupNeeded() ? "done" : "installing");
184770
+ const [caInstallPhase, setCaInstallPhase] = useState(() => !systemSetupNeeded() ? "done" : "prompt");
184740
184771
  const [focusedIndex, setFocusedIndex] = useState(initialState.focusedIndex);
184741
184772
  const [checked, setChecked] = useState(
184742
184773
  initialState.detected
@@ -184767,6 +184798,10 @@ function InitUI() {
184767
184798
  }
184768
184799
  const isSubmitFocused = focusedIndex === options.length;
184769
184800
  useInput((input, key) => {
184801
+ if (phase === "installing-ca" && caInstallPhase === "prompt" && key.return) {
184802
+ setCaInstallPhase("installing");
184803
+ return;
184804
+ }
184770
184805
  if (phase !== "agents") return;
184771
184806
  if (key.upArrow || input === "k") {
184772
184807
  setFocusedIndex((prev) => Math.max(0, prev - 1));
@@ -184796,8 +184831,11 @@ function InitUI() {
184796
184831
  }
184797
184832
  }, [phase, exit]);
184798
184833
  if (phase === "installing-ca") {
184834
+ if (caInstallPhase === "prompt") {
184835
+ 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.local.spcf.app"), /* @__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."));
184836
+ }
184799
184837
  if (caInstallPhase === "installing") {
184800
- return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Local Development Setup"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "Setting up TLS certificates and DNS resolution for local"), /* @__PURE__ */ React2.createElement(Text2, null, "development. This is a one-time setup for Specific projects."), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "Your password is required to configure your system."), /* @__PURE__ */ React2.createElement(Text2, null, " "));
184838
+ 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"));
184801
184839
  }
184802
184840
  }
184803
184841
  if (phase === "done") {
@@ -184917,7 +184955,7 @@ var BETA_REGISTRY = [
184917
184955
  ];
184918
184956
 
184919
184957
  // src/lib/beta/storage.ts
184920
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync5, existsSync as existsSync6, mkdirSync as mkdirSync6 } from "fs";
184958
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync6, existsSync as existsSync6, mkdirSync as mkdirSync6 } from "fs";
184921
184959
  import { join as join7 } from "path";
184922
184960
  var BETAS_FILE = ".specific/betas.json";
184923
184961
  function loadEnabledBetas(projectDir = process.cwd()) {
@@ -184950,7 +184988,7 @@ function saveBetas(enabled, projectDir) {
184950
184988
  mkdirSync6(specificDir, { recursive: true });
184951
184989
  }
184952
184990
  const data = { enabled };
184953
- writeFileSync5(
184991
+ writeFileSync6(
184954
184992
  join7(projectDir, BETAS_FILE),
184955
184993
  JSON.stringify(data, null, 2) + "\n"
184956
184994
  );
@@ -185017,7 +185055,7 @@ function resolveFilesystemDoc(path30) {
185017
185055
  import React3, { useState as useState2, useEffect as useEffect2 } from "react";
185018
185056
  import { render as render3, Text as Text3, Box as Box3 } from "ink";
185019
185057
  import Spinner3 from "ink-spinner";
185020
- import * as fs13 from "fs";
185058
+ import * as fs14 from "fs";
185021
185059
  import * as path9 from "path";
185022
185060
  import { execFile as execFile7 } from "child_process";
185023
185061
 
@@ -185889,32 +185927,32 @@ var ExtractionError = class extends Error {
185889
185927
  };
185890
185928
 
185891
185929
  // src/lib/bin/manager.ts
185892
- import * as fs12 from "fs";
185930
+ import * as fs13 from "fs";
185893
185931
  import * as path8 from "path";
185894
- import * as os7 from "os";
185932
+ import * as os8 from "os";
185895
185933
  import { createReadStream } from "fs";
185896
185934
  import { createTarExtractor, extractTo } from "tar-vern";
185897
185935
  function getLibraryEnv(binary) {
185898
185936
  if (!binary.libraryPath) {
185899
185937
  return {};
185900
185938
  }
185901
- const platform5 = os7.platform();
185902
- if (platform5 === "darwin") {
185939
+ const platform6 = os8.platform();
185940
+ if (platform6 === "darwin") {
185903
185941
  return { DYLD_LIBRARY_PATH: binary.libraryPath };
185904
- } else if (platform5 === "linux") {
185942
+ } else if (platform6 === "linux") {
185905
185943
  return { LD_LIBRARY_PATH: binary.libraryPath };
185906
185944
  }
185907
185945
  return {};
185908
185946
  }
185909
185947
  function getBinBaseDir() {
185910
- return path8.join(os7.homedir(), ".specific", "bin");
185948
+ return path8.join(os8.homedir(), ".specific", "bin");
185911
185949
  }
185912
185950
  function getPlatformInfo() {
185913
- const platform5 = os7.platform();
185914
- const arch3 = os7.arch();
185915
- if (platform5 !== "darwin" && platform5 !== "linux") {
185951
+ const platform6 = os8.platform();
185952
+ const arch3 = os8.arch();
185953
+ if (platform6 !== "darwin" && platform6 !== "linux") {
185916
185954
  throw new Error(
185917
- `Unsupported platform: ${platform5}. Only macOS and Linux are supported.`
185955
+ `Unsupported platform: ${platform6}. Only macOS and Linux are supported.`
185918
185956
  );
185919
185957
  }
185920
185958
  const archStr = arch3;
@@ -185928,7 +185966,7 @@ function getPlatformInfo() {
185928
185966
  `Unsupported architecture: ${arch3}. Only x64 and arm64 are supported.`
185929
185967
  );
185930
185968
  }
185931
- return { platform: platform5, arch: mappedArch };
185969
+ return { platform: platform6, arch: mappedArch };
185932
185970
  }
185933
185971
  function getBinaryDir(definition, version, platformInfo) {
185934
185972
  return path8.join(
@@ -185942,7 +185980,7 @@ function isBinaryInstalled(definition, version, platformInfo) {
185942
185980
  const binDir = getBinaryDir(definition, version, platformInfo);
185943
185981
  for (const execPath of definition.executables) {
185944
185982
  const fullPath = path8.join(binDir, execPath);
185945
- if (!fs12.existsSync(fullPath)) {
185983
+ if (!fs13.existsSync(fullPath)) {
185946
185984
  return false;
185947
185985
  }
185948
185986
  }
@@ -185971,11 +186009,11 @@ async function downloadFile(url, destPath, onProgress) {
185971
186009
  );
185972
186010
  let bytesDownloaded = 0;
185973
186011
  const parentDir = path8.dirname(destPath);
185974
- if (!fs12.existsSync(parentDir)) {
185975
- fs12.mkdirSync(parentDir, { recursive: true });
186012
+ if (!fs13.existsSync(parentDir)) {
186013
+ fs13.mkdirSync(parentDir, { recursive: true });
185976
186014
  }
185977
186015
  const partPath = destPath + ".part";
185978
- const fileStream = fs12.createWriteStream(partPath);
186016
+ const fileStream = fs13.createWriteStream(partPath);
185979
186017
  try {
185980
186018
  const reader = response.body.getReader();
185981
186019
  while (true) {
@@ -185998,12 +186036,12 @@ async function downloadFile(url, destPath, onProgress) {
185998
186036
  else resolve10();
185999
186037
  });
186000
186038
  });
186001
- fs12.renameSync(partPath, destPath);
186039
+ fs13.renameSync(partPath, destPath);
186002
186040
  } catch (error) {
186003
186041
  try {
186004
186042
  fileStream.close();
186005
- if (fs12.existsSync(partPath)) {
186006
- fs12.unlinkSync(partPath);
186043
+ if (fs13.existsSync(partPath)) {
186044
+ fs13.unlinkSync(partPath);
186007
186045
  }
186008
186046
  } catch {
186009
186047
  }
@@ -186012,8 +186050,8 @@ async function downloadFile(url, destPath, onProgress) {
186012
186050
  }
186013
186051
  async function extractTarball(archivePath, destDir, definition, onProgress) {
186014
186052
  onProgress?.({ phase: "extracting" });
186015
- if (!fs12.existsSync(destDir)) {
186016
- fs12.mkdirSync(destDir, { recursive: true });
186053
+ if (!fs13.existsSync(destDir)) {
186054
+ fs13.mkdirSync(destDir, { recursive: true });
186017
186055
  }
186018
186056
  try {
186019
186057
  const fileStream = createReadStream(archivePath);
@@ -186022,8 +186060,8 @@ async function extractTarball(archivePath, destDir, definition, onProgress) {
186022
186060
  onProgress?.({ phase: "finalizing" });
186023
186061
  for (const execPath of definition.executables) {
186024
186062
  const fullPath = path8.join(destDir, execPath);
186025
- if (fs12.existsSync(fullPath)) {
186026
- fs12.chmodSync(fullPath, 493);
186063
+ if (fs13.existsSync(fullPath)) {
186064
+ fs13.chmodSync(fullPath, 493);
186027
186065
  }
186028
186066
  }
186029
186067
  } catch (error) {
@@ -186047,12 +186085,12 @@ async function ensureBinary(definition, version, onProgress) {
186047
186085
  `Binary type definitions must have exactly one executable, got ${definition.executables.length}`
186048
186086
  );
186049
186087
  }
186050
- if (!fs12.existsSync(binDir)) {
186051
- fs12.mkdirSync(binDir, { recursive: true });
186088
+ if (!fs13.existsSync(binDir)) {
186089
+ fs13.mkdirSync(binDir, { recursive: true });
186052
186090
  }
186053
186091
  const execPath = path8.join(binDir, definition.executables[0]);
186054
186092
  await downloadFile(url, execPath, onProgress);
186055
- fs12.chmodSync(execPath, 493);
186093
+ fs13.chmodSync(execPath, 493);
186056
186094
  onProgress?.({ phase: "finalizing" });
186057
186095
  } else {
186058
186096
  const downloadDir = path8.join(getBinBaseDir(), "downloads");
@@ -186063,8 +186101,8 @@ async function ensureBinary(definition, version, onProgress) {
186063
186101
  await extractTarball(archivePath, binDir, definition, onProgress);
186064
186102
  } finally {
186065
186103
  try {
186066
- if (fs12.existsSync(archivePath)) {
186067
- fs12.unlinkSync(archivePath);
186104
+ if (fs13.existsSync(archivePath)) {
186105
+ fs13.unlinkSync(archivePath);
186068
186106
  }
186069
186107
  } catch {
186070
186108
  }
@@ -186240,20 +186278,20 @@ function CheckUI() {
186240
186278
  useEffect2(() => {
186241
186279
  async function load() {
186242
186280
  const configPath = path9.join(process.cwd(), "specific.hcl");
186243
- if (!fs13.existsSync(configPath)) {
186281
+ if (!fs14.existsSync(configPath)) {
186244
186282
  setState({
186245
186283
  status: "error",
186246
186284
  error: "No specific.hcl found in current directory"
186247
186285
  });
186248
186286
  return;
186249
186287
  }
186250
- const hcl = fs13.readFileSync(configPath, "utf-8");
186288
+ const hcl = fs14.readFileSync(configPath, "utf-8");
186251
186289
  try {
186252
186290
  const config2 = await parseConfig(hcl);
186253
186291
  for (const build of config2.builds) {
186254
186292
  if (build.dockerfile) {
186255
186293
  const dockerfilePath = path9.resolve(process.cwd(), build.dockerfile);
186256
- if (!fs13.existsSync(dockerfilePath)) {
186294
+ if (!fs14.existsSync(dockerfilePath)) {
186257
186295
  setState({
186258
186296
  status: "error",
186259
186297
  error: `Build "${build.name}": Dockerfile not found at "${build.dockerfile}" (resolved to ${dockerfilePath})`
@@ -186269,7 +186307,7 @@ function CheckUI() {
186269
186307
  process.cwd(),
186270
186308
  pg.reshape.migrations_dir ?? "migrations"
186271
186309
  );
186272
- if (!fs13.existsSync(migrationsDir)) {
186310
+ if (!fs14.existsSync(migrationsDir)) {
186273
186311
  reshapeChecks2.push({
186274
186312
  databaseName: pg.name,
186275
186313
  migrationsDir: pg.reshape.migrations_dir ?? "migrations",
@@ -186320,9 +186358,9 @@ function checkCommand() {
186320
186358
 
186321
186359
  // src/commands/dev.tsx
186322
186360
  import React6, { useState as useState5, useEffect as useEffect3, useRef } from "react";
186323
- import { render as render4, Text as Text6, Box as Box6, useApp as useApp2, Static } from "ink";
186361
+ import { render as render4, Text as Text6, Box as Box6, useApp as useApp2, Static, useInput as useInput4 } from "ink";
186324
186362
  import Spinner4 from "ink-spinner";
186325
- import * as fs23 from "fs";
186363
+ import * as fs24 from "fs";
186326
186364
  import * as path20 from "path";
186327
186365
 
186328
186366
  // node_modules/.pnpm/chokidar@5.0.0/node_modules/chokidar/index.js
@@ -188077,7 +188115,7 @@ var PortAllocator = class {
188077
188115
  };
188078
188116
 
188079
188117
  // src/lib/dev/stable-port-allocator.ts
188080
- import * as fs14 from "fs";
188118
+ import * as fs15 from "fs";
188081
188119
  import * as path10 from "path";
188082
188120
  var PORT_RANGE_START2 = 4e4;
188083
188121
  var PORT_RANGE_END2 = 49999;
@@ -188092,11 +188130,11 @@ var StablePortAllocator = class {
188092
188130
  this.loadPorts();
188093
188131
  }
188094
188132
  loadPorts() {
188095
- if (!fs14.existsSync(this.portsFilePath)) {
188133
+ if (!fs15.existsSync(this.portsFilePath)) {
188096
188134
  return;
188097
188135
  }
188098
188136
  try {
188099
- const content = fs14.readFileSync(this.portsFilePath, "utf-8");
188137
+ const content = fs15.readFileSync(this.portsFilePath, "utf-8");
188100
188138
  const data = JSON.parse(content);
188101
188139
  if (data.version === 1 && data.ports) {
188102
188140
  this.savedPorts = data.ports;
@@ -188109,14 +188147,14 @@ var StablePortAllocator = class {
188109
188147
  }
188110
188148
  }
188111
188149
  savePorts() {
188112
- if (!fs14.existsSync(this.portsDir)) {
188113
- fs14.mkdirSync(this.portsDir, { recursive: true });
188150
+ if (!fs15.existsSync(this.portsDir)) {
188151
+ fs15.mkdirSync(this.portsDir, { recursive: true });
188114
188152
  }
188115
188153
  const data = {
188116
188154
  version: 1,
188117
188155
  ports: this.savedPorts
188118
188156
  };
188119
- fs14.writeFileSync(this.portsFilePath, JSON.stringify(data, null, 2));
188157
+ fs15.writeFileSync(this.portsFilePath, JSON.stringify(data, null, 2));
188120
188158
  }
188121
188159
  allocateRandom() {
188122
188160
  const rangeSize = PORT_RANGE_END2 - PORT_RANGE_START2 + 1;
@@ -188143,7 +188181,7 @@ var StablePortAllocator = class {
188143
188181
  };
188144
188182
 
188145
188183
  // src/lib/dev/database-manager.ts
188146
- import * as fs15 from "fs";
188184
+ import * as fs16 from "fs";
188147
188185
  import * as path11 from "path";
188148
188186
  import * as net from "net";
188149
188187
  import { spawn } from "child_process";
@@ -188155,9 +188193,9 @@ async function startPostgres(pg, port, dataDir, onProgress) {
188155
188193
  const password = "postgres";
188156
188194
  const libraryEnv = getLibraryEnv(binary);
188157
188195
  const env2 = { ...process.env, ...libraryEnv };
188158
- const dataExists = fs15.existsSync(dbDataPath);
188196
+ const dataExists = fs16.existsSync(dbDataPath);
188159
188197
  if (!dataExists) {
188160
- fs15.mkdirSync(dbDataPath, { recursive: true });
188198
+ fs16.mkdirSync(dbDataPath, { recursive: true });
188161
188199
  await runCommand(
188162
188200
  binary.executables["initdb"],
188163
188201
  ["-D", dbDataPath, "-U", user, "--auth=trust", "--no-locale", "-E", "UTF8"],
@@ -188234,8 +188272,8 @@ async function startRedis(redis, port, onProgress) {
188234
188272
  async function startStorage(storage, port, dataDir) {
188235
188273
  const S3rver = (await import("s3rver")).default;
188236
188274
  const storageDataPath = path11.join(process.cwd(), dataDir, storage.name);
188237
- if (!fs15.existsSync(storageDataPath)) {
188238
- fs15.mkdirSync(storageDataPath, { recursive: true });
188275
+ if (!fs16.existsSync(storageDataPath)) {
188276
+ fs16.mkdirSync(storageDataPath, { recursive: true });
188239
188277
  }
188240
188278
  const host = "127.0.0.1";
188241
188279
  const accessKey = "S3RVER";
@@ -188878,7 +188916,7 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
188878
188916
  }
188879
188917
 
188880
188918
  // src/lib/dev/instance-state.ts
188881
- import * as fs16 from "fs";
188919
+ import * as fs17 from "fs";
188882
188920
  import * as path12 from "path";
188883
188921
  var InstanceStateManager = class {
188884
188922
  stateDir;
@@ -188896,8 +188934,8 @@ var InstanceStateManager = class {
188896
188934
  return this.key;
188897
188935
  }
188898
188936
  ensureStateDir() {
188899
- if (!fs16.existsSync(this.stateDir)) {
188900
- fs16.mkdirSync(this.stateDir, { recursive: true });
188937
+ if (!fs17.existsSync(this.stateDir)) {
188938
+ fs17.mkdirSync(this.stateDir, { recursive: true });
188901
188939
  }
188902
188940
  }
188903
188941
  isProcessRunning(pid) {
@@ -188914,15 +188952,15 @@ var InstanceStateManager = class {
188914
188952
  const startTime = Date.now();
188915
188953
  while (Date.now() - startTime < timeoutMs) {
188916
188954
  try {
188917
- const fd = fs16.openSync(
188955
+ const fd = fs17.openSync(
188918
188956
  this.lockPath,
188919
- fs16.constants.O_CREAT | fs16.constants.O_EXCL | fs16.constants.O_WRONLY
188957
+ fs17.constants.O_CREAT | fs17.constants.O_EXCL | fs17.constants.O_WRONLY
188920
188958
  );
188921
- fs16.writeSync(fd, String(process.pid));
188922
- fs16.closeSync(fd);
188959
+ fs17.writeSync(fd, String(process.pid));
188960
+ fs17.closeSync(fd);
188923
188961
  return () => {
188924
188962
  try {
188925
- fs16.unlinkSync(this.lockPath);
188963
+ fs17.unlinkSync(this.lockPath);
188926
188964
  } catch {
188927
188965
  }
188928
188966
  };
@@ -188931,16 +188969,16 @@ var InstanceStateManager = class {
188931
188969
  if (err.code === "EEXIST") {
188932
188970
  try {
188933
188971
  const lockPid = parseInt(
188934
- fs16.readFileSync(this.lockPath, "utf-8").trim(),
188972
+ fs17.readFileSync(this.lockPath, "utf-8").trim(),
188935
188973
  10
188936
188974
  );
188937
188975
  if (!this.isProcessRunning(lockPid)) {
188938
- fs16.unlinkSync(this.lockPath);
188976
+ fs17.unlinkSync(this.lockPath);
188939
188977
  continue;
188940
188978
  }
188941
188979
  } catch {
188942
188980
  try {
188943
- fs16.unlinkSync(this.lockPath);
188981
+ fs17.unlinkSync(this.lockPath);
188944
188982
  } catch {
188945
188983
  }
188946
188984
  continue;
@@ -188954,12 +188992,12 @@ var InstanceStateManager = class {
188954
188992
  throw new Error("Failed to acquire state lock (timeout)");
188955
188993
  }
188956
188994
  async getExistingInstances() {
188957
- if (!fs16.existsSync(this.statePath)) {
188995
+ if (!fs17.existsSync(this.statePath)) {
188958
188996
  return null;
188959
188997
  }
188960
188998
  const releaseLock = await this.acquireLock();
188961
188999
  try {
188962
- const content = fs16.readFileSync(this.statePath, "utf-8");
189000
+ const content = fs17.readFileSync(this.statePath, "utf-8");
188963
189001
  const state = JSON.parse(content);
188964
189002
  if (!this.isProcessRunning(state.owner.pid)) {
188965
189003
  return null;
@@ -188972,21 +189010,21 @@ var InstanceStateManager = class {
188972
189010
  }
188973
189011
  }
188974
189012
  async cleanStaleState() {
188975
- if (!fs16.existsSync(this.statePath)) {
189013
+ if (!fs17.existsSync(this.statePath)) {
188976
189014
  return false;
188977
189015
  }
188978
189016
  const releaseLock = await this.acquireLock();
188979
189017
  try {
188980
- const content = fs16.readFileSync(this.statePath, "utf-8");
189018
+ const content = fs17.readFileSync(this.statePath, "utf-8");
188981
189019
  const state = JSON.parse(content);
188982
189020
  if (!this.isProcessRunning(state.owner.pid)) {
188983
- fs16.unlinkSync(this.statePath);
189021
+ fs17.unlinkSync(this.statePath);
188984
189022
  return true;
188985
189023
  }
188986
189024
  return false;
188987
189025
  } catch {
188988
189026
  try {
188989
- fs16.unlinkSync(this.statePath);
189027
+ fs17.unlinkSync(this.statePath);
188990
189028
  return true;
188991
189029
  } catch {
188992
189030
  }
@@ -188998,8 +189036,8 @@ var InstanceStateManager = class {
188998
189036
  async claimOwnership(command) {
188999
189037
  const releaseLock = await this.acquireLock();
189000
189038
  try {
189001
- if (fs16.existsSync(this.statePath)) {
189002
- const content = fs16.readFileSync(this.statePath, "utf-8");
189039
+ if (fs17.existsSync(this.statePath)) {
189040
+ const content = fs17.readFileSync(this.statePath, "utf-8");
189003
189041
  const state2 = JSON.parse(content);
189004
189042
  if (this.isProcessRunning(state2.owner.pid)) {
189005
189043
  throw new Error(`Instances already owned by PID ${state2.owner.pid}`);
@@ -189068,8 +189106,8 @@ var InstanceStateManager = class {
189068
189106
  }
189069
189107
  const releaseLock = await this.acquireLock();
189070
189108
  try {
189071
- if (fs16.existsSync(this.statePath)) {
189072
- fs16.unlinkSync(this.statePath);
189109
+ if (fs17.existsSync(this.statePath)) {
189110
+ fs17.unlinkSync(this.statePath);
189073
189111
  }
189074
189112
  this.ownsInstances = false;
189075
189113
  } finally {
@@ -189077,21 +189115,21 @@ var InstanceStateManager = class {
189077
189115
  }
189078
189116
  }
189079
189117
  readState() {
189080
- const content = fs16.readFileSync(this.statePath, "utf-8");
189118
+ const content = fs17.readFileSync(this.statePath, "utf-8");
189081
189119
  return JSON.parse(content);
189082
189120
  }
189083
189121
  writeStateAtomic(state) {
189084
189122
  this.ensureStateDir();
189085
189123
  const tmpPath = this.statePath + ".tmp";
189086
- fs16.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
189087
- fs16.renameSync(tmpPath, this.statePath);
189124
+ fs17.writeFileSync(tmpPath, JSON.stringify(state, null, 2));
189125
+ fs17.renameSync(tmpPath, this.statePath);
189088
189126
  }
189089
189127
  };
189090
189128
 
189091
189129
  // src/lib/dev/http-proxy.ts
189092
189130
  import * as http from "http";
189093
189131
  import * as https from "https";
189094
- import * as fs17 from "fs";
189132
+ import * as fs18 from "fs";
189095
189133
  import * as path13 from "path";
189096
189134
  import { fileURLToPath as fileURLToPath3 } from "url";
189097
189135
  import httpProxy from "http-proxy";
@@ -189499,10 +189537,10 @@ function serveFilesystemFile(res, pathname) {
189499
189537
  res.end("<h1>Forbidden</h1>");
189500
189538
  return;
189501
189539
  }
189502
- if (fs17.existsSync(resolvedPath)) {
189503
- if (fs17.statSync(resolvedPath).isDirectory()) {
189540
+ if (fs18.existsSync(resolvedPath)) {
189541
+ if (fs18.statSync(resolvedPath).isDirectory()) {
189504
189542
  const indexPath2 = path13.join(resolvedPath, "index.html");
189505
- if (fs17.existsSync(indexPath2)) {
189543
+ if (fs18.existsSync(indexPath2)) {
189506
189544
  return serveFile(res, indexPath2);
189507
189545
  }
189508
189546
  } else {
@@ -189510,15 +189548,15 @@ function serveFilesystemFile(res, pathname) {
189510
189548
  }
189511
189549
  }
189512
189550
  const htmlPath = resolvedPath + ".html";
189513
- if (fs17.existsSync(htmlPath)) {
189551
+ if (fs18.existsSync(htmlPath)) {
189514
189552
  return serveFile(res, htmlPath);
189515
189553
  }
189516
189554
  const indexPath = path13.join(resolvedPath, "index.html");
189517
- if (fs17.existsSync(indexPath)) {
189555
+ if (fs18.existsSync(indexPath)) {
189518
189556
  return serveFile(res, indexPath);
189519
189557
  }
189520
189558
  const notFoundPath = path13.join(adminDir, "404.html");
189521
- if (fs17.existsSync(notFoundPath)) {
189559
+ if (fs18.existsSync(notFoundPath)) {
189522
189560
  return serveFileContent(res, notFoundPath, "text/html", 404);
189523
189561
  }
189524
189562
  res.writeHead(404, { "Content-Type": "text/html" });
@@ -189531,7 +189569,7 @@ function serveFile(res, filePath) {
189531
189569
  }
189532
189570
  function serveFileContent(res, filePath, contentType, statusCode = 200) {
189533
189571
  try {
189534
- const content = fs17.readFileSync(filePath);
189572
+ const content = fs18.readFileSync(filePath);
189535
189573
  res.writeHead(statusCode, { "Content-Type": contentType });
189536
189574
  res.end(content);
189537
189575
  } catch (err) {
@@ -189906,7 +189944,7 @@ async function startMailServer(mail, smtpPort, apiPort) {
189906
189944
 
189907
189945
  // src/lib/dev/drizzle-gateway-manager.ts
189908
189946
  import * as net3 from "net";
189909
- import * as fs18 from "fs";
189947
+ import * as fs19 from "fs";
189910
189948
  import * as path15 from "path";
189911
189949
  import { spawn as spawn4 } from "child_process";
189912
189950
  import { randomUUID as randomUUID2 } from "crypto";
@@ -189940,12 +189978,12 @@ async function startDrizzleGateway(postgresInstances, port, configDir, options2)
189940
189978
  );
189941
189979
  const host = "127.0.0.1";
189942
189980
  const drizzleConfigDir = path15.join(configDir, "drizzle-gateway");
189943
- if (!fs18.existsSync(drizzleConfigDir)) {
189944
- fs18.mkdirSync(drizzleConfigDir, { recursive: true });
189981
+ if (!fs19.existsSync(drizzleConfigDir)) {
189982
+ fs19.mkdirSync(drizzleConfigDir, { recursive: true });
189945
189983
  }
189946
189984
  const storeJson = generateStoreJson(postgresInstances);
189947
189985
  const storeJsonPath = path15.join(drizzleConfigDir, "store.json");
189948
- fs18.writeFileSync(storeJsonPath, JSON.stringify(storeJson, null, 2));
189986
+ fs19.writeFileSync(storeJsonPath, JSON.stringify(storeJson, null, 2));
189949
189987
  writeLog("drizzle-gateway", `Starting Drizzle Gateway`);
189950
189988
  writeLog("drizzle-gateway", `STORE_PATH: ${drizzleConfigDir}`);
189951
189989
  writeLog("drizzle-gateway", `PORT: ${port}`);
@@ -190047,16 +190085,16 @@ function detectSyncDatabases(config) {
190047
190085
  }
190048
190086
 
190049
190087
  // src/lib/dev/reshape-watcher.ts
190050
- import * as fs19 from "fs";
190088
+ import * as fs20 from "fs";
190051
190089
  import * as path16 from "path";
190052
190090
  import { spawnSync } from "child_process";
190053
190091
  function getMigrationFiles(dir, log) {
190054
190092
  log(`Scanning migrations directory: ${dir}`);
190055
- if (!fs19.existsSync(dir)) {
190093
+ if (!fs20.existsSync(dir)) {
190056
190094
  log(`Migrations directory does not exist: ${dir}`);
190057
190095
  return [];
190058
190096
  }
190059
- const files = fs19.readdirSync(dir);
190097
+ const files = fs20.readdirSync(dir);
190060
190098
  log(`Found ${files.length} files in directory`);
190061
190099
  const tomlFiles = files.filter((f) => f.endsWith(".toml")).sort((a, b) => a.localeCompare(b));
190062
190100
  log(`Found ${tomlFiles.length} .toml migration files: ${tomlFiles.join(", ") || "(none)"}`);
@@ -190106,7 +190144,7 @@ function runReshape(args, databaseUrl, migrationsDir, reshapeBinaryPath, log) {
190106
190144
  }
190107
190145
  function makeReadOnly(filePath, log) {
190108
190146
  try {
190109
- fs19.chmodSync(filePath, 292);
190147
+ fs20.chmodSync(filePath, 292);
190110
190148
  log(`Set file permissions to read-only (444): ${filePath}`);
190111
190149
  } catch (err) {
190112
190150
  const message = err instanceof Error ? err.message : String(err);
@@ -190192,9 +190230,9 @@ function createReshapeWatcher(options2) {
190192
190230
  };
190193
190231
  const startWatching = () => {
190194
190232
  log(`Starting file watcher for migrations directory...`);
190195
- if (!fs19.existsSync(migrationsDir)) {
190233
+ if (!fs20.existsSync(migrationsDir)) {
190196
190234
  log(`Migrations directory does not exist, creating: ${migrationsDir}`);
190197
- fs19.mkdirSync(migrationsDir, { recursive: true });
190235
+ fs20.mkdirSync(migrationsDir, { recursive: true });
190198
190236
  }
190199
190237
  log(`Watching directory: ${migrationsDir}`);
190200
190238
  watcher = chokidar_default.watch(migrationsDir, {
@@ -190329,7 +190367,7 @@ function createReshapeWatcher(options2) {
190329
190367
  }
190330
190368
 
190331
190369
  // src/lib/dev/temporal-manager.ts
190332
- import * as fs20 from "fs";
190370
+ import * as fs21 from "fs";
190333
190371
  import * as path17 from "path";
190334
190372
  import * as net4 from "net";
190335
190373
  import { spawn as spawn5 } from "child_process";
@@ -190337,8 +190375,8 @@ async function startTemporalDevServer(temporals, grpcPort, uiPort, dataDir, onPr
190337
190375
  const binary = await ensureBinary(temporalBinary, void 0, onProgress);
190338
190376
  const dbPath = path17.join(process.cwd(), dataDir, "temporal.db");
190339
190377
  const dbDir = path17.dirname(dbPath);
190340
- if (!fs20.existsSync(dbDir)) {
190341
- fs20.mkdirSync(dbDir, { recursive: true });
190378
+ if (!fs21.existsSync(dbDir)) {
190379
+ fs21.mkdirSync(dbDir, { recursive: true });
190342
190380
  }
190343
190381
  const host = "127.0.0.1";
190344
190382
  const namespaceArgs = temporals.flatMap((t) => ["--namespace", t.name]);
@@ -190667,7 +190705,7 @@ function watchConfigFile(configPath, debounceMs, onChange) {
190667
190705
  }
190668
190706
 
190669
190707
  // src/lib/dev/subdomain-generator.ts
190670
- import * as fs21 from "fs";
190708
+ import * as fs22 from "fs";
190671
190709
  import * as path18 from "path";
190672
190710
  import { generateSlug } from "random-word-slugs";
190673
190711
  var StableSubdomainAllocator = class {
@@ -190680,11 +190718,11 @@ var StableSubdomainAllocator = class {
190680
190718
  this.loadTunnels();
190681
190719
  }
190682
190720
  loadTunnels() {
190683
- if (!fs21.existsSync(this.tunnelsFilePath)) {
190721
+ if (!fs22.existsSync(this.tunnelsFilePath)) {
190684
190722
  return;
190685
190723
  }
190686
190724
  try {
190687
- const content = fs21.readFileSync(this.tunnelsFilePath, "utf-8");
190725
+ const content = fs22.readFileSync(this.tunnelsFilePath, "utf-8");
190688
190726
  const data = JSON.parse(content);
190689
190727
  if (data.version === 1 && data.baseSlug) {
190690
190728
  this.baseSlug = data.baseSlug;
@@ -190694,14 +190732,14 @@ var StableSubdomainAllocator = class {
190694
190732
  }
190695
190733
  }
190696
190734
  saveTunnels() {
190697
- if (!fs21.existsSync(this.tunnelsDir)) {
190698
- fs21.mkdirSync(this.tunnelsDir, { recursive: true });
190735
+ if (!fs22.existsSync(this.tunnelsDir)) {
190736
+ fs22.mkdirSync(this.tunnelsDir, { recursive: true });
190699
190737
  }
190700
190738
  const data = {
190701
190739
  version: 1,
190702
190740
  baseSlug: this.baseSlug
190703
190741
  };
190704
- fs21.writeFileSync(this.tunnelsFilePath, JSON.stringify(data, null, 2));
190742
+ fs22.writeFileSync(this.tunnelsFilePath, JSON.stringify(data, null, 2));
190705
190743
  }
190706
190744
  generateBaseSlug() {
190707
190745
  return generateSlug(2, {
@@ -190857,9 +190895,9 @@ async function startTunnel(serviceName, endpointName, port, subdomain, callbacks
190857
190895
  }
190858
190896
 
190859
190897
  // src/lib/dev/proxy-registry.ts
190860
- import * as fs22 from "fs";
190898
+ import * as fs23 from "fs";
190861
190899
  import * as path19 from "path";
190862
- import * as os8 from "os";
190900
+ import * as os9 from "os";
190863
190901
  import * as net6 from "net";
190864
190902
  var ProxyRegistryManager = class {
190865
190903
  proxyDir;
@@ -190869,14 +190907,14 @@ var ProxyRegistryManager = class {
190869
190907
  isOwner = false;
190870
190908
  registryWatcher = null;
190871
190909
  constructor() {
190872
- this.proxyDir = path19.join(os8.homedir(), ".specific", "proxy");
190910
+ this.proxyDir = path19.join(os9.homedir(), ".specific", "proxy");
190873
190911
  this.ownerPath = path19.join(this.proxyDir, "owner.json");
190874
190912
  this.registryPath = path19.join(this.proxyDir, "registry.json");
190875
190913
  this.lockPath = path19.join(this.proxyDir, "registry.lock");
190876
190914
  }
190877
190915
  ensureProxyDir() {
190878
- if (!fs22.existsSync(this.proxyDir)) {
190879
- fs22.mkdirSync(this.proxyDir, { recursive: true });
190916
+ if (!fs23.existsSync(this.proxyDir)) {
190917
+ fs23.mkdirSync(this.proxyDir, { recursive: true });
190880
190918
  }
190881
190919
  }
190882
190920
  isProcessRunning(pid) {
@@ -190933,15 +190971,15 @@ var ProxyRegistryManager = class {
190933
190971
  const startTime = Date.now();
190934
190972
  while (Date.now() - startTime < timeoutMs) {
190935
190973
  try {
190936
- const fd = fs22.openSync(
190974
+ const fd = fs23.openSync(
190937
190975
  this.lockPath,
190938
- fs22.constants.O_CREAT | fs22.constants.O_EXCL | fs22.constants.O_WRONLY
190976
+ fs23.constants.O_CREAT | fs23.constants.O_EXCL | fs23.constants.O_WRONLY
190939
190977
  );
190940
- fs22.writeSync(fd, String(process.pid));
190941
- fs22.closeSync(fd);
190978
+ fs23.writeSync(fd, String(process.pid));
190979
+ fs23.closeSync(fd);
190942
190980
  return () => {
190943
190981
  try {
190944
- fs22.unlinkSync(this.lockPath);
190982
+ fs23.unlinkSync(this.lockPath);
190945
190983
  } catch {
190946
190984
  }
190947
190985
  };
@@ -190950,16 +190988,16 @@ var ProxyRegistryManager = class {
190950
190988
  if (err.code === "EEXIST") {
190951
190989
  try {
190952
190990
  const lockPid = parseInt(
190953
- fs22.readFileSync(this.lockPath, "utf-8").trim(),
190991
+ fs23.readFileSync(this.lockPath, "utf-8").trim(),
190954
190992
  10
190955
190993
  );
190956
190994
  if (!this.isProcessRunning(lockPid)) {
190957
- fs22.unlinkSync(this.lockPath);
190995
+ fs23.unlinkSync(this.lockPath);
190958
190996
  continue;
190959
190997
  }
190960
190998
  } catch {
190961
190999
  try {
190962
- fs22.unlinkSync(this.lockPath);
191000
+ fs23.unlinkSync(this.lockPath);
190963
191001
  } catch {
190964
191002
  }
190965
191003
  continue;
@@ -190979,8 +191017,8 @@ var ProxyRegistryManager = class {
190979
191017
  async claimProxyOwnership(key) {
190980
191018
  const releaseLock = await this.acquireLock();
190981
191019
  try {
190982
- if (fs22.existsSync(this.ownerPath)) {
190983
- const content = fs22.readFileSync(this.ownerPath, "utf-8");
191020
+ if (fs23.existsSync(this.ownerPath)) {
191021
+ const content = fs23.readFileSync(this.ownerPath, "utf-8");
190984
191022
  const ownerFile2 = JSON.parse(content);
190985
191023
  if (await this.isProxyOwnerHealthy(ownerFile2.owner.pid)) {
190986
191024
  return false;
@@ -191010,11 +191048,11 @@ var ProxyRegistryManager = class {
191010
191048
  }
191011
191049
  const releaseLock = await this.acquireLock();
191012
191050
  try {
191013
- if (fs22.existsSync(this.ownerPath)) {
191014
- const content = fs22.readFileSync(this.ownerPath, "utf-8");
191051
+ if (fs23.existsSync(this.ownerPath)) {
191052
+ const content = fs23.readFileSync(this.ownerPath, "utf-8");
191015
191053
  const ownerFile = JSON.parse(content);
191016
191054
  if (ownerFile.owner.pid === process.pid) {
191017
- fs22.unlinkSync(this.ownerPath);
191055
+ fs23.unlinkSync(this.ownerPath);
191018
191056
  }
191019
191057
  }
191020
191058
  this.isOwner = false;
@@ -191026,12 +191064,12 @@ var ProxyRegistryManager = class {
191026
191064
  * Get the current proxy owner.
191027
191065
  */
191028
191066
  async getProxyOwner() {
191029
- if (!fs22.existsSync(this.ownerPath)) {
191067
+ if (!fs23.existsSync(this.ownerPath)) {
191030
191068
  return null;
191031
191069
  }
191032
191070
  const releaseLock = await this.acquireLock();
191033
191071
  try {
191034
- const content = fs22.readFileSync(this.ownerPath, "utf-8");
191072
+ const content = fs23.readFileSync(this.ownerPath, "utf-8");
191035
191073
  const ownerFile = JSON.parse(content);
191036
191074
  if (!await this.isProxyOwnerHealthy(ownerFile.owner.pid)) {
191037
191075
  return null;
@@ -191128,7 +191166,7 @@ var ProxyRegistryManager = class {
191128
191166
  */
191129
191167
  watchRegistry(onChange) {
191130
191168
  this.ensureProxyDir();
191131
- if (!fs22.existsSync(this.registryPath)) {
191169
+ if (!fs23.existsSync(this.registryPath)) {
191132
191170
  const emptyRegistry = {
191133
191171
  version: 1,
191134
191172
  keys: {},
@@ -191171,13 +191209,13 @@ var ProxyRegistryManager = class {
191171
191209
  async attemptElection(key) {
191172
191210
  const releaseLock = await this.acquireLock();
191173
191211
  try {
191174
- if (fs22.existsSync(this.ownerPath)) {
191175
- const content = fs22.readFileSync(this.ownerPath, "utf-8");
191212
+ if (fs23.existsSync(this.ownerPath)) {
191213
+ const content = fs23.readFileSync(this.ownerPath, "utf-8");
191176
191214
  const ownerFile2 = JSON.parse(content);
191177
191215
  if (await this.isProxyOwnerHealthy(ownerFile2.owner.pid)) {
191178
191216
  return false;
191179
191217
  }
191180
- fs22.unlinkSync(this.ownerPath);
191218
+ fs23.unlinkSync(this.ownerPath);
191181
191219
  }
191182
191220
  const ownerFile = {
191183
191221
  version: 1,
@@ -191195,7 +191233,7 @@ var ProxyRegistryManager = class {
191195
191233
  }
191196
191234
  }
191197
191235
  readRegistry() {
191198
- if (!fs22.existsSync(this.registryPath)) {
191236
+ if (!fs23.existsSync(this.registryPath)) {
191199
191237
  return {
191200
191238
  version: 1,
191201
191239
  keys: {},
@@ -191203,7 +191241,7 @@ var ProxyRegistryManager = class {
191203
191241
  };
191204
191242
  }
191205
191243
  try {
191206
- const content = fs22.readFileSync(this.registryPath, "utf-8");
191244
+ const content = fs23.readFileSync(this.registryPath, "utf-8");
191207
191245
  return JSON.parse(content);
191208
191246
  } catch {
191209
191247
  return {
@@ -191216,8 +191254,8 @@ var ProxyRegistryManager = class {
191216
191254
  writeFileAtomic(filePath, data) {
191217
191255
  this.ensureProxyDir();
191218
191256
  const tmpPath = filePath + ".tmp";
191219
- fs22.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
191220
- fs22.renameSync(tmpPath, filePath);
191257
+ fs23.writeFileSync(tmpPath, JSON.stringify(data, null, 2));
191258
+ fs23.renameSync(tmpPath, filePath);
191221
191259
  }
191222
191260
  };
191223
191261
 
@@ -191326,7 +191364,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191326
191364
  const setupDone = tunnelEnabled || !systemSetupNeeded();
191327
191365
  return {
191328
191366
  status: setupDone ? "loading" : "installing-ca",
191329
- ...setupDone ? {} : { caInstallPhase: "installing" },
191367
+ ...setupDone ? {} : { caInstallPhase: "prompt" },
191330
191368
  resources: /* @__PURE__ */ new Map(),
191331
191369
  resourceStatus: /* @__PURE__ */ new Map(),
191332
191370
  services: [],
@@ -191342,6 +191380,11 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191342
191380
  installSystemConfig();
191343
191381
  }
191344
191382
  }, [state.status, state.caInstallPhase]);
191383
+ useInput4((_input, key) => {
191384
+ if (state.status === "installing-ca" && state.caInstallPhase === "prompt" && key.return) {
191385
+ setState((s) => ({ ...s, caInstallPhase: "installing" }));
191386
+ }
191387
+ });
191345
191388
  async function installSystemConfig() {
191346
191389
  try {
191347
191390
  performSystemSetup();
@@ -191535,7 +191578,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191535
191578
  const configPath = path20.join(process.cwd(), "specific.hcl");
191536
191579
  const watcher = watchConfigFile(configPath, 1e3, () => {
191537
191580
  try {
191538
- const hcl = fs23.readFileSync(configPath, "utf-8");
191581
+ const hcl = fs24.readFileSync(configPath, "utf-8");
191539
191582
  parseConfig(hcl).then(() => {
191540
191583
  triggerReload();
191541
191584
  }).catch((err) => {
@@ -191661,7 +191704,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191661
191704
  return;
191662
191705
  }
191663
191706
  const configPath = path20.join(process.cwd(), "specific.hcl");
191664
- if (!fs23.existsSync(configPath)) {
191707
+ if (!fs24.existsSync(configPath)) {
191665
191708
  writeLog("system", "Waiting for specific.hcl to appear");
191666
191709
  setState((s) => ({
191667
191710
  ...s,
@@ -191679,7 +191722,7 @@ function DevUI({ instanceKey, tunnelEnabled }) {
191679
191722
  return;
191680
191723
  }
191681
191724
  let config2;
191682
- const hcl = fs23.readFileSync(configPath, "utf-8");
191725
+ const hcl = fs24.readFileSync(configPath, "utf-8");
191683
191726
  try {
191684
191727
  config2 = await parseConfig(hcl);
191685
191728
  } catch (err) {
@@ -192006,7 +192049,7 @@ Add them to the config block in specific.local`);
192006
192049
  if (service.volumes) {
192007
192050
  for (const vol of service.volumes) {
192008
192051
  const volumeDir = path20.resolve(`.specific/keys/${instanceKey}/data/volumes/${service.name}/${vol.name}`);
192009
- fs23.mkdirSync(volumeDir, { recursive: true });
192052
+ fs24.mkdirSync(volumeDir, { recursive: true });
192010
192053
  volumePaths.set(vol.name, volumeDir);
192011
192054
  }
192012
192055
  }
@@ -192062,7 +192105,7 @@ Add them to the config block in specific.local`);
192062
192105
  if (service.volumes) {
192063
192106
  for (const vol of service.volumes) {
192064
192107
  const volumeDir = path20.resolve(`.specific/keys/${instanceKey}/data/volumes/${service.name}/${vol.name}`);
192065
- fs23.mkdirSync(volumeDir, { recursive: true });
192108
+ fs24.mkdirSync(volumeDir, { recursive: true });
192066
192109
  volumePaths.set(vol.name, volumeDir);
192067
192110
  }
192068
192111
  }
@@ -192356,8 +192399,11 @@ Add them to the config block in specific.local`);
192356
192399
  };
192357
192400
  }, [reloadTrigger, readyToStart, instanceKey]);
192358
192401
  if (state.status === "installing-ca") {
192402
+ if (state.caInstallPhase === "prompt") {
192403
+ 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.local.spcf.app"), /* @__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."));
192404
+ }
192359
192405
  if (state.caInstallPhase === "installing") {
192360
- return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Local Development Setup"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Setting up TLS certificates and DNS resolution for local"), /* @__PURE__ */ React6.createElement(Text6, null, "development. This is a one-time setup for Specific projects."), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Your password is required to configure your system."), /* @__PURE__ */ React6.createElement(Text6, null, " "));
192406
+ 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"));
192361
192407
  }
192362
192408
  if (state.caInstallPhase === "error") {
192363
192409
  return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { color: "red" }, "Setup failed: ", state.caError));
@@ -192630,7 +192676,7 @@ init_open();
192630
192676
  import React7, { useState as useState6, useEffect as useEffect4, useCallback } from "react";
192631
192677
  import { render as render5, Text as Text7, Box as Box7, useApp as useApp3, useInput as useInput5 } from "ink";
192632
192678
  import Spinner5 from "ink-spinner";
192633
- import * as fs25 from "fs";
192679
+ import * as fs26 from "fs";
192634
192680
  import * as path23 from "path";
192635
192681
 
192636
192682
  // src/lib/deploy/build-tester.ts
@@ -192815,7 +192861,7 @@ async function testAllBuilds(builds, projectDir) {
192815
192861
 
192816
192862
  // src/lib/tarball/create.ts
192817
192863
  import { execSync as execSync4 } from "child_process";
192818
- import * as fs24 from "fs";
192864
+ import * as fs25 from "fs";
192819
192865
  import * as path22 from "path";
192820
192866
  import { createTarPacker, createEntryItemGenerator } from "tar-vern";
192821
192867
  function isInsideGitRepository(dir) {
@@ -192873,7 +192919,7 @@ var EXCLUDED_DIRS = [
192873
192919
  ];
192874
192920
  async function collectPaths(baseDir, currentDir, exclude) {
192875
192921
  const results = [];
192876
- const entries = await fs24.promises.readdir(currentDir, { withFileTypes: true });
192922
+ const entries = await fs25.promises.readdir(currentDir, { withFileTypes: true });
192877
192923
  for (const entry of entries) {
192878
192924
  const fullPath = path22.join(currentDir, entry.name);
192879
192925
  const relativePath = path22.relative(baseDir, fullPath);
@@ -192892,7 +192938,7 @@ async function collectPaths(baseDir, currentDir, exclude) {
192892
192938
  async function createTarArchive(projectDir) {
192893
192939
  writeLog("tarball", "Creating tarball using tar-vern (non-git project)");
192894
192940
  const configPath = path22.join(projectDir, "specific.hcl");
192895
- if (!fs24.existsSync(configPath)) {
192941
+ if (!fs25.existsSync(configPath)) {
192896
192942
  throw new Error("specific.hcl not found in project directory");
192897
192943
  }
192898
192944
  const relativePaths = await collectPaths(projectDir, projectDir, EXCLUDED_DIRS);
@@ -193509,7 +193555,8 @@ ${errorMsg}`
193509
193555
  ...s,
193510
193556
  phase: "error",
193511
193557
  deployment: status,
193512
- error: `Build "${failedBuild.serviceName}" failed: ${failedBuild.error}`
193558
+ error: `Build "${failedBuild.serviceName}" failed: ${failedBuild.error}`,
193559
+ buildOutput: failedBuild.output
193513
193560
  }));
193514
193561
  return;
193515
193562
  }
@@ -193689,6 +193736,7 @@ ${errorMsg}`
193689
193736
  phase,
193690
193737
  deployment,
193691
193738
  error,
193739
+ buildOutput,
193692
193740
  tarballSize,
193693
193741
  pendingActions,
193694
193742
  missingSecrets,
@@ -193819,16 +193867,16 @@ ${errorMsg}`
193819
193867
  onSubmit: handleConfigSubmit,
193820
193868
  onCancel: handleConfigCancel
193821
193869
  }
193822
- ), phase === "error" && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1 }, deployment?.error ? /* @__PURE__ */ React7.createElement(StructuredError, { error: deployment.error }) : /* @__PURE__ */ React7.createElement(Text7, { color: "red" }, "Error: ", error)), phase === "success" && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, "Deployment successful!"), deployment?.publicUrls && Object.keys(deployment.publicUrls).length > 0 && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Public URLs:"), Object.entries(deployment.publicUrls).map(([name, url]) => /* @__PURE__ */ React7.createElement(Text7, { key: name }, " ", name, ": ", /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, url))))));
193870
+ ), phase === "error" && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, deployment?.error ? /* @__PURE__ */ React7.createElement(StructuredError, { error: deployment.error }) : /* @__PURE__ */ React7.createElement(Text7, { color: "red", bold: true }, error), buildOutput && /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Build output:"), /* @__PURE__ */ React7.createElement(Text7, null, buildOutput))), phase === "success" && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { color: "green" }, "Deployment successful!"), deployment?.publicUrls && Object.keys(deployment.publicUrls).length > 0 && /* @__PURE__ */ React7.createElement(Box7, { marginTop: 1, flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Public URLs:"), Object.entries(deployment.publicUrls).map(([name, url]) => /* @__PURE__ */ React7.createElement(Text7, { key: name }, " ", name, ": ", /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, url))))));
193823
193871
  }
193824
193872
  async function deployCommand(environment, options2) {
193825
193873
  const configPath = path23.join(process.cwd(), "specific.hcl");
193826
- if (!fs25.existsSync(configPath)) {
193874
+ if (!fs26.existsSync(configPath)) {
193827
193875
  console.error("Error: No specific.hcl found in current directory");
193828
193876
  process.exit(1);
193829
193877
  }
193830
193878
  let config;
193831
- const hcl = fs25.readFileSync(configPath, "utf-8");
193879
+ const hcl = fs26.readFileSync(configPath, "utf-8");
193832
193880
  try {
193833
193881
  config = await parseConfig(hcl);
193834
193882
  } catch (err) {
@@ -193851,7 +193899,7 @@ async function deployCommand(environment, options2) {
193851
193899
 
193852
193900
  // src/commands/exec.tsx
193853
193901
  import { spawn as spawn7 } from "child_process";
193854
- import * as fs26 from "fs";
193902
+ import * as fs27 from "fs";
193855
193903
  import * as path24 from "path";
193856
193904
  async function execCommand(serviceName, command, instanceKey = "default") {
193857
193905
  if (command.length === 0) {
@@ -193881,12 +193929,12 @@ async function execCommand(serviceName, command, instanceKey = "default") {
193881
193929
  }
193882
193930
  };
193883
193931
  const configPath = path24.join(process.cwd(), "specific.hcl");
193884
- if (!fs26.existsSync(configPath)) {
193932
+ if (!fs27.existsSync(configPath)) {
193885
193933
  console.error("Error: No specific.hcl found in current directory");
193886
193934
  process.exit(1);
193887
193935
  }
193888
193936
  let config;
193889
- const hcl = fs26.readFileSync(configPath, "utf-8");
193937
+ const hcl = fs27.readFileSync(configPath, "utf-8");
193890
193938
  try {
193891
193939
  config = await parseConfig(hcl);
193892
193940
  } catch (err) {
@@ -194036,7 +194084,7 @@ async function execCommand(serviceName, command, instanceKey = "default") {
194036
194084
 
194037
194085
  // src/commands/psql.tsx
194038
194086
  import { spawn as spawn8 } from "child_process";
194039
- import * as fs27 from "fs";
194087
+ import * as fs28 from "fs";
194040
194088
  import * as path25 from "path";
194041
194089
  async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []) {
194042
194090
  let startedResources = [];
@@ -194055,12 +194103,12 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
194055
194103
  }
194056
194104
  };
194057
194105
  const configPath = path25.join(process.cwd(), "specific.hcl");
194058
- if (!fs27.existsSync(configPath)) {
194106
+ if (!fs28.existsSync(configPath)) {
194059
194107
  console.error("Error: No specific.hcl found in current directory");
194060
194108
  process.exit(1);
194061
194109
  }
194062
194110
  let config;
194063
- const hcl = fs27.readFileSync(configPath, "utf-8");
194111
+ const hcl = fs28.readFileSync(configPath, "utf-8");
194064
194112
  try {
194065
194113
  config = await parseConfig(hcl);
194066
194114
  } catch (err) {
@@ -194184,7 +194232,7 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
194184
194232
 
194185
194233
  // src/commands/reshape.tsx
194186
194234
  import { spawn as spawn9 } from "child_process";
194187
- import * as fs28 from "fs";
194235
+ import * as fs29 from "fs";
194188
194236
  import * as path26 from "path";
194189
194237
  var VALID_ACTIONS = ["start", "complete", "status", "abort", "check"];
194190
194238
  var MIGRATION_SUBCOMMANDS = ["start", "complete", "abort"];
@@ -194202,8 +194250,8 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
194202
194250
  let migrationsDir = "migrations";
194203
194251
  let targetDb;
194204
194252
  try {
194205
- if (fs28.existsSync(configPath)) {
194206
- const configContent = fs28.readFileSync(configPath, "utf-8");
194253
+ if (fs29.existsSync(configPath)) {
194254
+ const configContent = fs29.readFileSync(configPath, "utf-8");
194207
194255
  config = await parseConfig(configContent);
194208
194256
  if (databaseName) {
194209
194257
  const postgresConfig = config.postgres.find((p) => p.name === databaseName);
@@ -194339,7 +194387,7 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
194339
194387
  const reshapeArgs = isMigrationSubcommand ? ["migration", action] : [action];
194340
194388
  const fullMigrationsPath = path26.join(process.cwd(), migrationsDir);
194341
194389
  if (action === "check" || action === "start") {
194342
- if (fs28.existsSync(fullMigrationsPath)) {
194390
+ if (fs29.existsSync(fullMigrationsPath)) {
194343
194391
  reshapeArgs.push("--dirs", fullMigrationsPath);
194344
194392
  } else if (action === "check") {
194345
194393
  console.error(`Error: Migrations directory not found: ${fullMigrationsPath}`);
@@ -194389,7 +194437,7 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
194389
194437
  import React8, { useState as useState7, useEffect as useEffect5 } from "react";
194390
194438
  import { render as render6, Text as Text8, Box as Box8 } from "ink";
194391
194439
  import Spinner6 from "ink-spinner";
194392
- import * as fs29 from "fs";
194440
+ import * as fs30 from "fs";
194393
194441
  import * as path27 from "path";
194394
194442
  function CleanUI({ instanceKey }) {
194395
194443
  const [state, setState] = useState7({ status: "checking" });
@@ -194397,13 +194445,13 @@ function CleanUI({ instanceKey }) {
194397
194445
  async function clean() {
194398
194446
  const projectRoot = process.cwd();
194399
194447
  const specificDir = path27.join(projectRoot, ".specific");
194400
- if (!fs29.existsSync(specificDir)) {
194448
+ if (!fs30.existsSync(specificDir)) {
194401
194449
  setState({ status: "nothing" });
194402
194450
  return;
194403
194451
  }
194404
194452
  if (instanceKey) {
194405
194453
  const keyDir = path27.join(specificDir, "keys", instanceKey);
194406
- if (!fs29.existsSync(keyDir)) {
194454
+ if (!fs30.existsSync(keyDir)) {
194407
194455
  setState({ status: "nothing" });
194408
194456
  return;
194409
194457
  }
@@ -194419,7 +194467,7 @@ function CleanUI({ instanceKey }) {
194419
194467
  await stateManager.cleanStaleState();
194420
194468
  setState({ status: "cleaning" });
194421
194469
  try {
194422
- fs29.rmSync(keyDir, { recursive: true, force: true });
194470
+ fs30.rmSync(keyDir, { recursive: true, force: true });
194423
194471
  setState({ status: "success" });
194424
194472
  } catch (err) {
194425
194473
  setState({
@@ -194429,12 +194477,12 @@ function CleanUI({ instanceKey }) {
194429
194477
  }
194430
194478
  } else {
194431
194479
  const keysDir = path27.join(specificDir, "keys");
194432
- if (!fs29.existsSync(keysDir)) {
194480
+ if (!fs30.existsSync(keysDir)) {
194433
194481
  setState({ status: "nothing" });
194434
194482
  return;
194435
194483
  }
194436
- const keys = fs29.readdirSync(keysDir).filter(
194437
- (f) => fs29.statSync(path27.join(keysDir, f)).isDirectory()
194484
+ const keys = fs30.readdirSync(keysDir).filter(
194485
+ (f) => fs30.statSync(path27.join(keysDir, f)).isDirectory()
194438
194486
  );
194439
194487
  for (const key of keys) {
194440
194488
  const stateManager2 = new InstanceStateManager(projectRoot, key);
@@ -194458,7 +194506,7 @@ function CleanUI({ instanceKey }) {
194458
194506
  }
194459
194507
  setState({ status: "cleaning" });
194460
194508
  try {
194461
- fs29.rmSync(keysDir, { recursive: true, force: true });
194509
+ fs30.rmSync(keysDir, { recursive: true, force: true });
194462
194510
  setState({ status: "success" });
194463
194511
  } catch (err) {
194464
194512
  setState({
@@ -194623,7 +194671,7 @@ import { render as render9, Text as Text11, Box as Box10, useApp as useApp6 } fr
194623
194671
  import Spinner7 from "ink-spinner";
194624
194672
 
194625
194673
  // src/lib/update.ts
194626
- import * as fs30 from "fs";
194674
+ import * as fs31 from "fs";
194627
194675
  import * as path28 from "path";
194628
194676
  var BINARIES_BASE_URL = "https://binaries.specific.dev/cli";
194629
194677
  function compareVersions(a, b) {
@@ -194638,7 +194686,7 @@ function compareVersions(a, b) {
194638
194686
  return 0;
194639
194687
  }
194640
194688
  async function checkForUpdate() {
194641
- const currentVersion = "0.1.70";
194689
+ const currentVersion = "0.1.72";
194642
194690
  const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
194643
194691
  if (!response.ok) {
194644
194692
  throw new Error(`Failed to check for updates: HTTP ${response.status}`);
@@ -194654,7 +194702,7 @@ function isBinaryWritable() {
194654
194702
  const binaryPath = getCurrentBinaryPath();
194655
194703
  const dir = path28.dirname(binaryPath);
194656
194704
  try {
194657
- fs30.accessSync(dir, fs30.constants.W_OK);
194705
+ fs31.accessSync(dir, fs31.constants.W_OK);
194658
194706
  return true;
194659
194707
  } catch {
194660
194708
  return false;
@@ -194665,21 +194713,21 @@ async function performUpdate(version, onProgress) {
194665
194713
  const binaryDir = path28.dirname(binaryPath);
194666
194714
  const tempPath = path28.join(binaryDir, `.specific-update-${process.pid}`);
194667
194715
  try {
194668
- const { platform: platform5, arch: arch3 } = getPlatformInfo();
194669
- const url = `${BINARIES_BASE_URL}/${version}/specific-${platform5}-${arch3}`;
194716
+ const { platform: platform6, arch: arch3 } = getPlatformInfo();
194717
+ const url = `${BINARIES_BASE_URL}/${version}/specific-${platform6}-${arch3}`;
194670
194718
  await downloadFile(url, tempPath, onProgress);
194671
- const stat4 = fs30.statSync(tempPath);
194719
+ const stat4 = fs31.statSync(tempPath);
194672
194720
  if (stat4.size === 0) {
194673
194721
  throw new Error("Downloaded binary is empty");
194674
194722
  }
194675
- fs30.chmodSync(tempPath, 493);
194723
+ fs31.chmodSync(tempPath, 493);
194676
194724
  onProgress?.({ phase: "finalizing" });
194677
- fs30.unlinkSync(binaryPath);
194678
- fs30.renameSync(tempPath, binaryPath);
194725
+ fs31.unlinkSync(binaryPath);
194726
+ fs31.renameSync(tempPath, binaryPath);
194679
194727
  } catch (error) {
194680
194728
  try {
194681
- if (fs30.existsSync(tempPath)) {
194682
- fs30.unlinkSync(tempPath);
194729
+ if (fs31.existsSync(tempPath)) {
194730
+ fs31.unlinkSync(tempPath);
194683
194731
  }
194684
194732
  } catch {
194685
194733
  }
@@ -194689,21 +194737,21 @@ async function performUpdate(version, onProgress) {
194689
194737
 
194690
194738
  // src/lib/background-update.ts
194691
194739
  import { spawn as spawn10 } from "child_process";
194692
- import * as fs31 from "fs";
194740
+ import * as fs32 from "fs";
194693
194741
  import * as path29 from "path";
194694
- import * as os9 from "os";
194695
- var SPECIFIC_DIR = path29.join(os9.homedir(), ".specific");
194742
+ import * as os10 from "os";
194743
+ var SPECIFIC_DIR = path29.join(os10.homedir(), ".specific");
194696
194744
  var RATE_LIMIT_FILE = path29.join(SPECIFIC_DIR, "last-update-check");
194697
194745
  var LOCK_FILE = path29.join(SPECIFIC_DIR, "update.lock");
194698
194746
  var RATE_LIMIT_MS = 60 * 60 * 1e3;
194699
194747
  var STALE_LOCK_MS = 10 * 60 * 1e3;
194700
194748
  function writeCheckTimestamp() {
194701
- fs31.mkdirSync(SPECIFIC_DIR, { recursive: true });
194702
- fs31.writeFileSync(RATE_LIMIT_FILE, String(Date.now()), "utf-8");
194749
+ fs32.mkdirSync(SPECIFIC_DIR, { recursive: true });
194750
+ fs32.writeFileSync(RATE_LIMIT_FILE, String(Date.now()), "utf-8");
194703
194751
  }
194704
194752
  function isRateLimited() {
194705
194753
  try {
194706
- const content = fs31.readFileSync(RATE_LIMIT_FILE, "utf-8").trim();
194754
+ const content = fs32.readFileSync(RATE_LIMIT_FILE, "utf-8").trim();
194707
194755
  const lastCheck = parseInt(content, 10);
194708
194756
  if (isNaN(lastCheck)) return false;
194709
194757
  return Date.now() - lastCheck < RATE_LIMIT_MS;
@@ -194837,7 +194885,7 @@ function updateCommand() {
194837
194885
  var program = new Command();
194838
194886
  var env = "production";
194839
194887
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
194840
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.70").enablePositionalOptions();
194888
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.72").enablePositionalOptions();
194841
194889
  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));
194842
194890
  program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
194843
194891
  program.command("check").description("Validate specific.hcl configuration").action(checkCommand);