ardent-cli 0.0.50 → 0.0.52

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 (2) hide show
  1. package/dist/index.js +201 -49
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -511,6 +511,64 @@ async function resolveCurrentConnectorId() {
511
511
  );
512
512
  }
513
513
 
514
+ // src/lib/operation_stage_display.ts
515
+ var STAGE_DISPLAY = {
516
+ // engine_setup_worker / postgres_engine_setup
517
+ "dispatched": "Queued",
518
+ "preparing": "Preparing",
519
+ "creating-neon-project": "Provisioning the branch target",
520
+ "preparing-target-databases": "Preparing target databases",
521
+ "deploying-pgstream": "Starting replication",
522
+ "applying-rls": "Applying RLS policies",
523
+ "storing-credentials": "Storing connection credentials",
524
+ "validating": "Validating the branch target",
525
+ // reset_worker / reset_connector
526
+ "resetting": "Resetting",
527
+ "deleting-pgstream": "Stopping replication",
528
+ "rediscovering-source": "Re-checking the source database",
529
+ "resetting-neon-main": "Resetting the branch target",
530
+ "creating-target-schemas": "Recreating target schemas",
531
+ "redeploying-pgstream": "Restarting replication",
532
+ // environment deploy_worker
533
+ "loading_config": "Loading environment configuration",
534
+ "deploying_infrastructure": "Provisioning environment infrastructure",
535
+ "recording_success": "Finalizing environment",
536
+ "cleaning_failed_deploy": "Cleaning up failed environment provisioning",
537
+ // environment destroy_worker
538
+ "deleting_private_links": "Removing private network links",
539
+ "destroying_infrastructure": "Tearing down environment infrastructure",
540
+ "recording_destroy_success": "Finalizing environment teardown",
541
+ // discovery_worker / run_discovery_crawl (ARD-1098)
542
+ "connecting": "Connecting to source",
543
+ "enumerating_databases": "Listing databases",
544
+ "scanning_database": "Scanning schema",
545
+ "writing_schema": "Saving discovered schema",
546
+ "merging_results": "Combining results",
547
+ // connector delete DBOS workflow
548
+ "deleting": "Starting delete",
549
+ "cleaning_resources": "Removing connector resources",
550
+ "scheduling_secret_purge": "Scheduling credential removal",
551
+ "finalizing_delete": "Finalizing delete",
552
+ "purging_secrets": "Removing stored credentials",
553
+ // branch.create.v1 workflow (ARD-1244)
554
+ "provisioning": "Provisioning the branch",
555
+ "configuring": "Storing connection credentials",
556
+ "recording": "Recording the branch",
557
+ "finalizing": "Finalizing the branch",
558
+ "activating": "Activating the branch"
559
+ };
560
+ function humanizeRawStage(_raw) {
561
+ return "Working";
562
+ }
563
+ function operationStageDisplay(stage) {
564
+ if (!stage) return "Working";
565
+ const colonIndex = stage.indexOf(":");
566
+ const base = colonIndex === -1 ? stage : stage.slice(0, colonIndex);
567
+ const suffix = colonIndex === -1 ? "" : stage.slice(colonIndex + 1);
568
+ const label = STAGE_DISPLAY[base] ?? humanizeRawStage(base);
569
+ return suffix ? `${label} (for ${suffix})` : label;
570
+ }
571
+
514
572
  // src/lib/resource_name_validation.ts
515
573
  var RESERVED_SUFFIXES = ["pooler", "readonly", "direct"];
516
574
  var MAX_RESOURCE_NAME_LENGTH = 100;
@@ -683,6 +741,28 @@ function isMachineReadableBranchInvocation(args2) {
683
741
  }
684
742
 
685
743
  // src/commands/branch/create.ts
