@specific.dev/cli 0.1.72 → 0.1.74

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/admin/404/index.html +1 -1
  2. package/dist/admin/404.html +1 -1
  3. package/dist/admin/__next.!KGRlZmF1bHQp.__PAGE__.txt +2 -2
  4. package/dist/admin/__next.!KGRlZmF1bHQp.txt +5 -5
  5. package/dist/admin/__next._full.txt +8 -8
  6. package/dist/admin/__next._head.txt +1 -1
  7. package/dist/admin/__next._index.txt +3 -3
  8. package/dist/admin/__next._tree.txt +1 -1
  9. package/dist/admin/_next/static/chunks/4bd160f783f6ccf1.js +1 -0
  10. package/dist/admin/_not-found/__next._full.txt +3 -3
  11. package/dist/admin/_not-found/__next._head.txt +1 -1
  12. package/dist/admin/_not-found/__next._index.txt +3 -3
  13. package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
  14. package/dist/admin/_not-found/__next._not-found.txt +1 -1
  15. package/dist/admin/_not-found/__next._tree.txt +1 -1
  16. package/dist/admin/_not-found/index.html +1 -1
  17. package/dist/admin/_not-found/index.txt +3 -3
  18. package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.__PAGE__.txt +2 -2
  19. package/dist/admin/databases/__next.!KGRlZmF1bHQp.databases.txt +1 -1
  20. package/dist/admin/databases/__next.!KGRlZmF1bHQp.txt +5 -5
  21. package/dist/admin/databases/__next._full.txt +8 -8
  22. package/dist/admin/databases/__next._head.txt +1 -1
  23. package/dist/admin/databases/__next._index.txt +3 -3
  24. package/dist/admin/databases/__next._tree.txt +1 -1
  25. package/dist/admin/databases/index.html +1 -1
  26. package/dist/admin/databases/index.txt +8 -8
  27. package/dist/admin/fullscreen/__next._full.txt +4 -4
  28. package/dist/admin/fullscreen/__next._head.txt +1 -1
  29. package/dist/admin/fullscreen/__next._index.txt +3 -3
  30. package/dist/admin/fullscreen/__next._tree.txt +1 -1
  31. package/dist/admin/fullscreen/__next.fullscreen.__PAGE__.txt +2 -2
  32. package/dist/admin/fullscreen/__next.fullscreen.txt +1 -1
  33. package/dist/admin/fullscreen/databases/__next._full.txt +4 -4
  34. package/dist/admin/fullscreen/databases/__next._head.txt +1 -1
  35. package/dist/admin/fullscreen/databases/__next._index.txt +3 -3
  36. package/dist/admin/fullscreen/databases/__next._tree.txt +1 -1
  37. package/dist/admin/fullscreen/databases/__next.fullscreen.databases.__PAGE__.txt +2 -2
  38. package/dist/admin/fullscreen/databases/__next.fullscreen.databases.txt +1 -1
  39. package/dist/admin/fullscreen/databases/__next.fullscreen.txt +1 -1
  40. package/dist/admin/fullscreen/databases/index.html +1 -1
  41. package/dist/admin/fullscreen/databases/index.txt +4 -4
  42. package/dist/admin/fullscreen/index.html +1 -1
  43. package/dist/admin/fullscreen/index.txt +4 -4
  44. package/dist/admin/index.html +1 -1
  45. package/dist/admin/index.txt +8 -8
  46. package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.__PAGE__.txt +2 -2
  47. package/dist/admin/mail/__next.!KGRlZmF1bHQp.mail.txt +1 -1
  48. package/dist/admin/mail/__next.!KGRlZmF1bHQp.txt +5 -5
  49. package/dist/admin/mail/__next._full.txt +8 -8
  50. package/dist/admin/mail/__next._head.txt +1 -1
  51. package/dist/admin/mail/__next._index.txt +3 -3
  52. package/dist/admin/mail/__next._tree.txt +1 -1
  53. package/dist/admin/mail/index.html +1 -1
  54. package/dist/admin/mail/index.txt +8 -8
  55. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.txt +5 -5
  56. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.__PAGE__.txt +2 -2
  57. package/dist/admin/workflows/__next.!KGRlZmF1bHQp.workflows.txt +1 -1
  58. package/dist/admin/workflows/__next._full.txt +8 -8
  59. package/dist/admin/workflows/__next._head.txt +1 -1
  60. package/dist/admin/workflows/__next._index.txt +3 -3
  61. package/dist/admin/workflows/__next._tree.txt +1 -1
  62. package/dist/admin/workflows/index.html +1 -1
  63. package/dist/admin/workflows/index.txt +8 -8
  64. package/dist/cli.js +137 -341
  65. package/dist/postinstall.js +1 -1
  66. package/package.json +1 -1
  67. package/dist/admin/_next/static/chunks/47a5dab862795de7.js +0 -1
  68. /package/dist/admin/_next/static/{EdA1eXzy3MBFAW53uuebn → rlz8iRhM93pN2MuVf1ScD}/_buildManifest.js +0 -0
  69. /package/dist/admin/_next/static/{EdA1eXzy3MBFAW53uuebn → rlz8iRhM93pN2MuVf1ScD}/_clientMiddlewareManifest.json +0 -0
  70. /package/dist/admin/_next/static/{EdA1eXzy3MBFAW53uuebn → rlz8iRhM93pN2MuVf1ScD}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -637,19 +637,19 @@ var init_open = __esm({
637
637
  }
638
638
  const subprocess = childProcess3.spawn(command, cliArguments, childProcessOptions);
639
639
  if (options2.wait) {
640
- return new Promise((resolve10, reject) => {
640
+ return new Promise((resolve9, reject) => {
641
641
  subprocess.once("error", reject);
642
642
  subprocess.once("close", (exitCode) => {
643
643
  if (!options2.allowNonzeroExitCode && exitCode !== 0) {
644
644
  reject(new Error(`Exited with code ${exitCode}`));
645
645
  return;
646
646
  }
647
- resolve10(subprocess);
647
+ resolve9(subprocess);
648
648
  });
649
649
  });
650
650
  }
651
651
  if (isFallbackAttempt) {
652
- return new Promise((resolve10, reject) => {
652
+ return new Promise((resolve9, reject) => {
653
653
  subprocess.once("error", reject);
654
654
  subprocess.once("spawn", () => {
655
655
  subprocess.once("close", (exitCode) => {
@@ -659,17 +659,17 @@ var init_open = __esm({
659
659
  return;
660
660
  }
661
661
  subprocess.unref();
662
- resolve10(subprocess);
662
+ resolve9(subprocess);
663
663
  });
664
664
  });
665
665
  });
666
666
  }
667
667
  subprocess.unref();
668
- return new Promise((resolve10, reject) => {
668
+ return new Promise((resolve9, reject) => {
669
669
  subprocess.once("error", reject);
670
670
  subprocess.once("spawn", () => {
671
671
  subprocess.off("error", reject);
672
- resolve10(subprocess);
672
+ resolve9(subprocess);
673
673
  });
674
674
  });
675
675
  };
@@ -183448,9 +183448,25 @@ import { execSync } from "child_process";
183448
183448
  function getCADir() {
183449
183449
  return path.join(os.homedir(), ".specific", "root_ca");
183450
183450
  }
183451
+ var EXPECTED_DOMAIN = "spcf.localhost";
183451
183452
  function caFilesExist() {
183452
183453
  const caDir = getCADir();
183453
- return fs.existsSync(path.join(caDir, "ca.key")) && fs.existsSync(path.join(caDir, "ca.crt"));
183454
+ const keyPath = path.join(caDir, "ca.key");
183455
+ const certPath = path.join(caDir, "ca.crt");
183456
+ if (!fs.existsSync(keyPath) || !fs.existsSync(certPath)) {
183457
+ return false;
183458
+ }
183459
+ try {
183460
+ const certPem = fs.readFileSync(certPath, "utf-8");
183461
+ const cert = forge.pki.certificateFromPem(certPem);
183462
+ const nameConstraints = cert.getExtension("nameConstraints");
183463
+ if (!nameConstraints) {
183464
+ return false;
183465
+ }
183466
+ return nameConstraints.value.includes(EXPECTED_DOMAIN);
183467
+ } catch {
183468
+ return false;
183469
+ }
183454
183470
  }
183455
183471
  function caInstalledInTrustStore() {
183456
183472
  if (!caFilesExist()) {
@@ -183539,8 +183555,8 @@ function generateRootCA() {
183539
183555
  cert.setSubject(attrs);
183540
183556
  cert.setIssuer(attrs);
183541
183557
  const nameConstraintsExt = createNameConstraintsExtension([
183542
- ".local.spcf.app",
183543
- "local.spcf.app"
183558
+ ".spcf.localhost",
183559
+ "spcf.localhost"
183544
183560
  ]);
183545
183561
  cert.setExtensions([
183546
183562
  { name: "basicConstraints", cA: true, critical: true },
@@ -183638,7 +183654,8 @@ function generateCertificate(domain, keys = []) {
183638
183654
  // src/lib/dev/resolver-config.ts
183639
183655
  import * as fs2 from "fs";
183640
183656
  import * as os2 from "os";
183641
- var RESOLVER_FILE_MACOS = "/etc/resolver/local.spcf.app";
183657
+ var RESOLVER_FILE_MACOS = "/etc/resolver/spcf.localhost";
183658
+ var OLD_RESOLVER_FILE_MACOS = "/etc/resolver/local.spcf.app";
183642
183659
  var RESOLVER_FILE_LINUX = "/etc/systemd/resolved.conf.d/specific-local.conf";
183643
183660
  function resolverConfigExists() {
183644
183661
  const platform6 = os2.platform();
@@ -183654,13 +183671,14 @@ function getResolverInstallCommands(port) {
183654
183671
  if (platform6 === "darwin") {
183655
183672
  return [
183656
183673
  "mkdir -p /etc/resolver",
183674
+ `rm -f ${OLD_RESOLVER_FILE_MACOS}`,
183657
183675
  `printf "nameserver 127.0.0.1\\nport ${port}\\n" > ${RESOLVER_FILE_MACOS}`
183658
183676
  ];
183659
183677
  } else if (platform6 === "linux") {
183660
183678
  if (fs2.existsSync("/etc/systemd/resolved.conf.d") || fs2.existsSync("/etc/systemd")) {
183661
183679
  return [
183662
183680
  "mkdir -p /etc/systemd/resolved.conf.d",
183663
- `printf "[Resolve]\\nDNS=127.0.0.1:${port}\\nDomains=~local.spcf.app\\n" > ${RESOLVER_FILE_LINUX}`,
183681
+ `printf "[Resolve]\\nDNS=127.0.0.1:${port}\\nDomains=~spcf.localhost\\n" > ${RESOLVER_FILE_LINUX}`,
183664
183682
  "systemctl restart systemd-resolved"
183665
183683
  ];
183666
183684
  }
@@ -184028,7 +184046,7 @@ async function pollUntilToken(deviceAuth, isCancelled) {
184028
184046
  return null;
184029
184047
  }
184030
184048
  function sleep(ms) {
184031
- return new Promise((resolve10) => setTimeout(resolve10, ms));
184049
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
184032
184050
  }
184033
184051
 
184034
184052
  // src/lib/auth/login.tsx
@@ -184045,7 +184063,7 @@ function LoginUI({
184045
184063
  return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", gap: 1 }, isReauthentication && /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "Session expired. Please log in again."), /* @__PURE__ */ React.createElement(Text, { bold: true }, "Log in to Specific"), state.phase === "waiting-for-browser" ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, null, "Your authentication code:", " ", /* @__PURE__ */ React.createElement(Text, { color: "cyan", bold: true }, state.userCode))), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: "blue" }, /* @__PURE__ */ React.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React.createElement(Text, null, " Waiting for authentication in browser...")), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, "If the browser didn't open, visit: ", state.verificationUri)) : /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Text, { color: "blue" }, /* @__PURE__ */ React.createElement(Spinner, { type: "dots" })), /* @__PURE__ */ React.createElement(Text, null, " Initiating login...")));
184046
184064
  }
184047
184065
  function performLogin(options2 = {}) {
184048
- return new Promise((resolve10) => {
184066
+ return new Promise((resolve9) => {
184049
184067
  let currentState = { phase: "initiating" };
184050
184068
  let flowHandle;
184051
184069
  const instance = render(
@@ -184085,14 +184103,14 @@ function performLogin(options2 = {}) {
184085
184103
  process.off("SIGINT", handleExit);
184086
184104
  process.off("SIGTERM", handleExit);
184087
184105
  instance.unmount();
184088
- resolve10({ success: true, userEmail: newState.email });
184106
+ resolve9({ success: true, userEmail: newState.email });
184089
184107
  }, 100);
184090
184108
  } else if (newState.phase === "error") {
184091
184109
  setTimeout(() => {
184092
184110
  process.off("SIGINT", handleExit);
184093
184111
  process.off("SIGTERM", handleExit);
184094
184112
  instance.unmount();
184095
- resolve10({ success: false, error: new Error(newState.message) });
184113
+ resolve9({ success: false, error: new Error(newState.message) });
184096
184114
  }, 100);
184097
184115
  }
184098
184116
  }
@@ -184569,7 +184587,7 @@ function trackEvent(event, properties) {
184569
184587
  event,
184570
184588
  properties: {
184571
184589
  ...properties,
184572
- cli_version: "0.1.72",
184590
+ cli_version: "0.1.74",
184573
184591
  platform: process.platform,
184574
184592
  node_version: process.version,
184575
184593
  project_id: getProjectId()
@@ -184832,7 +184850,7 @@ function InitUI() {
184832
184850
  }, [phase, exit]);
184833
184851
  if (phase === "installing-ca") {
184834
184852
  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."));
184853
+ return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Configure development environment"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "We need to do a one-time setup for all your Specific projects."), /* @__PURE__ */ React2.createElement(Text2, null, "This is so we can run your apps locally with secure URLs:"), /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "green" }, "https://your-app.spcf.localhost"), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "You will be prompted to authorize with your password."), /* @__PURE__ */ React2.createElement(Text2, null, " "), /* @__PURE__ */ React2.createElement(Text2, null, "Press ", /* @__PURE__ */ React2.createElement(Text2, { bold: true, color: "cyan" }, "Enter"), " to authorize."));
184836
184854
  }
184837
184855
  if (caInstallPhase === "installing") {
184838
184856
  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"));
@@ -186030,10 +186048,10 @@ async function downloadFile(url, destPath, onProgress) {
186030
186048
  });
186031
186049
  }
186032
186050
  }
186033
- await new Promise((resolve10, reject) => {
186051
+ await new Promise((resolve9, reject) => {
186034
186052
  fileStream.end((err) => {
186035
186053
  if (err) reject(err);
186036
- else resolve10();
186054
+ else resolve9();
186037
186055
  });
186038
186056
  });
186039
186057
  fs13.renameSync(partPath, destPath);
@@ -186259,13 +186277,13 @@ async function runReshapeCheck(migrationsDir) {
186259
186277
  try {
186260
186278
  const binary = await ensureBinary(reshapeBinary);
186261
186279
  const reshapePath = binary.executables["reshape"];
186262
- return new Promise((resolve10) => {
186280
+ return new Promise((resolve9) => {
186263
186281
  execFile7(reshapePath, ["check", "--dirs", migrationsDir], (err, _stdout, stderr) => {
186264
186282
  if (err) {
186265
186283
  const errorMsg = stderr.trim() || err.message;
186266
- resolve10({ success: false, error: errorMsg });
186284
+ resolve9({ success: false, error: errorMsg });
186267
186285
  } else {
186268
- resolve10({ success: true });
186286
+ resolve9({ success: true });
186269
186287
  }
186270
186288
  });
186271
186289
  });
@@ -187234,7 +187252,7 @@ var NodeFsHandler = class {
187234
187252
  this._addToNodeFs(path30, initialAdd, wh, depth + 1);
187235
187253
  }
187236
187254
  }).on(EV.ERROR, this._boundHandleError);
187237
- return new Promise((resolve10, reject) => {
187255
+ return new Promise((resolve9, reject) => {
187238
187256
  if (!stream)
187239
187257
  return reject();
187240
187258
  stream.once(STR_END, () => {
@@ -187243,7 +187261,7 @@ var NodeFsHandler = class {
187243
187261
  return;
187244
187262
  }
187245
187263
  const wasThrottled = throttler ? throttler.clear() : false;
187246
- resolve10(void 0);
187264
+ resolve9(void 0);
187247
187265
  previous.getChildren().filter((item) => {
187248
187266
  return item !== directory && !current.has(item);
187249
187267
  }).forEach((item) => {
@@ -188307,7 +188325,7 @@ async function startStorage(storage, port, dataDir) {
188307
188325
  };
188308
188326
  }
188309
188327
  async function runCommand(command, args, env2) {
188310
- return new Promise((resolve10, reject) => {
188328
+ return new Promise((resolve9, reject) => {
188311
188329
  const proc = spawn(command, args, {
188312
188330
  stdio: ["ignore", "pipe", "pipe"],
188313
188331
  env: env2
@@ -188318,7 +188336,7 @@ async function runCommand(command, args, env2) {
188318
188336
  });
188319
188337
  proc.on("close", (code) => {
188320
188338
  if (code === 0) {
188321
- resolve10();
188339
+ resolve9();
188322
188340
  } else {
188323
188341
  reject(new Error(`Command failed with code ${code}: ${stderr}`));
188324
188342
  }
@@ -188327,7 +188345,7 @@ async function runCommand(command, args, env2) {
188327
188345
  });
188328
188346
  }
188329
188347
  async function createPostgresDatabase(postgresPath, dataDir, dbName, env2) {
188330
- return new Promise((resolve10, reject) => {
188348
+ return new Promise((resolve9, reject) => {
188331
188349
  const proc = spawn(
188332
188350
  postgresPath,
188333
188351
  ["--single", "-D", dataDir, "postgres"],
@@ -188341,7 +188359,7 @@ async function createPostgresDatabase(postgresPath, dataDir, dbName, env2) {
188341
188359
  stderr += data.toString();
188342
188360
  });
188343
188361
  proc.on("close", (code) => {
188344
- resolve10();
188362
+ resolve9();
188345
188363
  });
188346
188364
  proc.on("error", reject);
188347
188365
  proc.stdin?.write(`CREATE DATABASE "${dbName}";
@@ -188361,33 +188379,33 @@ async function waitForTcpPort(host, port, timeoutMs = 3e4) {
188361
188379
  throw new Error(`Port ${port} did not become available within timeout`);
188362
188380
  }
188363
188381
  function checkTcpPort(host, port) {
188364
- return new Promise((resolve10) => {
188382
+ return new Promise((resolve9) => {
188365
188383
  const socket = new net.Socket();
188366
188384
  socket.setTimeout(1e3);
188367
188385
  socket.on("connect", () => {
188368
188386
  socket.destroy();
188369
- resolve10(true);
188387
+ resolve9(true);
188370
188388
  });
188371
188389
  socket.on("timeout", () => {
188372
188390
  socket.destroy();
188373
- resolve10(false);
188391
+ resolve9(false);
188374
188392
  });
188375
188393
  socket.on("error", () => {
188376
188394
  socket.destroy();
188377
- resolve10(false);
188395
+ resolve9(false);
188378
188396
  });
188379
188397
  socket.connect(port, host);
188380
188398
  });
188381
188399
  }
188382
188400
  async function stopProcess(proc) {
188383
- return new Promise((resolve10) => {
188401
+ return new Promise((resolve9) => {
188384
188402
  if (proc.killed || proc.exitCode !== null) {
188385
- resolve10();
188403
+ resolve9();
188386
188404
  return;
188387
188405
  }
188388
188406
  proc.once("exit", () => {
188389
188407
  clearTimeout(forceKillTimeout);
188390
- resolve10();
188408
+ resolve9();
188391
188409
  });
188392
188410
  proc.kill("SIGTERM");
188393
188411
  const forceKillTimeout = setTimeout(() => {
@@ -188398,7 +188416,7 @@ async function stopProcess(proc) {
188398
188416
  });
188399
188417
  }
188400
188418
  function sleep2(ms) {
188401
- return new Promise((resolve10) => setTimeout(resolve10, ms));
188419
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
188402
188420
  }
188403
188421
 
188404
188422
  // src/lib/dev/service-runner.ts
@@ -188882,14 +188900,14 @@ function startService(service, resources, secrets, configs, endpointPorts, servi
188882
188900
  ports: endpointPorts,
188883
188901
  process: child,
188884
188902
  async stop() {
188885
- return new Promise((resolve10) => {
188903
+ return new Promise((resolve9) => {
188886
188904
  if (child.killed || child.exitCode !== null) {
188887
- resolve10();
188905
+ resolve9();
188888
188906
  return;
188889
188907
  }
188890
188908
  child.once("exit", () => {
188891
188909
  clearTimeout(forceKillTimeout);
188892
- resolve10();
188910
+ resolve9();
188893
188911
  });
188894
188912
  const pid = child.pid;
188895
188913
  if (pid) {
@@ -188983,7 +189001,7 @@ var InstanceStateManager = class {
188983
189001
  }
188984
189002
  continue;
188985
189003
  }
188986
- await new Promise((resolve10) => setTimeout(resolve10, 100));
189004
+ await new Promise((resolve9) => setTimeout(resolve9, 100));
188987
189005
  } else {
188988
189006
  throw e;
188989
189007
  }
@@ -189138,8 +189156,8 @@ var adminDir = path13.join(__dirname3, "admin");
189138
189156
  var _embeddedAdmin = null;
189139
189157
  var HTTP_PORT = 80;
189140
189158
  var HTTPS_PORT = 443;
189141
- var DOMAIN_SUFFIX = ".local.spcf.app";
189142
- var ADMIN_DOMAIN = "local.spcf.app";
189159
+ var DOMAIN_SUFFIX = ".spcf.localhost";
189160
+ var ADMIN_DOMAIN = "spcf.localhost";
189143
189161
  var DRIZZLE_GATEWAY_PREFIX = "__drizzle_gateway";
189144
189162
  var TEMPORAL_UI_PREFIX = "__temporal";
189145
189163
  var MIME_TYPES = {
@@ -189316,7 +189334,7 @@ async function startHttpProxy(services, certificate, getState, instanceKey = "de
189316
189334
  handleRequest
189317
189335
  );
189318
189336
  httpsServer.on("upgrade", handleUpgrade);
189319
- return new Promise((resolve10, reject) => {
189337
+ return new Promise((resolve9, reject) => {
189320
189338
  let httpStarted = false;
189321
189339
  let httpsStarted = false;
189322
189340
  let failed = false;
@@ -189327,7 +189345,7 @@ async function startHttpProxy(services, certificate, getState, instanceKey = "de
189327
189345
  "proxy",
189328
189346
  `HTTP/HTTPS proxy started on ports ${HTTP_PORT}/${HTTPS_PORT}`
189329
189347
  );
189330
- resolve10({
189348
+ resolve9({
189331
189349
  httpPort: HTTP_PORT,
189332
189350
  httpsPort: HTTPS_PORT,
189333
189351
  updateServices,
@@ -189339,13 +189357,13 @@ async function startHttpProxy(services, certificate, getState, instanceKey = "de
189339
189357
  writeLog("proxy", "Certificate updated");
189340
189358
  },
189341
189359
  async stop() {
189342
- return new Promise((resolve11) => {
189360
+ return new Promise((resolve10) => {
189343
189361
  let closed = 0;
189344
189362
  const onClose = () => {
189345
189363
  closed++;
189346
189364
  if (closed === 2) {
189347
189365
  clearTimeout(forceCloseTimeout);
189348
- resolve11();
189366
+ resolve10();
189349
189367
  }
189350
189368
  };
189351
189369
  httpServer.close(onClose);
@@ -189353,7 +189371,7 @@ async function startHttpProxy(services, certificate, getState, instanceKey = "de
189353
189371
  const forceCloseTimeout = setTimeout(() => {
189354
189372
  httpServer.closeAllConnections?.();
189355
189373
  httpsServer.closeAllConnections?.();
189356
- resolve11();
189374
+ resolve10();
189357
189375
  }, 2e3);
189358
189376
  });
189359
189377
  }
@@ -189463,9 +189481,9 @@ function sendNotFound(res, requestedService, serviceMap) {
189463
189481
  const serviceList = Array.from(serviceMap.keys()).map((key) => {
189464
189482
  const parts = key.split(".");
189465
189483
  if (parts.length === 1) {
189466
- return `<li>${parts[0]}.local.spcf.app</li>`;
189484
+ return `<li>${parts[0]}.spcf.localhost</li>`;
189467
189485
  } else {
189468
- return `<li>${key}.local.spcf.app</li>`;
189486
+ return `<li>${key}.spcf.localhost</li>`;
189469
189487
  }
189470
189488
  }).join("\n");
189471
189489
  const message = requestedService ? `No service named "${requestedService}" is running.` : "Invalid host header.";
@@ -189581,7 +189599,7 @@ function serveFileContent(res, filePath, contentType, statusCode = 200) {
189581
189599
  }
189582
189600
  }
189583
189601
  async function startAdminServer(getState) {
189584
- return new Promise((resolve10, reject) => {
189602
+ return new Promise((resolve9, reject) => {
189585
189603
  const server = http.createServer((req, res) => {
189586
189604
  const url = new URL(req.url || "/", "http://localhost");
189587
189605
  res.setHeader("Access-Control-Allow-Origin", "*");
@@ -189613,7 +189631,7 @@ async function startAdminServer(getState) {
189613
189631
  }
189614
189632
  const port = addr.port;
189615
189633
  writeLog("admin", `Admin API server started on port ${port}`);
189616
- resolve10({
189634
+ resolve9({
189617
189635
  port,
189618
189636
  stop: () => new Promise((res, rej) => {
189619
189637
  server.close((err) => err ? rej(err) : res());
@@ -189774,33 +189792,33 @@ async function waitForTcpPort2(host, port, timeoutMs = 3e4) {
189774
189792
  throw new Error(`Electric port ${port} did not become available within timeout`);
189775
189793
  }
189776
189794
  function checkTcpPort2(host, port) {
189777
- return new Promise((resolve10) => {
189795
+ return new Promise((resolve9) => {
189778
189796
  const socket = new net2.Socket();
189779
189797
  socket.setTimeout(1e3);
189780
189798
  socket.on("connect", () => {
189781
189799
  socket.destroy();
189782
- resolve10(true);
189800
+ resolve9(true);
189783
189801
  });
189784
189802
  socket.on("timeout", () => {
189785
189803
  socket.destroy();
189786
- resolve10(false);
189804
+ resolve9(false);
189787
189805
  });
189788
189806
  socket.on("error", () => {
189789
189807
  socket.destroy();
189790
- resolve10(false);
189808
+ resolve9(false);
189791
189809
  });
189792
189810
  socket.connect(port, host);
189793
189811
  });
189794
189812
  }
189795
189813
  async function stopProcess2(proc) {
189796
- return new Promise((resolve10) => {
189814
+ return new Promise((resolve9) => {
189797
189815
  if (proc.killed || proc.exitCode !== null) {
189798
- resolve10();
189816
+ resolve9();
189799
189817
  return;
189800
189818
  }
189801
189819
  proc.once("exit", () => {
189802
189820
  clearTimeout(forceKillTimeout);
189803
- resolve10();
189821
+ resolve9();
189804
189822
  });
189805
189823
  proc.kill("SIGTERM");
189806
189824
  const forceKillTimeout = setTimeout(() => {
@@ -189811,7 +189829,7 @@ async function stopProcess2(proc) {
189811
189829
  });
189812
189830
  }
189813
189831
  function sleep3(ms) {
189814
- return new Promise((resolve10) => setTimeout(resolve10, ms));
189832
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
189815
189833
  }
189816
189834
 
189817
189835
  // src/lib/dev/mail-manager.ts
@@ -189906,20 +189924,20 @@ async function startMailServer(mail, smtpPort, apiPort) {
189906
189924
  res.writeHead(404);
189907
189925
  res.end();
189908
189926
  });
189909
- await new Promise((resolve10, reject) => {
189910
- smtpServer.listen(smtpPort, "127.0.0.1", () => resolve10());
189927
+ await new Promise((resolve9, reject) => {
189928
+ smtpServer.listen(smtpPort, "127.0.0.1", () => resolve9());
189911
189929
  smtpServer.on("error", reject);
189912
189930
  });
189913
- await new Promise((resolve10, reject) => {
189914
- httpServer.listen(apiPort, "127.0.0.1", () => resolve10());
189931
+ await new Promise((resolve9, reject) => {
189932
+ httpServer.listen(apiPort, "127.0.0.1", () => resolve9());
189915
189933
  httpServer.on("error", reject);
189916
189934
  });
189917
189935
  const stop = async () => {
189918
- await new Promise((resolve10) => {
189919
- smtpServer.close(() => resolve10());
189936
+ await new Promise((resolve9) => {
189937
+ smtpServer.close(() => resolve9());
189920
189938
  });
189921
- await new Promise((resolve10) => {
189922
- httpServer.close(() => resolve10());
189939
+ await new Promise((resolve9) => {
189940
+ httpServer.close(() => resolve9());
189923
189941
  });
189924
189942
  };
189925
189943
  const resource = {
@@ -190023,33 +190041,33 @@ async function waitForTcpPort3(host, port, timeoutMs = 3e4) {
190023
190041
  );
190024
190042
  }
190025
190043
  function checkTcpPort3(host, port) {
190026
- return new Promise((resolve10) => {
190044
+ return new Promise((resolve9) => {
190027
190045
  const socket = new net3.Socket();
190028
190046
  socket.setTimeout(1e3);
190029
190047
  socket.on("connect", () => {
190030
190048
  socket.destroy();
190031
- resolve10(true);
190049
+ resolve9(true);
190032
190050
  });
190033
190051
  socket.on("timeout", () => {
190034
190052
  socket.destroy();
190035
- resolve10(false);
190053
+ resolve9(false);
190036
190054
  });
190037
190055
  socket.on("error", () => {
190038
190056
  socket.destroy();
190039
- resolve10(false);
190057
+ resolve9(false);
190040
190058
  });
190041
190059
  socket.connect(port, host);
190042
190060
  });
190043
190061
  }
190044
190062
  async function stopProcess3(proc) {
190045
- return new Promise((resolve10) => {
190063
+ return new Promise((resolve9) => {
190046
190064
  if (proc.killed || proc.exitCode !== null) {
190047
- resolve10();
190065
+ resolve9();
190048
190066
  return;
190049
190067
  }
190050
190068
  proc.once("exit", () => {
190051
190069
  clearTimeout(forceKillTimeout);
190052
- resolve10();
190070
+ resolve9();
190053
190071
  });
190054
190072
  proc.kill("SIGTERM");
190055
190073
  const forceKillTimeout = setTimeout(() => {
@@ -190060,7 +190078,7 @@ async function stopProcess3(proc) {
190060
190078
  });
190061
190079
  }
190062
190080
  function sleep4(ms) {
190063
- return new Promise((resolve10) => setTimeout(resolve10, ms));
190081
+ return new Promise((resolve9) => setTimeout(resolve9, ms));
190064
190082
  }
190065
190083
 
190066
190084
  // src/lib/dev/sync-detector.ts
@@ -190425,38 +190443,38 @@ async function waitForTcpPort4(host, port, timeoutMs = 3e4) {
190425
190443
  if (isOpen) {
190426
190444
  return;
190427
190445
  }
190428
- await new Promise((resolve10) => setTimeout(resolve10, 100));
190446
+ await new Promise((resolve9) => setTimeout(resolve9, 100));
190429
190447
  }
190430
190448
  throw new Error(`Temporal port ${port} did not become available within timeout`);
190431
190449
  }
190432
190450
  function checkTcpPort4(host, port) {
190433
- return new Promise((resolve10) => {
190451
+ return new Promise((resolve9) => {
190434
190452
  const socket = new net4.Socket();
190435
190453
  socket.setTimeout(1e3);
190436
190454
  socket.on("connect", () => {
190437
190455
  socket.destroy();
190438
- resolve10(true);
190456
+ resolve9(true);
190439
190457
  });
190440
190458
  socket.on("timeout", () => {
190441
190459
  socket.destroy();
190442
- resolve10(false);
190460
+ resolve9(false);
190443
190461
  });
190444
190462
  socket.on("error", () => {
190445
190463
  socket.destroy();
190446
- resolve10(false);
190464
+ resolve9(false);
190447
190465
  });
190448
190466
  socket.connect(port, host);
190449
190467
  });
190450
190468
  }
190451
190469
  async function stopProcess4(proc) {
190452
- return new Promise((resolve10) => {
190470
+ return new Promise((resolve9) => {
190453
190471
  if (proc.killed || proc.exitCode !== null) {
190454
- resolve10();
190472
+ resolve9();
190455
190473
  return;
190456
190474
  }
190457
190475
  proc.once("exit", () => {
190458
190476
  clearTimeout(forceKillTimeout);
190459
- resolve10();
190477
+ resolve9();
190460
190478
  });
190461
190479
  proc.kill("SIGTERM");
190462
190480
  const forceKillTimeout = setTimeout(() => {
@@ -190931,7 +190949,7 @@ var ProxyRegistryManager = class {
190931
190949
  * This catches cases where the owner process is alive but the proxy has crashed.
190932
190950
  */
190933
190951
  isProxyListening(port, timeoutMs = 1e3) {
190934
- return new Promise((resolve10) => {
190952
+ return new Promise((resolve9) => {
190935
190953
  const socket = new net6.Socket();
190936
190954
  let resolved = false;
190937
190955
  const cleanup = () => {
@@ -190943,15 +190961,15 @@ var ProxyRegistryManager = class {
190943
190961
  socket.setTimeout(timeoutMs);
190944
190962
  socket.on("connect", () => {
190945
190963
  cleanup();
190946
- resolve10(true);
190964
+ resolve9(true);
190947
190965
  });
190948
190966
  socket.on("timeout", () => {
190949
190967
  cleanup();
190950
- resolve10(false);
190968
+ resolve9(false);
190951
190969
  });
190952
190970
  socket.on("error", () => {
190953
190971
  cleanup();
190954
- resolve10(false);
190972
+ resolve9(false);
190955
190973
  });
190956
190974
  socket.connect(port, "127.0.0.1");
190957
190975
  });
@@ -191002,7 +191020,7 @@ var ProxyRegistryManager = class {
191002
191020
  }
191003
191021
  continue;
191004
191022
  }
191005
- await new Promise((resolve10) => setTimeout(resolve10, 100));
191023
+ await new Promise((resolve9) => setTimeout(resolve9, 100));
191006
191024
  } else {
191007
191025
  throw e;
191008
191026
  }
@@ -192018,7 +192036,7 @@ Add them to the config block in specific.local`);
192018
192036
  } else {
192019
192037
  const keySuffix = instanceKey === "default" ? "" : `.${instanceKey}`;
192020
192038
  for (const svc of exposedServices) {
192021
- publicUrls.set(svc.name, `${svc.name}${keySuffix}.local.spcf.app`);
192039
+ publicUrls.set(svc.name, `${svc.name}${keySuffix}.spcf.localhost`);
192022
192040
  }
192023
192041
  }
192024
192042
  const services2 = [];
@@ -192251,7 +192269,7 @@ Add them to the config block in specific.local`);
192251
192269
  dnsServerRef.current = dnsServer;
192252
192270
  const currentServices = await proxyRegistry.getAllServices();
192253
192271
  const registeredKeys = [...new Set(currentServices.map((s) => s.key))];
192254
- const certificate = generateCertificate("local.spcf.app", registeredKeys);
192272
+ const certificate = generateCertificate("spcf.localhost", registeredKeys);
192255
192273
  const proxy2 = await startHttpProxy(
192256
192274
  exposedServices,
192257
192275
  certificate,
@@ -192272,7 +192290,7 @@ Add them to the config block in specific.local`);
192272
192290
  knownKeys.add(key);
192273
192291
  }
192274
192292
  const allKeys = [...knownKeys];
192275
- const newCertificate = generateCertificate("local.spcf.app", allKeys);
192293
+ const newCertificate = generateCertificate("spcf.localhost", allKeys);
192276
192294
  proxy2.updateCertificate(newCertificate);
192277
192295
  }
192278
192296
  });
@@ -192324,7 +192342,7 @@ Add them to the config block in specific.local`);
192324
192342
  const electionServices = await proxyRegistry.getAllServices();
192325
192343
  const electionKeyRegistrations = await proxyRegistry.getAllKeyRegistrations();
192326
192344
  const electionKeyNames = Object.keys(electionKeyRegistrations);
192327
- const certificate = generateCertificate("local.spcf.app", electionKeyNames);
192345
+ const certificate = generateCertificate("spcf.localhost", electionKeyNames);
192328
192346
  const proxy2 = await startHttpProxy(
192329
192347
  exposedServices,
192330
192348
  certificate,
@@ -192400,7 +192418,7 @@ Add them to the config block in specific.local`);
192400
192418
  }, [reloadTrigger, readyToStart, instanceKey]);
192401
192419
  if (state.status === "installing-ca") {
192402
192420
  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."));
192421
+ return /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "column" }, /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Configure development environment"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "We need to do a one-time setup for all your Specific projects."), /* @__PURE__ */ React6.createElement(Text6, null, "This is so we can run your apps locally with secure URLs:"), /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "green" }, "https://your-app.spcf.localhost"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "You will be prompted to authorize with your password."), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, null, "Press ", /* @__PURE__ */ React6.createElement(Text6, { bold: true, color: "cyan" }, "Enter"), " to authorize."));
192404
192422
  }
192405
192423
  if (state.caInstallPhase === "installing") {
192406
192424
  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"));
@@ -192525,7 +192543,7 @@ Add them to the config block in specific.local`);
192525
192543
  ...!tunnelEnabled ? [
192526
192544
  {
192527
192545
  key: "admin",
192528
- content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Admin:"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "https://", instanceKey === "default" ? "" : `${instanceKey}.`, "local.spcf.app"))
192546
+ content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Admin:"), /* @__PURE__ */ React6.createElement(Text6, null, " "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "https://", instanceKey === "default" ? "" : `${instanceKey}.`, "spcf.localhost"))
192529
192547
  },
192530
192548
  { key: "admin-space", content: /* @__PURE__ */ React6.createElement(Text6, null, " ") }
192531
192549
  ] : [],
@@ -192563,7 +192581,7 @@ Add them to the config block in specific.local`);
192563
192581
  const proxyName = endpoint.name === "default" ? svc.name : `${svc.name}-${endpoint.name}`;
192564
192582
  return {
192565
192583
  key: `svc-${svc.name}-${endpoint.name}`,
192566
- content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, displayName), port ? endpoint.public ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, null, " \u2192 "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "https://", proxyName, instanceKey === "default" ? "" : `.${instanceKey}`, ".local.spcf.app"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", port, ")")) : /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", port, ")") : null)
192584
+ content: /* @__PURE__ */ React6.createElement(Text6, null, /* @__PURE__ */ React6.createElement(Text6, { color: "green" }, " \u25CF "), /* @__PURE__ */ React6.createElement(Text6, null, displayName), port ? endpoint.public ? /* @__PURE__ */ React6.createElement(React6.Fragment, null, /* @__PURE__ */ React6.createElement(Text6, null, " \u2192 "), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "https://", proxyName, instanceKey === "default" ? "" : `.${instanceKey}`, ".spcf.localhost"), /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", port, ")")) : /* @__PURE__ */ React6.createElement(Text6, { dimColor: true }, " (localhost:", port, ")") : null)
192567
192585
  };
192568
192586
  });
192569
192587
  }),
@@ -192679,186 +192697,6 @@ import Spinner5 from "ink-spinner";
192679
192697
  import * as fs26 from "fs";
192680
192698
  import * as path23 from "path";
192681
192699
 
192682
- // src/lib/deploy/build-tester.ts
192683
- import { spawn as spawn6 } from "child_process";
192684
- import { existsSync as existsSync22 } from "fs";
192685
- import { join as join23, resolve as resolve7 } from "path";
192686
- function getDependencyInstallCommand(build, projectDir) {
192687
- if (!build.base) return null;
192688
- switch (build.base) {
192689
- case "node":
192690
- if (existsSync22(join23(projectDir, "pnpm-lock.yaml"))) {
192691
- return "pnpm install --frozen-lockfile";
192692
- } else if (existsSync22(join23(projectDir, "yarn.lock"))) {
192693
- return "yarn install --frozen-lockfile";
192694
- } else if (existsSync22(join23(projectDir, "package-lock.json"))) {
192695
- return "npm ci";
192696
- } else if (existsSync22(join23(projectDir, "package.json"))) {
192697
- return "npm install";
192698
- }
192699
- return null;
192700
- case "python":
192701
- if (existsSync22(join23(projectDir, "poetry.lock"))) {
192702
- return "poetry install --no-interaction";
192703
- } else if (existsSync22(join23(projectDir, "Pipfile.lock"))) {
192704
- return "pipenv install --deploy";
192705
- } else if (existsSync22(join23(projectDir, "Pipfile"))) {
192706
- return "pipenv install";
192707
- } else if (existsSync22(join23(projectDir, "pyproject.toml"))) {
192708
- return "pip install .";
192709
- } else if (existsSync22(join23(projectDir, "requirements.txt"))) {
192710
- return "pip install -r requirements.txt";
192711
- }
192712
- return null;
192713
- case "go":
192714
- return "go mod download";
192715
- case "rust":
192716
- case "java":
192717
- return null;
192718
- default:
192719
- return null;
192720
- }
192721
- }
192722
- function runCommand2(command, projectDir, buildName) {
192723
- return new Promise((resolve10) => {
192724
- const stdout = [];
192725
- const stderr = [];
192726
- writeLog("build-test", `[${buildName}] Running: ${command}`);
192727
- const child = spawn6(command, {
192728
- shell: true,
192729
- cwd: projectDir,
192730
- env: { ...process.env },
192731
- stdio: ["ignore", "pipe", "pipe"]
192732
- });
192733
- child.stdout?.on("data", (data) => {
192734
- const text = data.toString();
192735
- stdout.push(text);
192736
- for (const line of text.split("\n")) {
192737
- if (line.trim()) {
192738
- writeLog(`build-test:${buildName}`, line);
192739
- }
192740
- }
192741
- });
192742
- child.stderr?.on("data", (data) => {
192743
- const text = data.toString();
192744
- stderr.push(text);
192745
- for (const line of text.split("\n")) {
192746
- if (line.trim()) {
192747
- writeLog(`build-test:${buildName}`, line);
192748
- }
192749
- }
192750
- });
192751
- child.on("error", (err) => {
192752
- writeLog("build-test:error", `[${buildName}] Failed to start: ${err.message}`);
192753
- resolve10({
192754
- success: false,
192755
- output: `Failed to start command: ${err.message}`
192756
- });
192757
- });
192758
- child.on("exit", (code) => {
192759
- const output = [...stdout, ...stderr].join("");
192760
- if (code === 0) {
192761
- writeLog("build-test", `[${buildName}] Command succeeded (exit code 0)`);
192762
- resolve10({ success: true, output });
192763
- } else {
192764
- writeLog("build-test:error", `[${buildName}] Command failed with exit code ${code}`);
192765
- resolve10({
192766
- success: false,
192767
- output: output || `Exit code: ${code}`
192768
- });
192769
- }
192770
- });
192771
- });
192772
- }
192773
- async function testBuild(build, projectDir) {
192774
- const startTime = Date.now();
192775
- if (build.dockerfile) {
192776
- writeLog("build-test", `Skipping test for build "${build.name}" (custom Dockerfile)`);
192777
- return {
192778
- buildName: build.name,
192779
- success: true,
192780
- output: "Skipped: custom Dockerfile build",
192781
- durationMs: Date.now() - startTime
192782
- };
192783
- }
192784
- const outputs = [];
192785
- const workDir = build.root ? resolve7(projectDir, build.root) : projectDir;
192786
- writeLog("build-test", `Starting test for build "${build.name}" (base: ${build.base}, workDir: ${workDir})`);
192787
- const depsCommand = getDependencyInstallCommand(build, workDir);
192788
- if (depsCommand) {
192789
- writeLog("build-test", `[${build.name}] Installing dependencies...`);
192790
- const depsResult = await runCommand2(depsCommand, workDir, build.name);
192791
- outputs.push(`[${depsCommand}]
192792
- ${depsResult.output}`);
192793
- if (!depsResult.success) {
192794
- const duration2 = Date.now() - startTime;
192795
- writeLog("build-test:error", `[${build.name}] Dependency installation failed after ${duration2}ms`);
192796
- return {
192797
- buildName: build.name,
192798
- success: false,
192799
- output: outputs.join("\n\n"),
192800
- durationMs: duration2
192801
- };
192802
- }
192803
- writeLog("build-test", `[${build.name}] Dependencies installed successfully`);
192804
- } else {
192805
- writeLog("build-test", `[${build.name}] No dependency installation needed for base "${build.base}"`);
192806
- }
192807
- if (build.command) {
192808
- writeLog("build-test", `[${build.name}] Running build command...`);
192809
- const buildResult = await runCommand2(build.command, workDir, build.name);
192810
- outputs.push(`[${build.command}]
192811
- ${buildResult.output}`);
192812
- if (!buildResult.success) {
192813
- const duration2 = Date.now() - startTime;
192814
- writeLog("build-test:error", `[${build.name}] Build command failed after ${duration2}ms`);
192815
- return {
192816
- buildName: build.name,
192817
- success: false,
192818
- output: outputs.join("\n\n"),
192819
- durationMs: duration2
192820
- };
192821
- }
192822
- writeLog("build-test", `[${build.name}] Build command completed successfully`);
192823
- } else {
192824
- writeLog("build-test", `[${build.name}] No build command defined, skipping`);
192825
- }
192826
- const duration = Date.now() - startTime;
192827
- writeLog("build-test", `[${build.name}] Build test passed in ${duration}ms`);
192828
- return {
192829
- buildName: build.name,
192830
- success: true,
192831
- output: outputs.join("\n\n"),
192832
- durationMs: duration
192833
- };
192834
- }
192835
- async function testAllBuilds(builds, projectDir) {
192836
- if (builds.length === 0) {
192837
- writeLog("build-test", "No builds to test");
192838
- return { results: [], allPassed: true };
192839
- }
192840
- writeLog("build-test", `Testing ${builds.length} build(s) in parallel: ${builds.map((b) => b.name).join(", ")}`);
192841
- writeLog("build-test", `Project directory: ${projectDir}`);
192842
- const results = await Promise.all(
192843
- builds.map((build) => testBuild(build, projectDir))
192844
- );
192845
- const passed = results.filter((r) => r.success).length;
192846
- const failed = results.filter((r) => !r.success).length;
192847
- const totalDuration = results.reduce((sum, r) => sum + r.durationMs, 0);
192848
- if (failed > 0) {
192849
- writeLog("build-test:error", `Build tests completed: ${passed} passed, ${failed} failed`);
192850
- for (const result of results.filter((r) => !r.success)) {
192851
- writeLog("build-test:error", `Failed build: ${result.buildName}`);
192852
- }
192853
- } else {
192854
- writeLog("build-test", `All ${passed} build(s) passed (total: ${totalDuration}ms)`);
192855
- }
192856
- return {
192857
- results,
192858
- allPassed: results.every((r) => r.success)
192859
- };
192860
- }
192861
-
192862
192700
  // src/lib/tarball/create.ts
192863
192701
  import { execSync as execSync4 } from "child_process";
192864
192702
  import * as fs25 from "fs";
@@ -192999,7 +192837,6 @@ function PhaseIndicator({
192999
192837
  showSpinner = true
193000
192838
  }) {
193001
192839
  const phases = [
193002
- "testing-builds",
193003
192840
  "creating-tarball",
193004
192841
  "creating-deployment",
193005
192842
  "uploading",
@@ -193111,7 +192948,7 @@ function formatErrorCode(code) {
193111
192948
  function StructuredError({ error }) {
193112
192949
  return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Text7, { color: "red", bold: true }, formatErrorCode(error.code), ": ", error.message), error.resource && /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Resource: ", error.resource), error.output && /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", marginTop: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Output:"), /* @__PURE__ */ React7.createElement(Text7, null, error.output)));
193113
192950
  }
193114
- function DeployUI({ environment, config, skipBuildTest }) {
192951
+ function DeployUI({ environment, config }) {
193115
192952
  const { exit } = useApp3();
193116
192953
  const [state, setState] = useState6({ phase: "checking-auth" });
193117
192954
  const clientRef = React7.useRef(null);
@@ -193122,7 +192959,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
193122
192959
  const projectId = readProjectId(projectDir);
193123
192960
  if (isLoggedIn()) {
193124
192961
  setState({
193125
- phase: "testing-builds",
192962
+ phase: "creating-tarball",
193126
192963
  projectId
193127
192964
  });
193128
192965
  } else {
@@ -193179,7 +193016,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
193179
193016
  const successResponse = response;
193180
193017
  await saveCredentialsFromToken(successResponse);
193181
193018
  setState(
193182
- (s) => s.projectId ? { phase: "testing-builds", projectId: s.projectId } : { phase: "loading-projects" }
193019
+ (s) => s.projectId ? { phase: "creating-tarball", projectId: s.projectId } : { phase: "loading-projects" }
193183
193020
  );
193184
193021
  }
193185
193022
  };
@@ -193230,7 +193067,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
193230
193067
  } else {
193231
193068
  writeProjectId(project.id);
193232
193069
  setState({
193233
- phase: "testing-builds",
193070
+ phase: "creating-tarball",
193234
193071
  projectId: project.id
193235
193072
  });
193236
193073
  }
@@ -193253,7 +193090,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
193253
193090
  if (cancelled) return;
193254
193091
  writeProjectId(project.id);
193255
193092
  setState({
193256
- phase: "testing-builds",
193093
+ phase: "creating-tarball",
193257
193094
  projectId: project.id
193258
193095
  });
193259
193096
  } catch (err) {
@@ -193414,43 +193251,11 @@ function DeployUI({ environment, config, skipBuildTest }) {
193414
193251
  })();
193415
193252
  }, [state]);
193416
193253
  useEffect4(() => {
193417
- if (state.phase !== "testing-builds" || !state.projectId) return;
193254
+ if (state.phase !== "creating-tarball" || !state.projectId) return;
193418
193255
  let cancelled = false;
193419
- async function runBuildTestsAndDeploy() {
193256
+ async function runDeploy() {
193420
193257
  const projectDir = process.cwd();
193421
193258
  const builds = config.builds || [];
193422
- if (!skipBuildTest && builds.length > 0) {
193423
- writeLog("deploy", `Testing ${builds.length} build(s) locally`);
193424
- try {
193425
- const results = await testAllBuilds(builds, projectDir);
193426
- if (cancelled) return;
193427
- if (!results.allPassed) {
193428
- const failures = results.results.filter((r) => !r.success);
193429
- const errorMsg = failures.map((f) => `Build "${f.buildName}" failed:
193430
- ${f.output}`).join("\n\n");
193431
- writeLog("deploy:error", errorMsg);
193432
- setState({
193433
- phase: "error",
193434
- error: `Build test failed:
193435
- ${errorMsg}`
193436
- });
193437
- return;
193438
- }
193439
- writeLog("deploy", "All builds passed local testing");
193440
- } catch (err) {
193441
- if (cancelled) return;
193442
- const errorMsg = `Build test failed: ${err instanceof Error ? err.message : String(err)}`;
193443
- writeLog("deploy:error", errorMsg);
193444
- setState({
193445
- phase: "error",
193446
- error: errorMsg
193447
- });
193448
- return;
193449
- }
193450
- } else {
193451
- writeLog("deploy", skipBuildTest ? "Skipping build tests (--skip-build-test)" : "No builds to test");
193452
- }
193453
- setState((s) => ({ ...s, phase: "creating-tarball" }));
193454
193259
  writeLog("deploy", `Starting deployment to "${environment}"`);
193455
193260
  writeLog("deploy", `Project directory: ${projectDir}`);
193456
193261
  const client2 = new ApiClient();
@@ -193516,11 +193321,11 @@ ${errorMsg}`
193516
193321
  writeLog("deploy", "Deployment in pending state, waiting for builds to complete");
193517
193322
  setState((s) => ({ ...s, phase: "pending", deployment: deployment2 }));
193518
193323
  }
193519
- runBuildTestsAndDeploy();
193324
+ runDeploy();
193520
193325
  return () => {
193521
193326
  cancelled = true;
193522
193327
  };
193523
- }, [state.projectId, environment, config.builds, skipBuildTest]);
193328
+ }, [state.projectId, environment, config.builds]);
193524
193329
  useEffect4(() => {
193525
193330
  if (state.phase !== "pending" || !state.deployment) return;
193526
193331
  let pollInterval;
@@ -193714,7 +193519,7 @@ ${errorMsg}`
193714
193519
  };
193715
193520
  }, [state.phase, state.deployment?.id]);
193716
193521
  useEffect4(() => {
193717
- if (state.phase === "testing-builds") {
193522
+ if (state.phase === "creating-tarball") {
193718
193523
  trackEvent("deploy_started", { environment });
193719
193524
  }
193720
193525
  }, [state.phase, environment]);
@@ -193809,13 +193614,6 @@ ${errorMsg}`
193809
193614
  return "Preparing";
193810
193615
  };
193811
193616
  return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, null, /* @__PURE__ */ React7.createElement(Text7, { bold: true, color: "cyan" }, "Deploying to ", environment), deployment && /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, " (", deployment.id, ")")), /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(
193812
- PhaseIndicator,
193813
- {
193814
- phase: "testing-builds",
193815
- currentPhase: displayPhase,
193816
- label: "Testing builds locally"
193817
- }
193818
- ), /* @__PURE__ */ React7.createElement(
193819
193617
  PhaseIndicator,
193820
193618
  {
193821
193619
  phase: "creating-tarball",
@@ -193869,7 +193667,7 @@ ${errorMsg}`
193869
193667
  }
193870
193668
  ), 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))))));
193871
193669
  }
193872
- async function deployCommand(environment, options2) {
193670
+ async function deployCommand(environment) {
193873
193671
  const configPath = path23.join(process.cwd(), "specific.hcl");
193874
193672
  if (!fs26.existsSync(configPath)) {
193875
193673
  console.error("Error: No specific.hcl found in current directory");
@@ -193884,21 +193682,19 @@ async function deployCommand(environment, options2) {
193884
193682
  process.exit(1);
193885
193683
  }
193886
193684
  const env2 = environment || "prod";
193887
- const skipBuildTest = options2?.skipBuildTest ?? false;
193888
193685
  render5(
193889
193686
  /* @__PURE__ */ React7.createElement(
193890
193687
  DeployUI,
193891
193688
  {
193892
193689
  environment: env2,
193893
- config,
193894
- skipBuildTest
193690
+ config
193895
193691
  }
193896
193692
  )
193897
193693
  );
193898
193694
  }
193899
193695
 
193900
193696
  // src/commands/exec.tsx
193901
- import { spawn as spawn7 } from "child_process";
193697
+ import { spawn as spawn6 } from "child_process";
193902
193698
  import * as fs27 from "fs";
193903
193699
  import * as path24 from "path";
193904
193700
  async function execCommand(serviceName, command, instanceKey = "default") {
@@ -194059,7 +193855,7 @@ async function execCommand(serviceName, command, instanceKey = "default") {
194059
193855
  effectiveCwd = path24.resolve(process.cwd(), build.root);
194060
193856
  }
194061
193857
  }
194062
- child = spawn7(command[0], command.slice(1), {
193858
+ child = spawn6(command[0], command.slice(1), {
194063
193859
  cwd: effectiveCwd,
194064
193860
  env: {
194065
193861
  ...process.env,
@@ -194083,7 +193879,7 @@ async function execCommand(serviceName, command, instanceKey = "default") {
194083
193879
  }
194084
193880
 
194085
193881
  // src/commands/psql.tsx
194086
- import { spawn as spawn8 } from "child_process";
193882
+ import { spawn as spawn7 } from "child_process";
194087
193883
  import * as fs28 from "fs";
194088
193884
  import * as path25 from "path";
194089
193885
  async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []) {
@@ -194206,7 +194002,7 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
194206
194002
  };
194207
194003
  process.on("SIGINT", () => handleSignal("SIGINT"));
194208
194004
  process.on("SIGTERM", () => handleSignal("SIGTERM"));
194209
- child = spawn8(psqlPath, ["-h", dbState.host, "-p", String(dbState.port), "-U", dbState.user, "-d", dbState.dbName, ...extraArgs], {
194005
+ child = spawn7(psqlPath, ["-h", dbState.host, "-p", String(dbState.port), "-U", dbState.user, "-d", dbState.dbName, ...extraArgs], {
194210
194006
  cwd: process.cwd(),
194211
194007
  env: {
194212
194008
  ...process.env,
@@ -194231,7 +194027,7 @@ async function psqlCommand(databaseName, instanceKey = "default", extraArgs = []
194231
194027
  }
194232
194028
 
194233
194029
  // src/commands/reshape.tsx
194234
- import { spawn as spawn9 } from "child_process";
194030
+ import { spawn as spawn8 } from "child_process";
194235
194031
  import * as fs29 from "fs";
194236
194032
  import * as path26 from "path";
194237
194033
  var VALID_ACTIONS = ["start", "complete", "status", "abort", "check"];
@@ -194409,7 +194205,7 @@ async function reshapeCommand(action, databaseName, instanceKey = "default") {
194409
194205
  };
194410
194206
  process.on("SIGINT", () => handleSignal("SIGINT"));
194411
194207
  process.on("SIGTERM", () => handleSignal("SIGTERM"));
194412
- child = spawn9(reshapePath, reshapeArgs, {
194208
+ child = spawn8(reshapePath, reshapeArgs, {
194413
194209
  cwd: process.cwd(),
194414
194210
  env: {
194415
194211
  ...process.env,
@@ -194686,7 +194482,7 @@ function compareVersions(a, b) {
194686
194482
  return 0;
194687
194483
  }
194688
194484
  async function checkForUpdate() {
194689
- const currentVersion = "0.1.72";
194485
+ const currentVersion = "0.1.74";
194690
194486
  const response = await fetch(`${BINARIES_BASE_URL}/latest?t=${Date.now()}`);
194691
194487
  if (!response.ok) {
194692
194488
  throw new Error(`Failed to check for updates: HTTP ${response.status}`);
@@ -194736,7 +194532,7 @@ async function performUpdate(version, onProgress) {
194736
194532
  }
194737
194533
 
194738
194534
  // src/lib/background-update.ts
194739
- import { spawn as spawn10 } from "child_process";
194535
+ import { spawn as spawn9 } from "child_process";
194740
194536
  import * as fs32 from "fs";
194741
194537
  import * as path29 from "path";
194742
194538
  import * as os10 from "os";
@@ -194765,7 +194561,7 @@ function maybeStartBackgroundUpdate() {
194765
194561
  if (process.env.SPECIFIC_BACKGROUND_UPDATE === "1") return;
194766
194562
  if (isRateLimited()) return;
194767
194563
  if (!isBinaryWritable()) return;
194768
- const child = spawn10(process.execPath, [], {
194564
+ const child = spawn9(process.execPath, [], {
194769
194565
  detached: true,
194770
194566
  stdio: "ignore",
194771
194567
  env: {
@@ -194885,7 +194681,7 @@ function updateCommand() {
194885
194681
  var program = new Command();
194886
194682
  var env = "production";
194887
194683
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
194888
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.72").enablePositionalOptions();
194684
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.74").enablePositionalOptions();
194889
194685
  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));
194890
194686
  program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
194891
194687
  program.command("check").description("Validate specific.hcl configuration").action(checkCommand);
@@ -194893,8 +194689,8 @@ program.command("dev").description("Start local development environment").option
194893
194689
  const key = options2.key ?? getDefaultKey();
194894
194690
  devCommand(key, options2.tunnel ?? false);
194895
194691
  });
194896
- program.command("deploy [environment]").description("Deploy to Specific infrastructure").option("--skip-build-test", "Skip local build testing before deploy").action((environment, options2) => {
194897
- deployCommand(environment, options2);
194692
+ program.command("deploy [environment]").description("Deploy to Specific infrastructure").action((environment) => {
194693
+ deployCommand(environment);
194898
194694
  });
194899
194695
  program.command("exec <service> [args...]").description("Run a one-off command with service environment").option("-k, --key <key>", "Dev environment namespace (auto-detected from git worktree if not specified)").passThroughOptions().action(async (service, args, options2) => {
194900
194696
  const filteredArgs = args[0] === "--" ? args.slice(1) : args;