@specific.dev/cli 0.1.44 → 0.1.46

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 (56) hide show
  1. package/dist/admin/404/index.html +1 -1
  2. package/dist/admin/404.html +1 -1
  3. package/dist/admin/__next.__PAGE__.txt +2 -2
  4. package/dist/admin/__next._full.txt +24 -17
  5. package/dist/admin/__next._head.txt +1 -1
  6. package/dist/admin/__next._index.txt +10 -4
  7. package/dist/admin/__next._tree.txt +3 -2
  8. package/dist/admin/_next/static/chunks/63473a6cb811ed42.js +5 -0
  9. package/dist/admin/_next/static/chunks/64efcb432fae8c98.js +1 -0
  10. package/dist/admin/_next/static/chunks/65ce370ab86b6df6.js +1 -0
  11. package/dist/admin/_next/static/chunks/6877369fbd84f127.js +1 -0
  12. package/dist/admin/_next/static/chunks/951210b423dc9315.css +4 -0
  13. package/dist/admin/_next/static/chunks/ce9a5f692b87aaa9.js +5 -0
  14. package/dist/admin/_next/static/chunks/d2b1f8ba26497c0b.js +1 -0
  15. package/dist/admin/_next/static/chunks/{a5c8191596f07db5.js → e13659c7ad8234ce.js} +2 -2
  16. package/dist/admin/_next/static/chunks/turbopack-ebee0930f5a58b67.js +4 -0
  17. package/dist/admin/_next/static/media/1bffadaabf893a1e-s.7cd81963.woff2 +0 -0
  18. package/dist/admin/_next/static/media/2bbe8d2671613f1f-s.76dcb0b2.woff2 +0 -0
  19. package/dist/admin/_next/static/media/2c55a0e60120577a-s.2a48534a.woff2 +0 -0
  20. package/dist/admin/_next/static/media/5476f68d60460930-s.c995e352.woff2 +0 -0
  21. package/dist/admin/_next/static/media/83afe278b6a6bb3c-s.p.3a6ba036.woff2 +0 -0
  22. package/dist/admin/_next/static/media/9c72aa0f40e4eef8-s.18a48cbc.woff2 +0 -0
  23. package/dist/admin/_next/static/media/ad66f9afd8947f86-s.7a40eb73.woff2 +0 -0
  24. package/dist/admin/_next/static/media/icon.456b8582.svg +12 -0
  25. package/dist/admin/_not-found/__next._full.txt +19 -13
  26. package/dist/admin/_not-found/__next._head.txt +1 -1
  27. package/dist/admin/_not-found/__next._index.txt +10 -4
  28. package/dist/admin/_not-found/__next._not-found.__PAGE__.txt +1 -1
  29. package/dist/admin/_not-found/__next._not-found.txt +1 -1
  30. package/dist/admin/_not-found/__next._tree.txt +2 -2
  31. package/dist/admin/_not-found/index.html +1 -1
  32. package/dist/admin/_not-found/index.txt +19 -13
  33. package/dist/admin/databases/__next._full.txt +24 -17
  34. package/dist/admin/databases/__next._head.txt +1 -1
  35. package/dist/admin/databases/__next._index.txt +10 -4
  36. package/dist/admin/databases/__next._tree.txt +3 -2
  37. package/dist/admin/databases/__next.databases.__PAGE__.txt +2 -2
  38. package/dist/admin/databases/__next.databases.txt +1 -1
  39. package/dist/admin/databases/index.html +1 -1
  40. package/dist/admin/databases/index.txt +24 -17
  41. package/dist/admin/icon.svg +12 -0
  42. package/dist/admin/index.html +1 -1
  43. package/dist/admin/index.txt +24 -17
  44. package/dist/cli.js +305 -160
  45. package/package.json +1 -1
  46. package/dist/admin/_next/static/chunks/522cc1cbb935d4c6.js +0 -1
  47. package/dist/admin/_next/static/chunks/62190944d690fc4e.js +0 -4
  48. package/dist/admin/_next/static/chunks/938d410f2031f3b1.css +0 -3
  49. package/dist/admin/_next/static/chunks/979e895ce202c4a3.js +0 -1
  50. package/dist/admin/_next/static/chunks/99f58b3b47071cc8.js +0 -5
  51. package/dist/admin/_next/static/chunks/a4ff1b18f2f45e23.js +0 -2
  52. package/dist/admin/_next/static/chunks/bf65cbe8dc67cf90.js +0 -5
  53. package/dist/admin/_next/static/chunks/turbopack-9e3df33047c5ecb2.js +0 -4
  54. /package/dist/admin/_next/static/{pbjYnqTudS-YVLwgwOgBz → uXSe8Dmoqn0jmhvY6Iln0}/_buildManifest.js +0 -0
  55. /package/dist/admin/_next/static/{pbjYnqTudS-YVLwgwOgBz → uXSe8Dmoqn0jmhvY6Iln0}/_clientMiddlewareManifest.json +0 -0
  56. /package/dist/admin/_next/static/{pbjYnqTudS-YVLwgwOgBz → uXSe8Dmoqn0jmhvY6Iln0}/_ssgManifest.js +0 -0