744
+ var BRANCH_CREATE_MAX_WAIT_MS = 10 * 60 * 1e3;
745
+ var BRANCH_CREATE_POLL_INTERVAL_MS = 2 * 1e3;
746
+ var BRANCH_CREATE_IN_PROGRESS_DETAIL = "Branch creation is still in progress";
747
+ function branchCreateMaxWaitMs() {
748
+ return Number(process.env.ARDENT_BRANCH_CREATE_MAX_WAIT_MS) || BRANCH_CREATE_MAX_WAIT_MS;
749
+ }
750
+ function branchCreatePollIntervalMs() {
751
+ return Number(process.env.ARDENT_BRANCH_CREATE_POLL_INTERVAL_MS) || BRANCH_CREATE_POLL_INTERVAL_MS;
752
+ }
753
+ var BRANCH_CREATE_TRANSIENT_WARN_EVERY = 10;
754
+ function asBranchCreateWarning(value) {
755
+ if (value && typeof value === "object" && value.type === "stale_source" && typeof value.message === "string") {
756
+ return value;
757
+ }
758
+ return void 0;
759
+ }
760
+ function isBranchCreateInProgressError(err) {
761
+ if (!(err instanceof Error)) {
762
+ return false;
763
+ }
764
+ return err.message.includes("API error 409") && err.message.includes(BRANCH_CREATE_IN_PROGRESS_DETAIL);
765
+ }
686
766
  async function createAction(name, options) {
687
767
  const modeResolution = resolveOutputMode(options);
688
768
  if (modeResolution.error) {
@@ -715,7 +795,7 @@ async function createAction(name, options) {
715
795
  idempotencyKey = randomUUID2();
716
796
  setPendingBranchCreateKey(idempotencyScopeKey, idempotencyKey);
717
797
  }
718
- const createResponse = await api.post(
798
+ const dispatch = await api.post(
719
799
  "/v1/branch/create",
720
800
  {
721
801
  connector_id: connectorId,
@@ -724,7 +804,7 @@ async function createAction(name, options) {
724
804
  },
725
805
  { "X-Idempotency-Key": idempotencyKey }
726
806
  );
727
- const warning = createResponse?.warning;
807
+ const warning = await pollBranchCreate(dispatch.operation_id, idempotencyScopeKey, mode);
728
808
  const response = await api.get(`/v1/cli/branches?connector_id=${connectorId}`);
729
809
  const apiBranches = response.branches || [];
730
810
  let apiBranch;
@@ -816,6 +896,15 @@ ${url}`);
816
896
  printCurrentConnectorSelectionError(err);
817
897
  process.exit(1);
818
898
  }
899
+ if (err instanceof BranchCreateTimeoutError) {
900
+ trackEvent("CLI: branch create failed", { reason: "timeout", output_mode: mode });
901
+ if (mode === "json") {
902
+ process.stdout.write(renderBranchJsonError("timeout", err.message));
903
+ process.exit(1);
904
+ }
905
+ console.error(`\u2717 ${err.message}`);
906
+ process.exit(1);
907
+ }
819
908
  if (isNetworkError(err)) {
820
909
  trackEvent("CLI: branch create failed", { reason: "offline", output_mode: mode });
821
910
  if (mode === "json") {
@@ -827,6 +916,19 @@ ${url}`);
827
916
  console.error("\u2717 Cannot create branch while offline");
828
917
  process.exit(1);
829
918
  }
919
+ if (isBranchCreateInProgressError(err)) {
920
+ trackEvent("CLI: branch create failed", {
921
+ reason: "in_progress",
922
+ output_mode: mode
923
+ });
924
+ const message2 = err instanceof Error ? err.message : String(err);
925
+ if (mode === "json") {
926
+ process.stdout.write(renderBranchJsonError("api_error", message2));
927
+ process.exit(1);
928
+ }
929
+ console.error("\u2717 Failed:", message2);
930
+ process.exit(1);
931
+ }
830
932
  if (idempotencyScopeKey) {
831
933
  clearPendingBranchCreateKey(idempotencyScopeKey);
832
934
  }
@@ -840,6 +942,57 @@ ${url}`);
840
942
  process.exit(1);
841
943
  }
842
944
  }
945
+ var BranchCreateTimeoutError = class extends Error {
946
+ constructor(message) {
947
+ super(message);
948
+ this.name = "BranchCreateTimeoutError";
949
+ }
950
+ };
951
+ async function pollBranchCreate(operationId, idempotencyScopeKey, mode) {
952
+ const startedAt = Date.now();
953
+ const maxWaitMs = branchCreateMaxWaitMs();
954
+ const pollIntervalMs = branchCreatePollIntervalMs();
955
+ let lastStage = null;
956
+ let consecutiveTransientFailures = 0;
957
+ let firstPoll = true;
958
+ while (Date.now() - startedAt < maxWaitMs) {
959
+ if (!firstPoll) {
960
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
961
+ }
962
+ firstPoll = false;
963
+ let op;
964
+ try {
965
+ op = await api.get(`/v1/operations/${operationId}`);
966
+ } catch (pollErr) {
967
+ if (isTransientOperationPollError(pollErr)) {
968
+ consecutiveTransientFailures += 1;
969
+ if (consecutiveTransientFailures % BRANCH_CREATE_TRANSIENT_WARN_EVERY === 0 && mode !== "json") {
970
+ console.warn(
971
+ ` \u26A0 Status check has failed ${consecutiveTransientFailures} times in a row. Branch creation is still running server-side and the CLI will keep waiting.`
972
+ );
973
+ }
974
+ continue;
975
+ }
976
+ throw pollErr;
977
+ }
978
+ consecutiveTransientFailures = 0;
979
+ if (op.stage && op.stage !== lastStage && mode !== "json" && mode !== "print-url") {
980
+ const progressLabel = op.progress != null ? ` (${op.progress}%)` : "";
981
+ console.log(` ${operationStageDisplay(op.stage)}${progressLabel}`);
982
+ lastStage = op.stage;
983
+ }
984
+ if (op.status === "completed") {
985
+ return asBranchCreateWarning(op.result?.warning);
986
+ }
987
+ if (op.status === "failed") {
988
+ clearPendingBranchCreateKey(idempotencyScopeKey);
989
+ throw new Error(op.error ?? "Branch creation failed");
990
+ }
991
+ }
992
+ throw new BranchCreateTimeoutError(
993
+ `Branch creation did not complete within ${Math.round(maxWaitMs / 6e4)} minutes. It may still be running server-side \u2014 do NOT assume it failed. Re-run the same command to resume, or check \`ardent branch list\`. If you contact Ardent support, reference operation id ${operationId}.`
994
+ );
995
+ }
843
996
 
