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.
- package/dist/index.js +201 -49
- 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
|
|
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 =
|
|
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 = [
|