package/dist/cli.js CHANGED
@@ -183398,6 +183398,36 @@ var ApiClient = class {
183398
183398
  }
183399
183399
  return response.json();
183400
183400
  }
183401
+ async startDeployment(deploymentId) {
183402
+ const url = `${this.baseUrl}/deployments/${deploymentId}/start`;
183403
+ writeLog("api", `POST ${url}`);
183404
+ const response = await fetch(url, {
183405
+ method: "POST",
183406
+ headers: this.authHeaders()
183407
+ });
183408
+ writeLog("api", `Response: ${response.status} ${response.statusText}`);
183409
+ if (!response.ok) {
183410
+ let errorBody;
183411
+ try {
183412
+ const error = await response.json();
183413
+ errorBody = JSON.stringify(error);
183414
+ writeLog("api:error", `API error: ${error.error} (${error.code})`);
183415
+ writeLog("api:error", `Request was: POST ${url}`);
183416
+ writeLog("api:error", `Response body: ${errorBody}`);
183417
+ throw new Error(
183418
+ `Failed to start deployment: ${error.error} (${error.code})`
183419
+ );
183420
+ } catch (e) {
183421
+ if (e instanceof Error && e.message.startsWith("Failed to start")) {
183422
+ throw e;
183423
+ }
183424
+ errorBody = await response.text();
183425
+ writeLog("api:error", `Failed to parse error response: ${errorBody}`);
183426
+ throw new Error(`Failed to start deployment: ${response.statusText}`);
183427
+ }
183428
+ }
183429
+ return response.json();
183430
+ }
183401
183431
  async submitSecrets(deploymentId, secrets) {
183402
183432
  const secretKeys = Object.keys(secrets);
183403
183433
  const url = `${this.baseUrl}/deployments/${deploymentId}/secrets`;
@@ -183539,7 +183569,7 @@ var ApiClient = class {
183539
183569
  writeLog("api", `GET ${url}`);
183540
183570
  const response = await fetch(url, {
183541
183571
  headers: this.authHeaders(),
183542
- signal
183572
+ ...signal ? { signal } : {}
183543
183573
  });
183544
183574
  writeLog("api", `Response: ${response.status} ${response.statusText}`);
183545
183575
  if (!response.ok) {
@@ -183843,7 +183873,7 @@ function trackEvent(event, properties) {
183843
183873
  event,
183844
183874
  properties: {
183845
183875
  ...properties,
183846
- cli_version: "0.1.44",
183876
+ cli_version: "0.1.46",
183847
183877
  platform: process.platform,
183848
183878
  node_version: process.version,
183849
183879
  project_id: getProjectId(),
@@ -186546,8 +186576,9 @@ var StablePortAllocator = class {
186546
186576
  return port;
186547
186577
  }
186548
186578
  allocate(key) {
186549
- if (key in this.savedPorts) {
186550
- return this.savedPorts[key];
186579
+ const savedPort = this.savedPorts[key];
186580
+ if (savedPort !== void 0) {
186581
+ return savedPort;
186551
186582
  }
186552
186583
  const port = this.allocateRandom();
186553
186584
  this.savedPorts[key] = port;
@@ -186614,10 +186645,11 @@ function getPlatformInfo() {
186614
186645
  `Unsupported platform: ${platform5}. Only macOS and Linux are supported.`
186615
186646
  );
186616
186647
  }
186648
+ const archStr = arch3;
186617
186649
  let mappedArch;
186618
- if (arch3 === "x64" || arch3 === "x86_64") {
186650
+ if (archStr === "x64" || archStr === "x86_64") {
186619
186651
  mappedArch = "x64";
186620
- } else if (arch3 === "arm64" || arch3 === "aarch64") {
186652
+ } else if (archStr === "arm64" || archStr === "aarch64") {
186621
186653
  mappedArch = "arm64";
186622
186654
  } else {
186623
186655
  throw new Error(
@@ -187318,7 +187350,7 @@ function resolveEnvValue(value, resources, secrets, configs, servicePort, servic
187318
187350
  }
187319
187351
  return pg.syncSecret;
187320
187352
  default:
187321
- throw new Error(`Unknown postgres attribute: ${value.attribute}`);
187353
+ throw new Error(`Unknown postgres attribute: ${String(value.attribute)}`);
187322
187354
  }
187323
187355
  }
187324
187356
  case "redis": {
@@ -187336,7 +187368,7 @@ function resolveEnvValue(value, resources, secrets, configs, servicePort, servic
187336
187368
  case "password":
187337
187369
  return redis.password;
187338
187370
  default:
187339
- throw new Error(`Unknown redis attribute: ${value.attribute}`);
187371
+ throw new Error(`Unknown redis attribute: ${String(value.attribute)}`);
187340
187372
  }
187341
187373
  }
187342
187374
  case "storage": {
@@ -187366,7 +187398,7 @@ function resolveEnvValue(value, resources, secrets, configs, servicePort, servic
187366
187398
  }
187367
187399
  return storage.bucket;
187368
187400
  default:
187369
- throw new Error(`Unknown storage attribute: ${value.attribute}`);
187401
+ throw new Error(`Unknown storage attribute: ${String(value.attribute)}`);
187370
187402
  }
187371
187403
  }
187372
187404
  case "config": {
@@ -187927,9 +187959,9 @@ function extractServiceAndKey(host) {
187927
187959
  return null;
187928
187960
  }
187929
187961
  const parts = prefix.split(".");
187930
- if (parts.length === 1) {
187962
+ if (parts.length === 1 && parts[0]) {
187931
187963
  return { serviceName: parts[0], key: "default" };
187932
- } else if (parts.length === 2) {
187964
+ } else if (parts.length === 2 && parts[0] && parts[1]) {
187933
187965
  return { serviceName: parts[0], key: parts[1] };
187934
187966
  }
187935
187967
  return null;
@@ -187946,7 +187978,7 @@ function extractDrizzleGatewayKey(host) {
187946
187978
  const parts = prefix.split(".");
187947
187979
  if (parts.length === 1 && parts[0] === DRIZZLE_GATEWAY_PREFIX) {
187948
187980
  return "default";
187949
- } else if (parts.length === 2 && parts[0] === DRIZZLE_GATEWAY_PREFIX) {
187981
+ } else if (parts.length === 2 && parts[0] === DRIZZLE_GATEWAY_PREFIX && parts[1]) {
187950
187982
  return parts[1];
187951
187983
  }
187952
187984
  return null;
@@ -187967,7 +187999,7 @@ function extractAdminKey(host) {
187967
187999
  return null;
187968
188000
  }
187969
188001
  const parts = prefix.split(".");
187970
- if (parts.length === 1) {
188002
+ if (parts.length === 1 && parts[0]) {
187971
188003
  return parts[0];
187972
188004
  }
187973
188005
  return null;
@@ -189502,14 +189534,15 @@ Add them to the config block in specific.local`);
189502
189534
  }
189503
189535
  }
189504
189536
  const validationErrors = validateEndpointReferences(config2);
189505
- if (validationErrors.length > 0) {
189537
+ const firstError = validationErrors[0];
189538
+ if (firstError) {
189506
189539
  for (const error of validationErrors) {
189507
189540
  writeLog("system:error", error.message);
189508
189541
  }
189509
189542
  setState((s) => ({
189510
189543
  ...s,
189511
189544
  status: "error",
189512
- error: validationErrors[0].message
189545
+ error: firstError.message
189513
189546
  }));
189514
189547
  return;
189515
189548
  }
@@ -189616,6 +189649,7 @@ Add them to the config block in specific.local`);
189616
189649
  for (const s of services2) {
189617
189650
  runningServicePorts.set(s.name, s.ports.get("default"));
189618
189651
  }
189652
+ const projectId = hasProjectId() ? readProjectId() : void 0;
189619
189653
  const getState = () => ({
189620
189654
  status: "running",
189621
189655
  services: config2.services.filter((svc) => runningServicePorts.has(svc.name) || svc.serve).map((svc) => ({
@@ -189630,7 +189664,8 @@ Add them to the config block in specific.local`);
189630
189664
  port: r.port,
189631
189665
  host: r.host,
189632
189666
  syncEnabled: r.type === "postgres" && syncDatabases.has(name)
189633
- }))
189667
+ })),
189668
+ projectId
189634
189669
  });
189635
189670
  const adminServer = await startAdminServer(getState);
189636
189671
  adminServerRef.current = adminServer;
@@ -190283,16 +190318,19 @@ function findWidestContext(projectDir, contexts) {
190283
190318
  if (contexts.length === 0) return ".";
190284
190319
  const absolute = contexts.map((c) => path18.resolve(projectDir, c));
190285
190320
  const segments = absolute.map((p) => p.split(path18.sep).filter(Boolean));
190321
+ const firstSegments = segments[0];
190322
+ if (!firstSegments) return ".";
190286
190323
  const minLen = Math.min(...segments.map((s) => s.length));
190287
190324
  let commonLength = 0;
190288
190325
  for (let i = 0; i < minLen; i++) {
190289
- if (segments.every((s) => s[i] === segments[0][i])) {
190326
+ const firstSeg = firstSegments[i];
190327
+ if (firstSeg !== void 0 && segments.every((s) => s[i] === firstSeg)) {
190290
190328
  commonLength = i + 1;
190291
190329
  } else {
190292
190330
  break;
190293
190331
  }
190294
190332
  }
190295
- const ancestorSegments = segments[0].slice(0, commonLength);
190333
+ const ancestorSegments = firstSegments.slice(0, commonLength);
190296
190334
  const ancestor = path18.sep + ancestorSegments.join(path18.sep);
190297
190335
  return path18.relative(projectDir, ancestor) || ".";
190298
190336
  }
@@ -190326,7 +190364,7 @@ function PhaseIndicator({
190326
190364
  "creating-tarball",
190327
190365
  "creating-deployment",
190328
190366
  "uploading",
190329
- "building",
190367
+ "pending",
190330
190368
  "deploying",
190331
190369
  "success"
190332
190370
  ];
@@ -190364,7 +190402,10 @@ function ProjectSelector({
190364
190402
  if (selectedIndex === 0) {
190365
190403
  onSelect("new");
190366
190404
  } else {
190367
- onSelect(projects[selectedIndex - 1]);
190405
+ const project = projects[selectedIndex - 1];
190406
+ if (project) {
190407
+ onSelect(project);
190408
+ }
190368
190409
  }
190369
190410
  } else if (key.upArrow) {
190370
190411
  onUp();
@@ -190395,6 +190436,30 @@ function NameInput({ onSubmit, onCancel }) {
190395
190436
  });
190396
190437
  return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React7.createElement(Text7, { bold: true }, "Create new project"), /* @__PURE__ */ React7.createElement(Text7, null, "Enter project name:"), /* @__PURE__ */ React7.createElement(Box7, null, /* @__PURE__ */ React7.createElement(Text7, { color: "cyan" }, "> "), /* @__PURE__ */ React7.createElement(Text7, null, value), /* @__PURE__ */ React7.createElement(Text7, { color: "gray" }, "|")), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Press Enter to create, Esc to go back"));
190397
190438
  }
190439
+ function getMissingSecrets(pendingActions) {
190440
+ if (!pendingActions) return [];
190441
+ const secretAction = pendingActions.find((a) => a.type === "missing_secrets");
190442
+ if (secretAction && secretAction.type === "missing_secrets") {
190443
+ return secretAction.secrets;
190444
+ }
190445
+ return [];
190446
+ }
190447
+ function getMissingConfigs(pendingActions) {
190448
+ if (!pendingActions) return [];
190449
+ const configAction = pendingActions.find((a) => a.type === "missing_configs");
190450
+ if (configAction && configAction.type === "missing_configs") {
190451
+ return configAction.configs;
190452
+ }
190453
+ return [];
190454
+ }
190455
+ function hasBuildsInProgress(pendingActions) {
190456
+ if (!pendingActions) return false;
190457
+ return pendingActions.some((a) => a.type === "build_in_progress");
190458
+ }
190459
+ function getFailedBuild(pendingActions) {
190460
+ if (!pendingActions) return void 0;
190461
+ return pendingActions.find((a) => a.type === "build_failed");
190462
+ }
190398
190463
  function DeployUI({ environment, config, skipBuildTest }) {
190399
190464
  const { exit } = useApp3();
190400
190465
  const [state, setState] = useState6({ phase: "checking-auth" });
@@ -190563,6 +190628,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
190563
190628
  setState((s) => {
190564
190629
  if (!s.missingSecrets || s.currentSecretIndex === void 0) return s;
190565
190630
  const currentSecret2 = s.missingSecrets[s.currentSecretIndex];
190631
+ if (!currentSecret2) return s;
190566
190632
  const newSecretValues = { ...s.secretValues, [currentSecret2]: value };
190567
190633
  const nextIndex = s.currentSecretIndex + 1;
190568
190634
  if (nextIndex < s.missingSecrets.length) {
@@ -190592,6 +190658,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
190592
190658
  setState((s) => {
190593
190659
  if (!s.missingConfigs || s.currentConfigIndex === void 0) return s;
190594
190660
  const currentConfig2 = s.missingConfigs[s.currentConfigIndex];
190661
+ if (!currentConfig2) return s;
190595
190662
  const newConfigValues = { ...s.configValues, [currentConfig2]: value };
190596
190663
  const nextIndex = s.currentConfigIndex + 1;
190597
190664
  if (nextIndex < s.missingConfigs.length) {
@@ -190644,7 +190711,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
190644
190711
  writeLog("deploy", "Secrets submitted successfully");
190645
190712
  setState((s) => ({
190646
190713
  ...s,
190647
- phase: "building",
190714
+ phase: "pending",
190648
190715
  missingSecrets: void 0,
190649
190716
  secretValues: void 0
190650
190717
  }));
@@ -190686,7 +190753,7 @@ function DeployUI({ environment, config, skipBuildTest }) {
190686
190753
  writeLog("deploy", "Configs submitted successfully");
190687
190754
  setState((s) => ({
190688
190755
  ...s,
190689
- phase: "building",
190756
+ phase: "pending",
190690
190757
  missingConfigs: void 0,
190691
190758
  configValues: void 0
190692
190759
  }));
@@ -190704,7 +190771,6 @@ function DeployUI({ environment, config, skipBuildTest }) {
190704
190771
  useEffect4(() => {
190705
190772
  if (state.phase !== "testing-builds" || !state.projectId) return;
190706
190773
  let cancelled = false;
190707
- let pollInterval;
190708
190774
  async function runBuildTestsAndDeploy() {
190709
190775
  const projectDir = process.cwd();
190710
190776
  const builds = config.builds || [];
@@ -190803,148 +190869,205 @@ ${errorMsg}`
190803
190869
  return;
190804
190870
  }
190805
190871
  if (cancelled) return;
190806
- writeLog("deploy", "Waiting for build to complete");
190807
- setState((s) => ({ ...s, phase: "building", deployment: deployment2 }));
190808
- let lastState;
190809
- const pollForCompletion = async () => {
190810
- try {
190811
- const status = await client2.getDeployment(deployment2.id);
190812
- if (cancelled) return;
190813
- if (status.state !== lastState) {
190814
- writeLog(
190815
- "deploy",
190816
- `Deployment state: ${status.state}${status.stateMessage ? ` - ${status.stateMessage}` : ""}`
190817
- );
190818
- lastState = status.state;
190819
- }
190820
- if (status.state === "failed") {
190821
- writeLog(
190822
- "deploy:error",
190823
- `Deployment failed: ${status.stateMessage || "Unknown error"}`
190824
- );
190825
- setState((s) => ({
190826
- ...s,
190827
- phase: "error",
190828
- deployment: status,
190829
- error: status.stateMessage || "Deployment failed"
190830
- }));
190831
- if (pollInterval) clearInterval(pollInterval);
190832
- return;
190833
- }
190834
- if (status.state === "active") {
190835
- writeLog("deploy", "Deployment successful");
190836
- if (status.publicUrls) {
190837
- for (const [name, url] of Object.entries(status.publicUrls)) {
190838
- writeLog("deploy", `Public URL: ${name} -> ${url}`);
190839
- }
190840
- }
190841
- setState((s) => ({ ...s, phase: "success", deployment: status }));
190842
- if (pollInterval) clearInterval(pollInterval);
190843
- return;
190844
- }
190845
- if (status.state === "awaiting_secrets") {
190846
- if (pollInterval) clearInterval(pollInterval);
190847
- pollInterval = void 0;
190848
- const missingSecrets2 = status.missingSecrets || [];
190849
- writeLog("deploy", `Awaiting secrets: ${missingSecrets2.join(", ")}`);
190850
- setState((s) => ({
190851
- ...s,
190852
- phase: "awaiting-secrets",
190853
- deployment: status,
190854
- missingSecrets: missingSecrets2,
190855
- secretValues: {},
190856
- currentSecretIndex: 0,
190857
- currentSecretInput: ""
190858
- }));
190859
- return;
190860
- }
190861
- if (status.state === "awaiting_configs") {
190862
- if (pollInterval) clearInterval(pollInterval);
190863
- pollInterval = void 0;
190864
- const missingConfigs2 = status.missingConfigs || [];
190865
- writeLog("deploy", `Awaiting configs: ${missingConfigs2.join(", ")}`);
190866
- setState((s) => ({
190867
- ...s,
190868
- phase: "awaiting-configs",
190869
- deployment: status,
190870
- missingConfigs: missingConfigs2,
190871
- configValues: {},
190872
- currentConfigIndex: 0,
190873
- currentConfigInput: ""
190874
- }));
190875
- return;
190876
- }
190877
- if (status.state === "deploying") {
190878
- setState((s) => ({ ...s, phase: "deploying", deployment: status }));
190879
- } else if (status.state === "building") {
190880
- setState((s) => ({ ...s, phase: "building", deployment: status }));
190881
- }
190882
- } catch {
190883
- }
190884
- };
190885
- pollInterval = setInterval(pollForCompletion, 2e3);
190886
- pollForCompletion();
190872
+ writeLog("deploy", "Deployment in pending state, waiting for builds to complete");
190873
+ setState((s) => ({ ...s, phase: "pending", deployment: deployment2 }));
190887
190874
  }
190888
190875
  runBuildTestsAndDeploy();
190889
190876
  return () => {
190890
190877
  cancelled = true;
190891
- if (pollInterval) clearInterval(pollInterval);
190892
190878
  };
190893
190879
  }, [state.projectId, environment, config.builds, skipBuildTest]);
190894
190880
  useEffect4(() => {
190881
+ if (state.phase !== "pending" || !state.deployment) return;
190895
190882
  let pollInterval;
190896
- if ((state.phase === "building" || state.phase === "deploying") && state.deployment && state.missingSecrets === void 0 && state.secretValues === void 0 && state.missingConfigs === void 0 && state.configValues === void 0) {
190897
- const client2 = clientRef.current;
190898
- if (!client2) return;
190899
- const pollForCompletion = async () => {
190900
- try {
190901
- const status = await client2.getDeployment(state.deployment.id);
190902
- if (status.state === "failed") {
190903
- setState((s) => ({
190904
- ...s,
190905
- phase: "error",
190906
- deployment: status,
190907
- error: status.stateMessage || "Deployment failed"
190908
- }));
190909
- if (pollInterval) clearInterval(pollInterval);
190910
- return;
190911
- }
190912
- if (status.state === "active") {
190913
- setState((s) => ({ ...s, phase: "success", deployment: status }));
190914
- if (pollInterval) clearInterval(pollInterval);
190915
- return;
190916
- }
190917
- if (status.state === "awaiting_configs") {
190918
- if (pollInterval) clearInterval(pollInterval);
190919
- pollInterval = void 0;
190920
- const missingConfigs2 = status.missingConfigs || [];
190921
- writeLog("deploy", `Awaiting configs: ${missingConfigs2.join(", ")}`);
190922
- setState((s) => ({
190923
- ...s,
190924
- phase: "awaiting-configs",
190925
- deployment: status,
190926
- missingConfigs: missingConfigs2,
190927
- configValues: {},
190928
- currentConfigIndex: 0,
190929
- currentConfigInput: ""
190930
- }));
190931
- return;
190932
- }
190933
- if (status.state === "deploying") {
190934
- setState((s) => ({ ...s, phase: "deploying", deployment: status }));
190935
- } else if (status.state === "building") {
190936
- setState((s) => ({ ...s, phase: "building", deployment: status }));
190883
+ let cancelled = false;
190884
+ const client2 = clientRef.current;
190885
+ if (!client2) return;
190886
+ const pollForPendingActions = async () => {
190887
+ if (cancelled) return;
190888
+ try {
190889
+ const status = await client2.getDeployment(state.deployment.id);
190890
+ if (cancelled) return;
190891
+ writeLog(
190892
+ "deploy",
190893
+ `Deployment state: ${status.state}, pending actions: ${JSON.stringify(status.pendingActions || [])}`
190894
+ );
190895
+ if (status.state === "failed") {
190896
+ if (pollInterval) clearInterval(pollInterval);
190897
+ writeLog("deploy:error", `Deployment failed: ${status.stateMessage || "Unknown error"}`);
190898
+ setState((s) => ({
190899
+ ...s,
190900
+ phase: "error",
190901
+ deployment: status,
190902
+ error: status.stateMessage || "Deployment failed"
190903
+ }));
190904
+ return;
190905
+ }
190906
+ const failedBuild = getFailedBuild(status.pendingActions);
190907
+ if (failedBuild && failedBuild.type === "build_failed") {
190908
+ if (pollInterval) clearInterval(pollInterval);
190909
+ writeLog("deploy:error", `Build failed: ${failedBuild.error}`);
190910
+ setState((s) => ({
190911
+ ...s,
190912
+ phase: "error",
190913
+ deployment: status,
190914
+ error: `Build "${failedBuild.serviceName}" failed: ${failedBuild.error}`
190915
+ }));
190916
+ return;
190917
+ }
190918
+ const missingSecrets2 = getMissingSecrets(status.pendingActions);
190919
+ if (missingSecrets2.length > 0) {
190920
+ if (pollInterval) clearInterval(pollInterval);
190921
+ pollInterval = void 0;
190922
+ writeLog("deploy", `Awaiting secrets: ${missingSecrets2.join(", ")}`);
190923
+ setState((s) => ({
190924
+ ...s,
190925
+ phase: "awaiting-secrets",
190926
+ deployment: status,
190927
+ pendingActions: status.pendingActions,
190928
+ missingSecrets: missingSecrets2,
190929
+ secretValues: {},
190930
+ currentSecretIndex: 0,
190931
+ currentSecretInput: ""
190932
+ }));
190933
+ return;
190934
+ }
190935
+ const missingConfigs2 = getMissingConfigs(status.pendingActions);
190936
+ if (missingConfigs2.length > 0) {
190937
+ if (pollInterval) clearInterval(pollInterval);
190938
+ pollInterval = void 0;
190939
+ writeLog("deploy", `Awaiting configs: ${missingConfigs2.join(", ")}`);
190940
+ setState((s) => ({
190941
+ ...s,
190942
+ phase: "awaiting-configs",
190943
+ deployment: status,
190944
+ pendingActions: status.pendingActions,
190945
+ missingConfigs: missingConfigs2,
190946
+ configValues: {},
190947
+ currentConfigIndex: 0,
190948
+ currentConfigInput: ""
190949
+ }));
190950
+ return;
190951
+ }
190952
+ if (hasBuildsInProgress(status.pendingActions)) {
190953
+ setState((s) => ({
190954
+ ...s,
190955
+ deployment: status,
190956
+ pendingActions: status.pendingActions
190957
+ }));
190958
+ return;
190959
+ }
190960
+ if (!status.pendingActions || status.pendingActions.length === 0) {
190961
+ if (pollInterval) clearInterval(pollInterval);
190962
+ pollInterval = void 0;
190963
+ writeLog("deploy", "All pending actions resolved, starting deployment");
190964
+ setState((s) => ({
190965
+ ...s,
190966
+ phase: "starting",
190967
+ deployment: status,
190968
+ pendingActions: []
190969
+ }));
190970
+ return;
190971
+ }
190972
+ setState((s) => ({
190973
+ ...s,
190974
+ deployment: status,
190975
+ pendingActions: status.pendingActions
190976
+ }));
190977
+ } catch (err) {
190978
+ writeLog("deploy", `Poll error: ${err instanceof Error ? err.message : String(err)}`);
190979
+ }
190980
+ };
190981
+ pollInterval = setInterval(pollForPendingActions, 2e3);
190982
+ pollForPendingActions();
190983
+ return () => {
190984
+ cancelled = true;
190985
+ if (pollInterval) clearInterval(pollInterval);
190986
+ };
190987
+ }, [state.phase, state.deployment?.id]);
190988
+ useEffect4(() => {
190989
+ if (state.phase !== "starting" || !state.deployment) return;
190990
+ let cancelled = false;
190991
+ const client2 = clientRef.current;
190992
+ if (!client2) return;
190993
+ (async () => {
190994
+ try {
190995
+ writeLog("deploy", "Calling /start endpoint");
190996
+ await client2.startDeployment(state.deployment.id);
190997
+ writeLog("deploy", "Deployment started successfully");
190998
+ if (cancelled) return;
190999
+ setState((s) => ({
191000
+ ...s,
191001
+ phase: "queued"
191002
+ }));
191003
+ } catch (err) {
191004
+ if (cancelled) return;
191005
+ const errorMsg = `Failed to start deployment: ${err instanceof Error ? err.message : String(err)}`;
191006
+ writeLog("deploy:error", errorMsg);
191007
+ setState((s) => ({
191008
+ ...s,
191009
+ phase: "error",
191010
+ error: errorMsg
191011
+ }));
191012
+ }
191013
+ })();
191014
+ return () => {
191015
+ cancelled = true;
191016
+ };
191017
+ }, [state.phase, state.deployment?.id]);
191018
+ useEffect4(() => {
191019
+ if (state.phase !== "queued" && state.phase !== "deploying" || !state.deployment) return;
191020
+ let pollInterval;
191021
+ let cancelled = false;
191022
+ const client2 = clientRef.current;
191023
+ if (!client2) return;
191024
+ const pollForCompletion = async () => {
191025
+ if (cancelled) return;
191026
+ try {
191027
+ const status = await client2.getDeployment(state.deployment.id);
191028
+ if (cancelled) return;
191029
+ writeLog(
191030
+ "deploy",
191031
+ `Deployment state: ${status.state}${status.stateMessage ? ` - ${status.stateMessage}` : ""}`
191032
+ );
191033
+ if (status.state === "failed") {
191034
+ if (pollInterval) clearInterval(pollInterval);
191035
+ writeLog("deploy:error", `Deployment failed: ${status.stateMessage || "Unknown error"}`);
191036
+ setState((s) => ({
191037
+ ...s,
191038
+ phase: "error",
191039
+ deployment: status,
191040
+ error: status.stateMessage || "Deployment failed"
191041
+ }));
191042
+ return;
191043
+ }
191044
+ if (status.state === "active") {
191045
+ if (pollInterval) clearInterval(pollInterval);
191046
+ writeLog("deploy", "Deployment successful");
191047
+ if (status.publicUrls) {
191048
+ for (const [name, url] of Object.entries(status.publicUrls)) {
191049
+ writeLog("deploy", `Public URL: ${name} -> ${url}`);
191050
+ }
190937
191051
  }
190938
- } catch {
191052
+ setState((s) => ({ ...s, phase: "success", deployment: status }));
191053
+ return;
190939
191054
  }
190940
- };
190941
- pollInterval = setInterval(pollForCompletion, 2e3);
190942
- pollForCompletion();
190943
- return () => {
190944
- if (pollInterval) clearInterval(pollInterval);
190945
- };
190946
- }
190947
- }, [state.phase, state.missingSecrets, state.secretValues, state.missingConfigs, state.configValues]);
191055
+ if (status.state === "deploying") {
191056
+ setState((s) => ({ ...s, phase: "deploying", deployment: status }));
191057
+ } else if (status.state === "queued") {
191058
+ setState((s) => ({ ...s, phase: "queued", deployment: status }));
191059
+ }
191060
+ } catch (err) {
191061
+ writeLog("deploy", `Poll error: ${err instanceof Error ? err.message : String(err)}`);
191062
+ }
191063
+ };
191064
+ pollInterval = setInterval(pollForCompletion, 2e3);
191065
+ pollForCompletion();
191066
+ return () => {
191067
+ cancelled = true;
191068
+ if (pollInterval) clearInterval(pollInterval);
191069
+ };
191070
+ }, [state.phase, state.deployment?.id]);
190948
191071
  useEffect4(() => {
190949
191072
  if (state.phase === "testing-builds") {
190950
191073
  trackEvent("deploy_started", { environment });
@@ -190969,6 +191092,7 @@ ${errorMsg}`
190969
191092
  deployment,
190970
191093
  error,
190971
191094
  tarballSize,
191095
+ pendingActions,
190972
191096
  missingSecrets,
190973
191097
  currentSecretIndex,
190974
191098
  missingConfigs,
@@ -191016,7 +191140,28 @@ ${errorMsg}`
191016
191140
  }
191017
191141
  const currentSecret = missingSecrets && currentSecretIndex !== void 0 ? missingSecrets[currentSecretIndex] : void 0;
191018
191142
  const currentConfig = missingConfigs && currentConfigIndex !== void 0 ? missingConfigs[currentConfigIndex] : void 0;
191019
- const displayPhase = phase === "awaiting-secrets" || phase === "awaiting-configs" ? "building" : phase;
191143
+ const getDisplayPhase = () => {
191144
+ if (phase === "awaiting-secrets" || phase === "awaiting-configs" || phase === "starting") {
191145
+ return "pending";
191146
+ }
191147
+ if (phase === "queued") {
191148
+ return "deploying";
191149
+ }
191150
+ return phase;
191151
+ };
191152
+ const displayPhase = getDisplayPhase();
191153
+ const getPendingLabel = () => {
191154
+ if (hasBuildsInProgress(pendingActions)) {
191155
+ return "Building images";
191156
+ }
191157
+ if (phase === "awaiting-secrets" || phase === "awaiting-configs") {
191158
+ return "Waiting for input";
191159
+ }
191160
+ if (phase === "starting") {
191161
+ return "Starting deployment";
191162
+ }
191163
+ return "Preparing";
191164
+ };
191020
191165
  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(
191021
191166
  PhaseIndicator,
191022
191167
  {
@@ -191048,16 +191193,16 @@ ${errorMsg}`
191048
191193
  ), /* @__PURE__ */ React7.createElement(
191049
191194
  PhaseIndicator,
191050
191195
  {
191051
- phase: "building",
191196
+ phase: "pending",
191052
191197
  currentPhase: displayPhase,
191053
- label: "Building"
191198
+ label: getPendingLabel()
191054
191199
  }
191055
191200
  ), /* @__PURE__ */ React7.createElement(
191056
191201
  PhaseIndicator,
191057
191202
  {
191058
191203
  phase: "deploying",
191059
191204
  currentPhase: displayPhase,
191060
- label: "Deploying"
191205
+ label: phase === "queued" ? "Waiting in queue" : "Deploying"
191061
191206
  }
191062
191207
  )), phase === "awaiting-secrets" && currentSecret && /* @__PURE__ */ React7.createElement(
191063
191208
  SecretInput,
@@ -191493,7 +191638,7 @@ function logoutCommand() {
191493
191638
  var program = new Command();
191494
191639
  var env = "production";
191495
191640
  var envLabel = env !== "production" ? `[${env.toUpperCase()}] ` : "";
191496
- program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.44").enablePositionalOptions();
191641
+ program.name("specific").description(`${envLabel}Infrastructure-as-code for coding agents`).version("0.1.46").enablePositionalOptions();
191497
191642
  program.command("init").description("Initialize project for use with a coding agent").action(initCommand);
191498
191643
  program.command("docs [topic]").description("Fetch LLM-optimized documentation").action(docsCommand);
191499
191644
  program.command("check").description("Validate specific.hcl configuration").action(checkCommand);