844
997
  // src/commands/branch/list.ts
845
998
  function formatCreatedDate(createdAtIso) {
@@ -1143,52 +1296,6 @@ function printDegradedWarnings(warnings) {
1143
1296
  console.log(" Review this connector with: ardent connector list");
1144
1297
  }
1145
1298
 
1146
- // src/lib/operation_stage_display.ts
1147
- var STAGE_DISPLAY = {
1148
- // engine_setup_worker / postgres_engine_setup
1149
- "dispatched": "Starting",
1150
- "preparing": "Preparing",
1151
- "creating-neon-project": "Provisioning the branch target",
1152
- "preparing-target-databases": "Preparing target databases",
1153
- "deploying-pgstream": "Starting replication",
1154
- "applying-rls": "Applying RLS policies",
1155
- "storing-credentials": "Storing connection credentials",
1156
- "validating": "Validating the branch target",
1157
- // reset_worker / reset_connector
1158
- "resetting": "Resetting",
1159
- "deleting-pgstream": "Stopping replication",
1160
- "rediscovering-source": "Re-checking the source database",
1161
- "resetting-neon-main": "Resetting the branch target",
1162
- "creating-target-schemas": "Recreating target schemas",
1163
- "redeploying-pgstream": "Restarting replication",
1164
- // environment deploy_worker
1165
- "loading_config": "Loading environment configuration",
1166
- "deploying_infrastructure": "Provisioning environment infrastructure",
1167
- "recording_success": "Finalizing environment",
1168
- "cleaning_failed_deploy": "Cleaning up failed environment provisioning",
1169
- // environment destroy_worker
1170
- "deleting_private_links": "Removing private network links",
1171
- "destroying_infrastructure": "Tearing down environment infrastructure",
1172
- "recording_destroy_success": "Finalizing environment teardown",
1173
- // discovery_worker / run_discovery_crawl (ARD-1098)
1174
- "connecting": "Connecting to source",
1175
- "enumerating_databases": "Listing databases",
1176
- "scanning_database": "Scanning schema",
1177
- "writing_schema": "Saving discovered schema",
1178
- "merging_results": "Combining results"
1179
- };
1180
- function humanizeRawStage(_raw) {
1181
- return "Working";
1182
- }
1183
- function operationStageDisplay(stage) {
1184
- if (!stage) return "Working";
1185
- const colonIndex = stage.indexOf(":");
1186
- const base = colonIndex === -1 ? stage : stage.slice(0, colonIndex);
1187
- const suffix = colonIndex === -1 ? "" : stage.slice(colonIndex + 1);
1188
- const label = STAGE_DISPLAY[base] ?? humanizeRawStage(base);
1189
- return suffix ? `${label} (for ${suffix})` : label;
1190
- }
1191
-
1192
1299
  // src/lib/discover.ts
1193
1300
  var DiscoveryTimeoutError = class extends Error {
1194
1301
  constructor(message) {
@@ -2557,6 +2664,9 @@ async function listAction2() {
2557
2664
  }
2558
2665
 
2559
2666
  // src/commands/connector/delete.ts
2667
+ var CONNECTOR_DELETE_MAX_WAIT_MS = 60 * 60 * 1e3;
2668
+ var CONNECTOR_DELETE_POLL_INTERVAL_MS = 5 * 1e3;
2669
+ var CONNECTOR_DELETE_TRANSIENT_WARN_EVERY = 6;
2560
2670
  async function deleteAction2(name, options = {}) {
2561
2671
  const cached = getCacheEntry("connectors");
2562
2672
  let connector = cached?.data.find((c) => c.name === name);
@@ -2584,7 +2694,11 @@ async function deleteAction2(name, options = {}) {
2584
2694
  console.log("Deleting connector...");
2585
2695
  const force = options.force ?? false;
2586
2696
  const path = force ? `/v1/connectors/${connector.id}?force=true` : `/v1/connectors/${connector.id}`;
2587
- await api.delete(path);
2697
+ const dispatch = await api.delete(path);
2698
+ const operationId = dispatch.operation_id;
2699
+ if (operationId && dispatch.status !== "completed") {
2700
+ await waitForConnectorDelete(operationId);
2701
+ }
2588
2702
  const currentCache = getCacheEntry("connectors");
2589
2703
  if (currentCache?.data) {
2590
2704
  const updatedConnectors = currentCache.data.filter((c) => c.id !== connector.id);
@@ -2619,6 +2733,44 @@ async function deleteAction2(name, options = {}) {
2619
2733
  process.exit(1);
2620
2734
  }
2621
2735
  }
2736
+ async function waitForConnectorDelete(operationId) {
2737
+ const startedAt = Date.now();
2738
+ let lastStage = null;
2739
+ let consecutiveTransientFailures = 0;
2740
+ while (Date.now() - startedAt < CONNECTOR_DELETE_MAX_WAIT_MS) {
2741
+ await new Promise((resolve) => setTimeout(resolve, CONNECTOR_DELETE_POLL_INTERVAL_MS));
2742
+ let operation;
2743
+ try {
2744
+ operation = await api.get(`/v1/operations/${operationId}`);
2745
+ } catch (pollErr) {
2746
+ if (isTransientOperationPollError(pollErr)) {
2747
+ consecutiveTransientFailures += 1;
2748
+ if (consecutiveTransientFailures % CONNECTOR_DELETE_TRANSIENT_WARN_EVERY === 0) {
2749
+ console.warn(
2750
+ ` \u26A0 Status check has failed ${consecutiveTransientFailures} times in a row. Delete is still running server-side and the CLI will keep waiting.`
2751
+ );
2752
+ }
2753
+ continue;
2754
+ }
2755
+ throw pollErr;
2756
+ }
2757
+ consecutiveTransientFailures = 0;
2758
+ if (operation.stage && operation.stage !== lastStage) {
2759
+ const progressLabel = operation.progress != null ? ` (${operation.progress}%)` : "";
2760
+ console.log(` ${operationStageDisplay(operation.stage)}${progressLabel}`);
2761
+ lastStage = operation.stage;
2762
+ }
2763
+ if (operation.status === "completed") {
2764
+ return;
2765
+ }
2766
+ if (operation.status === "failed") {
2767
+ throw new Error(operation.error ?? "Connector delete failed. Contact Ardent support.");
2768
+ }
2769
+ }
2770
+ throw new Error(
2771
+ `Connector delete did not complete within ${CONNECTOR_DELETE_MAX_WAIT_MS / 6e4} minutes. It may still be running server-side. If you contact Ardent support, reference operation id ${operationId}.`
2772
+ );
2773
+ }
2622
2774
 
2623
2775
  // src/commands/connector/preflight.ts
2624
2776
  var CHECK_RENDER_ORDER = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ardent-cli",
3
- "version": "0.0.50",
3
+ "version": "0.0.52",
4
4
  "description": "Git for Data infrastructure",
5
5
  "type": "module",
6
6
  "bin": {