@specific.dev/cli 0.1.107 → 0.1.109

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 (67) 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 +151 -35
  64. package/package.json +5 -2
  65. /package/dist/admin/_next/static/{Zu_1z5_BR3qjEF5OmxQ4k → wQVAcL4Ep_DjuTPb20c3O}/_buildManifest.js +0 -0
  66. /package/dist/admin/_next/static/{Zu_1z5_BR3qjEF5OmxQ4k → wQVAcL4Ep_DjuTPb20c3O}/_clientMiddlewareManifest.json +0 -0
  67. /package/dist/admin/_next/static/{Zu_1z5_BR3qjEF5OmxQ4k → wQVAcL4Ep_DjuTPb20c3O}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -602,12 +602,12 @@ var init_is_wsl = __esm({
602
602
  // node_modules/.pnpm/powershell-utils@0.1.0/node_modules/powershell-utils/index.js
603
603
  import process3 from "node:process";
604
604
  import { Buffer as Buffer2 } from "node:buffer";
605
- import { promisify } from "node:util";
605
+ import { promisify as promisify2 } from "node:util";
606
606
  import childProcess from "node:child_process";
607
- var execFile, powerShellPath, executePowerShell;
607
+ var execFile2, powerShellPath, executePowerShell;
608
608
  var init_powershell_utils = __esm({
609
609
  "node_modules/.pnpm/powershell-utils@0.1.0/node_modules/powershell-utils/index.js"() {
610
- execFile = promisify(childProcess.execFile);
610
+ execFile2 = promisify2(childProcess.execFile);
611
611
  powerShellPath = () => `${process3.env.SYSTEMROOT || process3.env.windir || String.raw`C:\Windows`}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`;
612
612
  executePowerShell = async (command, options2 = {}) => {
613
613
  const {
@@ -615,7 +615,7 @@ var init_powershell_utils = __esm({
615
615
  ...execFileOptions
616
616
  } = options2;
617
617
  const encodedCommand = executePowerShell.encodeCommand(command);
618
- return execFile(
618
+ return execFile2(
619
619
  psPath ?? powerShellPath(),
620
620
  [
621
621
  ...executePowerShell.argumentsPrefix,
@@ -658,17 +658,17 @@ var init_utilities = __esm({
658
658
  });
659
659
 
660
660
  // node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/index.js
661
- import { promisify as promisify2 } from "node:util";
661
+ import { promisify as promisify3 } from "node:util";
662
662
  import childProcess2 from "node:child_process";
663
663
  import fs17, { constants as fsConstants } from "node:fs/promises";
664
- var execFile2, wslDrivesMountPoint, powerShellPathFromWsl, powerShellPath2, canAccessPowerShellPromise, canAccessPowerShell, wslDefaultBrowser, convertWslPathToWindows;
664
+ var execFile3, wslDrivesMountPoint, powerShellPathFromWsl, powerShellPath2, canAccessPowerShellPromise, canAccessPowerShell, wslDefaultBrowser, convertWslPathToWindows;
665
665
  var init_wsl_utils = __esm({
666
666
  "node_modules/.pnpm/wsl-utils@0.3.1/node_modules/wsl-utils/index.js"() {
667
667
  init_is_wsl();
668
668
  init_powershell_utils();
669
669
  init_utilities();
670
670
  init_is_wsl();
671
- execFile2 = promisify2(childProcess2.execFile);
671
+ execFile3 = promisify3(childProcess2.execFile);
672
672
  wslDrivesMountPoint = /* @__PURE__ */ (() => {
673
673
  const defaultMountPoint = "/mnt/";
674
674
  let mountPoint;
@@ -724,7 +724,7 @@ var init_wsl_utils = __esm({
724
724
  return path27;
725
725
  }
726
726
  try {
727
- const { stdout } = await execFile2("wslpath", ["-aw", path27], { encoding: "utf8" });
727
+ const { stdout } = await execFile3("wslpath", ["-aw", path27], { encoding: "utf8" });
728
728
  return stdout.trim();
729
729
  } catch {
730
730
  return path27;
@@ -756,14 +756,14 @@ var init_define_lazy_prop = __esm({
756
756
  });
757
757
 
758
758
  // node_modules/.pnpm/default-browser-id@5.0.1/node_modules/default-browser-id/index.js
759
- import { promisify as promisify3 } from "node:util";
759
+ import { promisify as promisify4 } from "node:util";
760
760
  import process4 from "node:process";
761
- import { execFile as execFile3 } from "node:child_process";
761
+ import { execFile as execFile4 } from "node:child_process";
762
762
  async function defaultBrowserId() {
763
763
  if (process4.platform !== "darwin") {
764
764
  throw new Error("macOS only");
765
765
  }
766
- const { stdout } = await execFileAsync("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"]);
766
+ const { stdout } = await execFileAsync2("defaults", ["read", "com.apple.LaunchServices/com.apple.launchservices.secure", "LSHandlers"]);
767
767
  const match = /LSHandlerRoleAll = "(?!-)(?<id>[^"]+?)";\s+?LSHandlerURLScheme = (?:http|https);/.exec(stdout);
768
768
  const browserId = match?.groups.id ?? "com.apple.Safari";
769
769
  if (browserId === "com.apple.safari") {
@@ -771,17 +771,17 @@ async function defaultBrowserId() {
771
771
  }
772
772
  return browserId;
773
773
  }
774
- var execFileAsync;
774
+ var execFileAsync2;
775
775
  var init_default_browser_id = __esm({
776
776
  "node_modules/.pnpm/default-browser-id@5.0.1/node_modules/default-browser-id/index.js"() {
777
- execFileAsync = promisify3(execFile3);
777
+ execFileAsync2 = promisify4(execFile4);
778
778
  }
779
779
  });
780
780
 
781
781
  // node_modules/.pnpm/run-applescript@7.1.0/node_modules/run-applescript/index.js
782
782
  import process5 from "node:process";
783
- import { promisify as promisify4 } from "node:util";
784
- import { execFile as execFile4, execFileSync } from "node:child_process";
783
+ import { promisify as promisify5 } from "node:util";
784
+ import { execFile as execFile5, execFileSync } from "node:child_process";
785
785
  async function runAppleScript(script, { humanReadableOutput = true, signal } = {}) {
786
786
  if (process5.platform !== "darwin") {
787
787
  throw new Error("macOS only");
@@ -791,13 +791,13 @@ async function runAppleScript(script, { humanReadableOutput = true, signal } = {
791
791
  if (signal) {
792
792
  execOptions.signal = signal;
793
793
  }
794
- const { stdout } = await execFileAsync2("osascript", ["-e", script, outputArguments], execOptions);
794
+ const { stdout } = await execFileAsync3("osascript", ["-e", script, outputArguments], execOptions);
795
795
  return stdout.trim();
796
796
  }
797
- var execFileAsync2;
797
+ var execFileAsync3;
798
798
  var init_run_applescript = __esm({
799
799
  "node_modules/.pnpm/run-applescript@7.1.0/node_modules/run-applescript/index.js"() {
800
- execFileAsync2 = promisify4(execFile4);
800
+ execFileAsync3 = promisify5(execFile5);
801
801
  }
802
802
  });
803
803
 
@@ -813,9 +813,9 @@ var init_bundle_name = __esm({
813
813
  });
814
814
 
815
815
  // node_modules/.pnpm/default-browser@5.4.0/node_modules/default-browser/windows.js
816
- import { promisify as promisify5 } from "node:util";
817
- import { execFile as execFile5 } from "node:child_process";
818
- async function defaultBrowser(_execFileAsync = execFileAsync3) {
816
+ import { promisify as promisify6 } from "node:util";
817
+ import { execFile as execFile6 } from "node:child_process";
818
+ async function defaultBrowser(_execFileAsync = execFileAsync4) {
819
819
  const { stdout } = await _execFileAsync("reg", [
820
820
  "QUERY",
821
821
  " HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice",
@@ -833,10 +833,10 @@ async function defaultBrowser(_execFileAsync = execFileAsync3) {
833
833
  }
834
834
  return browser;
835
835
  }
836
- var execFileAsync3, windowsBrowserProgIds, _windowsBrowserProgIdMap, UnknownBrowserError;
836
+ var execFileAsync4, windowsBrowserProgIds, _windowsBrowserProgIdMap, UnknownBrowserError;
837
837
  var init_windows = __esm({
838
838
  "node_modules/.pnpm/default-browser@5.4.0/node_modules/default-browser/windows.js"() {
839
- execFileAsync3 = promisify5(execFile5);
839
+ execFileAsync4 = promisify6(execFile6);
840
840
  windowsBrowserProgIds = {
841
841
  MSEdgeHTM: { name: "Edge", id: "com.microsoft.edge" },
842
842
  // The missing `L` is correct.
@@ -863,9 +863,9 @@ var init_windows = __esm({
863
863
  });
864
864
 
865
865
  // node_modules/.pnpm/default-browser@5.4.0/node_modules/default-browser/index.js
866
- import { promisify as promisify6 } from "node:util";
866
+ import { promisify as promisify7 } from "node:util";
867
867
  import process6 from "node:process";
868
- import { execFile as execFile6 } from "node:child_process";
868
+ import { execFile as execFile7 } from "node:child_process";
869
869
  async function defaultBrowser2() {
870
870
  if (process6.platform === "darwin") {
871
871
  const id = await defaultBrowserId();
@@ -873,7 +873,7 @@ async function defaultBrowser2() {
873
873
  return { name, id };
874
874
  }
875
875
  if (process6.platform === "linux") {
876
- const { stdout } = await execFileAsync4("xdg-mime", ["query", "default", "x-scheme-handler/http"]);
876
+ const { stdout } = await execFileAsync5("xdg-mime", ["query", "default", "x-scheme-handler/http"]);
877
877
  const id = stdout.trim();
878
878
  const name = titleize(id.replace(/.desktop$/, "").replace("-", " "));
879
879
  return { name, id };
@@ -883,14 +883,14 @@ async function defaultBrowser2() {
883
883
  }
884
884
  throw new Error("Only macOS, Linux, and Windows are supported");
885
885
  }
886
- var execFileAsync4, titleize;
886
+ var execFileAsync5, titleize;
887
887
  var init_default_browser = __esm({
888
888
  "node_modules/.pnpm/default-browser@5.4.0/node_modules/default-browser/index.js"() {
889
889
  init_default_browser_id();
890
890
  init_bundle_name();
891
891
  init_windows();
892
892
  init_windows();
893
- execFileAsync4 = promisify6(execFile6);
893
+ execFileAsync5 = promisify7(execFile7);
894
894
  titleize = (string) => string.toLowerCase().replaceAll(/(?:^|\s|-)\S/g, (x) => x.toUpperCase());
895
895
  }
896
896
  });
@@ -185191,6 +185191,8 @@ import { readFile, writeFile } from "fs/promises";
185191
185191
  import { existsSync as existsSync5 } from "fs";
185192
185192
  import * as fs6 from "fs";
185193
185193
  import * as path6 from "path";
185194
+ import { execFile } from "child_process";
185195
+ import { promisify } from "util";
185194
185196
  import * as http from "http";
185195
185197
  import * as fs7 from "fs";
185196
185198
  import * as path7 from "path";
@@ -369253,7 +369255,18 @@ async function startPostgres(pg, port, dataDir, onProgress) {
369253
369255
  }
369254
369256
  );
369255
369257
  pipeProcess("postgres", postgres);
369256
- await waitForTcpPort(host, port);
369258
+ await Promise.race([
369259
+ waitForTcpPort(host, port),
369260
+ new Promise((_, reject) => {
369261
+ postgres.once("exit", (code, signal) => {
369262
+ reject(
369263
+ new Error(
369264
+ `PostgreSQL "${pg.name}" exited during startup (code ${code}, signal ${signal}). Port ${port} may already be in use by another process.`
369265
+ )
369266
+ );
369267
+ });
369268
+ })
369269
+ ]);
369257
369270
  return {
369258
369271
  name: pg.name,
369259
369272
  type: "postgres",
@@ -369936,6 +369949,7 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
369936
369949
  }
369937
369950
  };
369938
369951
  }
369952
+ var execFileAsync = promisify(execFile);
369939
369953
  var InstanceStateManager = class {
369940
369954
  stateDir;
369941
369955
  statePath;
@@ -370036,6 +370050,7 @@ var InstanceStateManager = class {
370036
370050
  const content = fs6.readFileSync(this.statePath, "utf-8");
370037
370051
  const state = JSON.parse(content);
370038
370052
  if (!this.isProcessRunning(state.owner.pid)) {
370053
+ await this.killOrphanedProcesses(state);
370039
370054
  fs6.unlinkSync(this.statePath);
370040
370055
  return true;
370041
370056
  }
@@ -370051,6 +370066,35 @@ var InstanceStateManager = class {
370051
370066
  releaseLock();
370052
370067
  }
370053
370068
  }
370069
+ /**
370070
+ * Kill orphaned child processes left behind by a dead owner.
370071
+ * Best-effort: failures are silently ignored.
370072
+ */
370073
+ async killOrphanedProcesses(state) {
370074
+ for (const service of Object.values(state.services)) {
370075
+ if (service.pid && this.isProcessRunning(service.pid)) {
370076
+ try {
370077
+ process.kill(-service.pid, "SIGKILL");
370078
+ } catch {
370079
+ }
370080
+ }
370081
+ }
370082
+ for (const db of Object.values(state.databases)) {
370083
+ if (db.syncUrl) {
370084
+ try {
370085
+ const url = new URL(db.syncUrl);
370086
+ const port = parseInt(url.port, 10);
370087
+ if (!isNaN(port)) {
370088
+ const pid = await findPidOnPort(port);
370089
+ if (pid !== void 0) {
370090
+ process.kill(pid, "SIGKILL");
370091
+ }
370092
+ }
370093
+ } catch {
370094
+ }
370095
+ }
370096
+ }
370097
+ }
370054
370098
  async claimOwnership(command) {
370055
370099
  const releaseLock = await this.acquireLock();
370056
370100
  try {
@@ -370143,6 +370187,19 @@ var InstanceStateManager = class {
370143
370187
  fs6.renameSync(tmpPath, this.statePath);
370144
370188
  }
370145
370189
  };
370190
+ async function findPidOnPort(port) {
370191
+ try {
370192
+ const { stdout } = await execFileAsync(
370193
+ "lsof",
370194
+ ["-i", `:${port}`, "-t", "-sTCP:LISTEN"],
370195
+ { timeout: 5e3 }
370196
+ );
370197
+ const pid = parseInt(stdout.trim().split("\n")[0], 10);
370198
+ return isNaN(pid) ? void 0 : pid;
370199
+ } catch {
370200
+ return void 0;
370201
+ }
370202
+ }
370146
370203
  var __dirname = path7.dirname(fileURLToPath(import.meta.url));
370147
370204
  var adminDir = path7.join(__dirname, "admin");
370148
370205
  var _embeddedAdmin = null;
@@ -370399,6 +370456,22 @@ async function startElectric(postgres, port, dataDir, options2) {
370399
370456
  );
370400
370457
  const secret = generateRandomString(32);
370401
370458
  const host = "127.0.0.1";
370459
+ if (await checkTcpPort2(host, port)) {
370460
+ let freed = false;
370461
+ for (let i = 0; i < 30; i++) {
370462
+ await sleep2(100);
370463
+ if (!await checkTcpPort2(host, port)) {
370464
+ freed = true;
370465
+ break;
370466
+ }
370467
+ }
370468
+ if (!freed) {
370469
+ throw new Error(
370470
+ `Electric port ${port} is already in use. This may be an orphaned process from a previous session \u2014 find it with \`lsof -i :${port}\` and kill it, then retry.`
370471
+ );
370472
+ }
370473
+ writeLog("electric", `Port ${port} was occupied but is now free`);
370474
+ }
370402
370475
  const storageDir = path9.join(process.cwd(), dataDir, `electric-${postgres.name}`);
370403
370476
  fs8.rmSync(storageDir, { recursive: true, force: true });
370404
370477
  writeLog("electric", `Starting Electric for database "${postgres.name}"`);
@@ -370423,6 +370496,7 @@ async function startElectric(postgres, port, dataDir, options2) {
370423
370496
  await waitForTcpPort2(host, port);
370424
370497
  return {
370425
370498
  databaseName: postgres.name,
370499
+ pid: electric.pid,
370426
370500
  port,
370427
370501
  url: `http://${host}:${port}`,
370428
370502
  secret,
@@ -371370,7 +371444,9 @@ var BlockPortAllocator = class _BlockPortAllocator {
371370
371444
  static async create() {
371371
371445
  for (let i = 0; i < MAX_BLOCKS; i++) {
371372
371446
  const base = FIRST_BLOCK_START + i * BLOCK_SIZE;
371373
- if (await isPortAvailable(base)) {
371447
+ const ports = Array.from({ length: BLOCK_SIZE }, (_, j) => base + j);
371448
+ const results = await Promise.all(ports.map(isPortAvailable));
371449
+ if (results.every(Boolean)) {
371374
371450
  return new _BlockPortAllocator(base);
371375
371451
  }
371376
371452
  }
@@ -371720,6 +371796,9 @@ var DevEnvironment = class extends TypedEventEmitter {
371720
371796
  collectedSecrets = {};
371721
371797
  collectedConfigs = {};
371722
371798
  inputResolve = null;
371799
+ // Last-resort exit handler to kill child processes when the process exits
371800
+ // unexpectedly (e.g. SIGKILL from an agent). Runs synchronously.
371801
+ exitHandler = null;
371723
371802
  constructor(options2) {
371724
371803
  super();
371725
371804
  this.projectDir = options2?.projectDir ?? process.cwd();
@@ -371922,6 +372001,7 @@ var DevEnvironment = class extends TypedEventEmitter {
371922
372001
  if (this.stateManager) {
371923
372002
  await this.stateManager.releaseOwnership();
371924
372003
  }
372004
+ this.removeExitHandler();
371925
372005
  this.systemLog("Shutdown complete");
371926
372006
  closeDebugLog();
371927
372007
  this.setStatus("idle");
@@ -371931,6 +372011,41 @@ var DevEnvironment = class extends TypedEventEmitter {
371931
372011
  this.startResolve = null;
371932
372012
  }
371933
372013
  }
372014
+ // ── Private: exit handler ─────────────────────────────────────────────────
372015
+ /**
372016
+ * Register a synchronous process 'exit' handler that kills all known child
372017
+ * PIDs. This is a last resort for when the process exits without going
372018
+ * through shutdownInternal (e.g. parent sends SIGKILL).
372019
+ */
372020
+ registerExitHandler() {
372021
+ this.removeExitHandler();
372022
+ this.exitHandler = () => {
372023
+ for (const electric of this.electricInstances) {
372024
+ if (electric.pid) {
372025
+ try {
372026
+ process.kill(electric.pid, "SIGKILL");
372027
+ } catch {
372028
+ }
372029
+ }
372030
+ }
372031
+ for (const service of this.services) {
372032
+ const pid = service.process.pid;
372033
+ if (pid) {
372034
+ try {
372035
+ process.kill(-pid, "SIGKILL");
372036
+ } catch {
372037
+ }
372038
+ }
372039
+ }
372040
+ };
372041
+ process.on("exit", this.exitHandler);
372042
+ }
372043
+ removeExitHandler() {
372044
+ if (this.exitHandler) {
372045
+ process.removeListener("exit", this.exitHandler);
372046
+ this.exitHandler = null;
372047
+ }
372048
+ }
371934
372049
  // ── Private: config file watcher ───────────────────────────────────────────
371935
372050
  startConfigWatcher() {
371936
372051
  const configPath = path14.join(this.projectDir, "specific.hcl");
@@ -372108,6 +372223,7 @@ var DevEnvironment = class extends TypedEventEmitter {
372108
372223
  if (result.cancelled) return;
372109
372224
  resources = result.resources;
372110
372225
  this.mailServers = result.mail;
372226
+ this.registerExitHandler();
372111
372227
  } catch (err) {
372112
372228
  const errorMsg = `Failed to start resources: ${err instanceof Error ? err.message : String(err)}`;
372113
372229
  writeLog("system:error", errorMsg);
@@ -373084,7 +373200,7 @@ function trackEvent(event, properties) {
373084
373200
  event,
373085
373201
  properties: {
373086
373202
  ...properties,
373087
- cli_version: "0.1.107",
373203
+ cli_version: "0.1.109",
373088
373204
  platform: process.platform,
373089
373205
  node_version: process.version,
373090
373206
  project_id: getProjectId()
@@ -373521,7 +373637,7 @@ import { render as render3, Text as Text3, Box as Box3 } from "ink";
373521
373637
  import Spinner2 from "ink-spinner";
373522
373638
  import * as fs21 from "fs";
373523
373639
  import * as path19 from "path";
373524
- import { execFile as execFile7 } from "child_process";
373640
+ import { execFile as execFile8 } from "child_process";
373525
373641
 
373526
373642
  // node_modules/.pnpm/@specific+config@file+..+config/node_modules/@specific/config/dist/parser.js
373527
373643
  var import_hcl2_json_parser3 = __toESM(require_dist2(), 1);
@@ -374175,7 +374291,7 @@ async function runReshapeCheck(migrationsDir) {
374175
374291
  const binary = await ensureBinary(reshapeBinary);
374176
374292
  const reshapePath = binary.executables["reshape"];
374177
374293
  return new Promise((resolve9) => {
374178
- execFile7(reshapePath, ["check", "--dirs", migrationsDir], (err, _stdout, stderr) => {
374294
+ execFile8(reshapePath, ["check", "--dirs", migrationsDir], (err, _stdout, stderr) => {
374179
374295
  if (err) {
374180
374296
  const errorMsg = stderr.trim() || err.message;
374181
374297
  resolve9({ success: false, error: errorMsg });
@@ -376763,7 +376879,7 @@ function compareVersions(a, b) {
376763
376879
  return 0;
376764
376880
  }
376765
376881
  async function checkForUpdate() {
376766
- const currentVersion = "0.1.107";
376882
+ const currentVersion = "0.1.109";
376767
376883
  const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
376768
376884
  if (!response.ok) {
376769
376885
  throw new Error(`Failed to check for updates: HTTP ${response.status}`);
@@ -377031,7 +377147,7 @@ async function projectListCommand() {
377031
377147
  var program = new Command();
377032
377148
  var env = "production";
377033
377149
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
377034
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.107").enablePositionalOptions();
377150
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.109").enablePositionalOptions();
377035
377151
  program.command("init").description("Initialize project for use with a coding agent").option("--agent <name...>", "Agents to configure (cursor, claude, codex, other)").addHelpText("after", `
377036
377152
  Examples:
377037
377153
  $ specific init
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specific.dev/cli",
3
- "version": "0.1.107",
3
+ "version": "0.1.109",
4
4
  "description": "CLI for Specific infrastructure-as-code",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",
@@ -17,7 +17,10 @@
17
17
  "link:dev": "npm run build:dev && npm link",
18
18
  "link:staging": "npm run build:staging && npm link",
19
19
  "link:prod": "npm run build && npm link",
20
- "build:binary": "npx tsx scripts/build-binary.ts"
20
+ "build:binary": "npx tsx scripts/build-binary.ts",
21
+ "link:binary:dev": "NODE_ENV=development npm run build:binary && codesign -s - dist/specific && cp dist/specific ~/.local/bin/specific",
22
+ "link:binary:staging": "NODE_ENV=staging npm run build:binary && codesign -s - dist/specific && cp dist/specific ~/.local/bin/specific",
23
+ "link:binary:prod": "npm run build:binary && codesign -s - dist/specific && cp dist/specific ~/.local/bin/specific"
21
24
  },
22
25
  "keywords": [
23
26
  "infrastructure",