ardent-cli 0.0.26 → 0.0.28
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 +286 -79
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -757,29 +757,125 @@ import { Command as Command2 } from "commander";
|
|
|
757
757
|
var SUCCESS_ENGINE_STATUSES = /* @__PURE__ */ new Set(["healthy", "degraded"]);
|
|
758
758
|
var RETRYABLE_ENGINE_STATUSES = /* @__PURE__ */ new Set(["configuration_verified"]);
|
|
759
759
|
var FAILED_ENGINE_STATUSES = /* @__PURE__ */ new Set(["configuration_failed", "failed_validation"]);
|
|
760
|
-
|
|
760
|
+
var EngineSetupTerminalStatusError = class extends Error {
|
|
761
|
+
kind;
|
|
762
|
+
engineStatus;
|
|
763
|
+
constructor(kind, engineStatus, message) {
|
|
764
|
+
super(message);
|
|
765
|
+
this.name = "EngineSetupTerminalStatusError";
|
|
766
|
+
this.kind = kind;
|
|
767
|
+
this.engineStatus = engineStatus;
|
|
768
|
+
}
|
|
769
|
+
};
|
|
770
|
+
function assertEngineSetupCompleted(operation, connectorName) {
|
|
761
771
|
const result = operation.result;
|
|
762
772
|
if (!result) {
|
|
763
|
-
throw new
|
|
773
|
+
throw new EngineSetupTerminalStatusError(
|
|
774
|
+
"malformed_result",
|
|
775
|
+
null,
|
|
776
|
+
"Engine setup completed without a result payload."
|
|
777
|
+
);
|
|
764
778
|
}
|
|
765
779
|
const engineStatus = result.branching_engine_status;
|
|
766
780
|
if (typeof engineStatus !== "string" || engineStatus.length === 0) {
|
|
767
|
-
throw new
|
|
781
|
+
throw new EngineSetupTerminalStatusError(
|
|
782
|
+
"malformed_result",
|
|
783
|
+
null,
|
|
784
|
+
"Engine setup completed without branching_engine_status in the result."
|
|
785
|
+
);
|
|
768
786
|
}
|
|
769
787
|
if (SUCCESS_ENGINE_STATUSES.has(engineStatus)) {
|
|
770
788
|
return;
|
|
771
789
|
}
|
|
772
790
|
if (RETRYABLE_ENGINE_STATUSES.has(engineStatus)) {
|
|
773
|
-
throw new
|
|
774
|
-
|
|
791
|
+
throw new EngineSetupTerminalStatusError(
|
|
792
|
+
"retryable_engine_status",
|
|
793
|
+
engineStatus,
|
|
794
|
+
`Engine setup needs retry (status: ${engineStatus}). Run \`ardent connector retry-setup ${connectorName}\` to retry, or \`ardent connector list\` to inspect connector state first.`
|
|
775
795
|
);
|
|
776
796
|
}
|
|
777
797
|
if (FAILED_ENGINE_STATUSES.has(engineStatus)) {
|
|
778
|
-
throw new
|
|
798
|
+
throw new EngineSetupTerminalStatusError(
|
|
799
|
+
"failed_engine_status",
|
|
800
|
+
engineStatus,
|
|
779
801
|
`Engine setup failed (status: ${engineStatus}). Run \`ardent connector list\` to inspect connector state.`
|
|
780
802
|
);
|
|
781
803
|
}
|
|
782
|
-
throw new
|
|
804
|
+
throw new EngineSetupTerminalStatusError(
|
|
805
|
+
"unexpected_engine_status",
|
|
806
|
+
engineStatus,
|
|
807
|
+
`Engine setup completed with unexpected status: ${engineStatus}.`
|
|
808
|
+
);
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// src/lib/engine_setup.ts
|
|
812
|
+
var EngineSetupTimeoutError = class extends Error {
|
|
813
|
+
constructor(message) {
|
|
814
|
+
super(message);
|
|
815
|
+
this.name = "EngineSetupTimeoutError";
|
|
816
|
+
}
|
|
817
|
+
};
|
|
818
|
+
var EngineSetupOperationFailedError = class extends Error {
|
|
819
|
+
operationError;
|
|
820
|
+
constructor(operationError, connectorName) {
|
|
821
|
+
super(
|
|
822
|
+
`Engine setup failed: ${operationError ?? "unknown error"}. Inspect connector state with \`ardent connector list\`, then retry with \`ardent connector retry-setup ${connectorName}\` once the underlying issue is resolved.`
|
|
823
|
+
);
|
|
824
|
+
this.name = "EngineSetupOperationFailedError";
|
|
825
|
+
this.operationError = operationError;
|
|
826
|
+
}
|
|
827
|
+
};
|
|
828
|
+
async function runEngineSetupWithPolling(connectorId, connectorName) {
|
|
829
|
+
let dispatch;
|
|
830
|
+
try {
|
|
831
|
+
dispatch = await api.post(
|
|
832
|
+
`/v1/connectors/${connectorId}/engine-setup`,
|
|
833
|
+
{}
|
|
834
|
+
);
|
|
835
|
+
} catch (err) {
|
|
836
|
+
if (isGatewayTimeoutError(err)) {
|
|
837
|
+
throw new Error(
|
|
838
|
+
"Backend dispatch timed out. The engine-setup endpoint is now async and should respond in under a second; this may indicate a backend incident. Try again, or check `ardent connector list` to see if an operation was created anyway."
|
|
839
|
+
);
|
|
840
|
+
}
|
|
841
|
+
throw err;
|
|
842
|
+
}
|
|
843
|
+
const operationId = dispatch?.operation_id;
|
|
844
|
+
if (!operationId) {
|
|
845
|
+
return { dispatched: false };
|
|
846
|
+
}
|
|
847
|
+
const startedAt = Date.now();
|
|
848
|
+
const maxWaitMs = 20 * 60 * 1e3;
|
|
849
|
+
const pollIntervalMs = 5 * 1e3;
|
|
850
|
+
let lastStage = null;
|
|
851
|
+
while (Date.now() - startedAt < maxWaitMs) {
|
|
852
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
853
|
+
let op;
|
|
854
|
+
try {
|
|
855
|
+
op = await api.get(`/v1/operations/${operationId}`);
|
|
856
|
+
} catch (pollErr) {
|
|
857
|
+
if (isGatewayTimeoutError(pollErr)) continue;
|
|
858
|
+
throw pollErr;
|
|
859
|
+
}
|
|
860
|
+
if (op.stage && op.stage !== lastStage) {
|
|
861
|
+
const progressLabel = op.progress != null ? ` (${op.progress}%)` : "";
|
|
862
|
+
console.log(` ${op.stage}${progressLabel}`);
|
|
863
|
+
lastStage = op.stage;
|
|
864
|
+
}
|
|
865
|
+
if (op.status === "completed") {
|
|
866
|
+
assertEngineSetupCompleted(
|
|
867
|
+
{ status: op.status, result: op.result },
|
|
868
|
+
connectorName
|
|
869
|
+
);
|
|
870
|
+
return { dispatched: true };
|
|
871
|
+
}
|
|
872
|
+
if (op.status === "failed") {
|
|
873
|
+
throw new EngineSetupOperationFailedError(op.error, connectorName);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
throw new EngineSetupTimeoutError(
|
|
877
|
+
`Engine setup did not complete within ${maxWaitMs / 6e4} minutes. Setup may still be running server-side \u2014 do NOT delete the connector. Check status with: ardent connector list (operation ${operationId})`
|
|
878
|
+
);
|
|
783
879
|
}
|
|
784
880
|
|
|
785
881
|
// src/lib/onboarding.ts
|
|
@@ -892,63 +988,6 @@ Example:
|
|
|
892
988
|
}
|
|
893
989
|
|
|
894
990
|
// src/commands/connector/create.ts
|
|
895
|
-
var EngineSetupTimeoutError = class extends Error {
|
|
896
|
-
constructor(message) {
|
|
897
|
-
super(message);
|
|
898
|
-
this.name = "EngineSetupTimeoutError";
|
|
899
|
-
}
|
|
900
|
-
};
|
|
901
|
-
async function runEngineSetupWithPolling(connectorId) {
|
|
902
|
-
let dispatch;
|
|
903
|
-
try {
|
|
904
|
-
dispatch = await api.post(
|
|
905
|
-
`/v1/connectors/${connectorId}/engine-setup`,
|
|
906
|
-
{}
|
|
907
|
-
);
|
|
908
|
-
} catch (err) {
|
|
909
|
-
if (isGatewayTimeoutError(err)) {
|
|
910
|
-
throw new Error(
|
|
911
|
-
"Backend dispatch timed out. The engine-setup endpoint is now async and should respond in under a second; this may indicate a backend incident. Try again, or check `ardent connector list` to see if an operation was created anyway."
|
|
912
|
-
);
|
|
913
|
-
}
|
|
914
|
-
throw err;
|
|
915
|
-
}
|
|
916
|
-
const operationId = dispatch?.operation_id;
|
|
917
|
-
if (!operationId) {
|
|
918
|
-
return;
|
|
919
|
-
}
|
|
920
|
-
const startedAt = Date.now();
|
|
921
|
-
const maxWaitMs = 20 * 60 * 1e3;
|
|
922
|
-
const pollIntervalMs = 5 * 1e3;
|
|
923
|
-
let lastStage = null;
|
|
924
|
-
while (Date.now() - startedAt < maxWaitMs) {
|
|
925
|
-
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
926
|
-
let op;
|
|
927
|
-
try {
|
|
928
|
-
op = await api.get(`/v1/operations/${operationId}`);
|
|
929
|
-
} catch (pollErr) {
|
|
930
|
-
if (isGatewayTimeoutError(pollErr)) continue;
|
|
931
|
-
throw pollErr;
|
|
932
|
-
}
|
|
933
|
-
if (op.stage && op.stage !== lastStage) {
|
|
934
|
-
const progressLabel = op.progress != null ? ` (${op.progress}%)` : "";
|
|
935
|
-
console.log(` ${op.stage}${progressLabel}`);
|
|
936
|
-
lastStage = op.stage;
|
|
937
|
-
}
|
|
938
|
-
if (op.status === "completed") {
|
|
939
|
-
assertEngineSetupCompleted({ status: op.status, result: op.result });
|
|
940
|
-
return;
|
|
941
|
-
}
|
|
942
|
-
if (op.status === "failed") {
|
|
943
|
-
throw new Error(
|
|
944
|
-
`Engine setup failed: ${op.error ?? "unknown error"}. Run \`ardent connector list\` to inspect connector state, or re-trigger setup once the underlying issue is resolved.`
|
|
945
|
-
);
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
throw new EngineSetupTimeoutError(
|
|
949
|
-
`Engine setup did not complete within ${maxWaitMs / 6e4} minutes. Setup may still be running server-side \u2014 do NOT delete the connector. Check status with: ardent connector list (operation ${operationId})`
|
|
950
|
-
);
|
|
951
|
-
}
|
|
952
991
|
function parsePostgresUrl(url) {
|
|
953
992
|
const atIndex = url.lastIndexOf("@");
|
|
954
993
|
const credentialsPart = atIndex > 0 ? url.substring(0, atIndex) : url;
|
|
@@ -963,6 +1002,11 @@ function parsePostgresUrl(url) {
|
|
|
963
1002
|
if (!username) throw new Error("Username required in connection URL");
|
|
964
1003
|
return { host, port, username, password };
|
|
965
1004
|
}
|
|
1005
|
+
function printEngineSetupRecoveryHint(connectorName) {
|
|
1006
|
+
console.error("\u2717 Engine setup did not complete for this connector.");
|
|
1007
|
+
console.error(" Inspect: ardent connector list");
|
|
1008
|
+
console.error(` Retry: ardent connector retry-setup ${connectorName}`);
|
|
1009
|
+
}
|
|
966
1010
|
async function createAction2(type, url, options) {
|
|
967
1011
|
const supportedTypes = ["postgresql"];
|
|
968
1012
|
if (!supportedTypes.includes(type.toLowerCase())) {
|
|
@@ -1073,11 +1117,10 @@ async function createAction2(type, url, options) {
|
|
|
1073
1117
|
if (isByoc) {
|
|
1074
1118
|
console.log("Setting up branching engine...");
|
|
1075
1119
|
try {
|
|
1076
|
-
await runEngineSetupWithPolling(connectorId);
|
|
1120
|
+
await runEngineSetupWithPolling(connectorId, connectorName);
|
|
1077
1121
|
} catch (setupErr) {
|
|
1078
1122
|
if (!(setupErr instanceof EngineSetupTimeoutError)) {
|
|
1079
|
-
|
|
1080
|
-
console.error(` ardent connector delete ${connectorName}`);
|
|
1123
|
+
printEngineSetupRecoveryHint(connectorName);
|
|
1081
1124
|
}
|
|
1082
1125
|
throw setupErr;
|
|
1083
1126
|
}
|
|
@@ -1104,7 +1147,14 @@ async function createAction2(type, url, options) {
|
|
|
1104
1147
|
const connector = await api.get(`/v1/connectors/${connectorId}`);
|
|
1105
1148
|
if (connector.branching_engine_status === "configuration_verified") {
|
|
1106
1149
|
console.log("Setting up branching engine...");
|
|
1107
|
-
|
|
1150
|
+
try {
|
|
1151
|
+
await runEngineSetupWithPolling(connectorId, connectorName);
|
|
1152
|
+
} catch (setupErr) {
|
|
1153
|
+
if (!(setupErr instanceof EngineSetupTimeoutError)) {
|
|
1154
|
+
printEngineSetupRecoveryHint(connectorName);
|
|
1155
|
+
}
|
|
1156
|
+
throw setupErr;
|
|
1157
|
+
}
|
|
1108
1158
|
}
|
|
1109
1159
|
}
|
|
1110
1160
|
const finalConnector = await api.get(`/v1/connectors/${connectorId}`);
|
|
@@ -1112,7 +1162,9 @@ async function createAction2(type, url, options) {
|
|
|
1112
1162
|
id: connectorId,
|
|
1113
1163
|
name: connectorName,
|
|
1114
1164
|
service_name: "postgresql",
|
|
1115
|
-
status: finalConnector.branching_engine_status ?? finalConnector.connection_status ?? "pending"
|
|
1165
|
+
status: finalConnector.branching_engine_status ?? finalConnector.connection_status ?? "pending",
|
|
1166
|
+
branching_engine_status: finalConnector.branching_engine_status ?? null,
|
|
1167
|
+
connection_error: finalConnector.connection_error ?? null
|
|
1116
1168
|
};
|
|
1117
1169
|
const cached = getCacheEntry("connectors");
|
|
1118
1170
|
const cachedConnectors = cached?.data || [];
|
|
@@ -1144,6 +1196,34 @@ async function createAction2(type, url, options) {
|
|
|
1144
1196
|
}
|
|
1145
1197
|
}
|
|
1146
1198
|
|
|
1199
|
+
// src/lib/connector_render.ts
|
|
1200
|
+
var GREEN = "\x1B[32m";
|
|
1201
|
+
var CYAN = "\x1B[36m";
|
|
1202
|
+
var RED = "\x1B[31m";
|
|
1203
|
+
function pendingHint(state, connectorName) {
|
|
1204
|
+
if (state === "validating") {
|
|
1205
|
+
return `engine setup running \u2014 re-run \`ardent connector list\` to refresh, or \`ardent connector retry-setup ${connectorName}\` if it stays this way`;
|
|
1206
|
+
}
|
|
1207
|
+
return `engine setup not finished \u2014 run: ardent connector retry-setup ${connectorName}`;
|
|
1208
|
+
}
|
|
1209
|
+
var ENGINE_PENDING_STATES = /* @__PURE__ */ new Set(["configuration_verified", "validating"]);
|
|
1210
|
+
function renderConnectorIcon(connector) {
|
|
1211
|
+
const engine = connector.branching_engine_status;
|
|
1212
|
+
const statusAllowsEnginePending = connector.status === "healthy" || ENGINE_PENDING_STATES.has(connector.status);
|
|
1213
|
+
if (engine && ENGINE_PENDING_STATES.has(engine) && statusAllowsEnginePending) {
|
|
1214
|
+
return {
|
|
1215
|
+
kind: "engine_pending",
|
|
1216
|
+
icon: "\u25D0",
|
|
1217
|
+
color: CYAN,
|
|
1218
|
+
hint: pendingHint(engine, connector.name)
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
if (connector.status === "healthy") {
|
|
1222
|
+
return { kind: "ready", icon: "\u25CF", color: GREEN };
|
|
1223
|
+
}
|
|
1224
|
+
return { kind: "broken", icon: "\u25CB", color: RED };
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1147
1227
|
// src/commands/connector/list.ts
|
|
1148
1228
|
async function listAction2() {
|
|
1149
1229
|
let connectors = [];
|
|
@@ -1198,23 +1278,28 @@ async function listAction2() {
|
|
|
1198
1278
|
const green2 = "\x1B[32m";
|
|
1199
1279
|
const dim2 = "\x1B[2m";
|
|
1200
1280
|
const yellow = "\x1B[33m";
|
|
1201
|
-
const reset2 = "\x1B[0m";
|
|
1202
1281
|
const red = "\x1B[31m";
|
|
1282
|
+
const reset2 = "\x1B[0m";
|
|
1203
1283
|
console.log("Connectors:\n");
|
|
1284
|
+
let enginePendingCount = 0;
|
|
1204
1285
|
for (const connector of connectors) {
|
|
1205
1286
|
const isCurrent = connector.id === currentConnectorId;
|
|
1206
|
-
const
|
|
1287
|
+
const render = renderConnectorIcon(connector);
|
|
1288
|
+
if (render.kind === "engine_pending") enginePendingCount += 1;
|
|
1207
1289
|
const warnings = connector.warnings ?? [];
|
|
1208
1290
|
const warningSuffix = warnings.length > 0 ? ` ${yellow}\u26A0 ${warnings.length}${reset2}` : "";
|
|
1209
|
-
const statusSuffix =
|
|
1291
|
+
const statusSuffix = render.kind === "ready" ? "" : ` ${render.color}[${connector.status}]${reset2}`;
|
|
1292
|
+
const nameLine = isCurrent ? `${green2}* ${render.color}${render.icon}${green2} ${connector.name}${reset2}${warningSuffix}${statusSuffix}` : ` ${render.color}${render.icon}${reset2} ${connector.name}${warningSuffix}${statusSuffix}`;
|
|
1293
|
+
console.log(nameLine);
|
|
1210
1294
|
if (isCurrent) {
|
|
1211
|
-
console.log(`${green2}* ${icon} ${connector.name}${reset2}${warningSuffix}${statusSuffix}`);
|
|
1212
1295
|
console.log(`${green2} ${connector.service_name}${reset2}`);
|
|
1213
1296
|
} else {
|
|
1214
|
-
console.log(` ${icon} ${connector.name}${warningSuffix}${statusSuffix}`);
|
|
1215
1297
|
console.log(`${dim2} ${connector.service_name}${reset2}`);
|
|
1216
1298
|
}
|
|
1217
|
-
if (
|
|
1299
|
+
if (render.kind === "engine_pending") {
|
|
1300
|
+
console.log(`${render.color} ${render.hint}${reset2}`);
|
|
1301
|
+
}
|
|
1302
|
+
if (render.kind === "broken" && connector.connection_error) {
|
|
1218
1303
|
for (const line of connector.connection_error.split("\n")) {
|
|
1219
1304
|
if (line.trim().length === 0) continue;
|
|
1220
1305
|
console.log(`${red} ${line}${reset2}`);
|
|
@@ -1225,10 +1310,16 @@ async function listAction2() {
|
|
|
1225
1310
|
}
|
|
1226
1311
|
console.log();
|
|
1227
1312
|
}
|
|
1313
|
+
if (enginePendingCount > 0) {
|
|
1314
|
+
trackEvent("CLI: connector list rendered engine-pending", {
|
|
1315
|
+
engine_pending_count: enginePendingCount,
|
|
1316
|
+
connector_count: connectors.length
|
|
1317
|
+
});
|
|
1318
|
+
}
|
|
1228
1319
|
}
|
|
1229
1320
|
|
|
1230
1321
|
// src/commands/connector/delete.ts
|
|
1231
|
-
async function deleteAction2(name) {
|
|
1322
|
+
async function deleteAction2(name, options = {}) {
|
|
1232
1323
|
const cached = getCacheEntry("connectors");
|
|
1233
1324
|
let connector = cached?.data.find((c) => c.name === name);
|
|
1234
1325
|
if (!connector) {
|
|
@@ -1253,13 +1344,15 @@ async function deleteAction2(name) {
|
|
|
1253
1344
|
}
|
|
1254
1345
|
try {
|
|
1255
1346
|
console.log("Deleting connector...");
|
|
1256
|
-
|
|
1347
|
+
const force = options.force ?? false;
|
|
1348
|
+
const path = force ? `/v1/connectors/${connector.id}?force=true` : `/v1/connectors/${connector.id}`;
|
|
1349
|
+
await api.delete(path);
|
|
1257
1350
|
const currentCache = getCacheEntry("connectors");
|
|
1258
1351
|
if (currentCache?.data) {
|
|
1259
1352
|
const updatedConnectors = currentCache.data.filter((c) => c.id !== connector.id);
|
|
1260
1353
|
setCacheEntry("connectors", updatedConnectors);
|
|
1261
1354
|
}
|
|
1262
|
-
trackEvent("CLI: connector delete succeeded");
|
|
1355
|
+
trackEvent("CLI: connector delete succeeded", { force });
|
|
1263
1356
|
console.log("\u2713 Connector deleted");
|
|
1264
1357
|
} catch (err) {
|
|
1265
1358
|
if (isNetworkError(err)) {
|
|
@@ -1276,7 +1369,114 @@ async function deleteAction2(name) {
|
|
|
1276
1369
|
console.error(" \u2022 Upgrade your role to Admin");
|
|
1277
1370
|
process.exit(1);
|
|
1278
1371
|
}
|
|
1372
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1373
|
+
const isDrainRefusal = message.includes("Branch still has un-replicated changes") || typeof err === "object" && err !== null && "status" in err && err.status === 409;
|
|
1374
|
+
if (isDrainRefusal) {
|
|
1375
|
+
trackEvent("CLI: connector delete failed", { reason: "drain_refused" });
|
|
1376
|
+
console.error(`\u2717 ${message}`);
|
|
1377
|
+
process.exit(1);
|
|
1378
|
+
}
|
|
1279
1379
|
trackEvent("CLI: connector delete failed", { reason: "api_error" });
|
|
1380
|
+
console.error("\u2717 Failed:", message);
|
|
1381
|
+
process.exit(1);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
// src/commands/connector/retry-setup.ts
|
|
1386
|
+
function connectorRetrySetupFailureTelemetry(err) {
|
|
1387
|
+
if (isPermissionError(err)) {
|
|
1388
|
+
return { reason: "permission_denied" };
|
|
1389
|
+
}
|
|
1390
|
+
if (err instanceof EngineSetupTimeoutError) {
|
|
1391
|
+
return { reason: "polling_timeout" };
|
|
1392
|
+
}
|
|
1393
|
+
if (err instanceof EngineSetupOperationFailedError) {
|
|
1394
|
+
return {
|
|
1395
|
+
reason: "operation_failed",
|
|
1396
|
+
operation_error: err.operationError ?? "unknown error"
|
|
1397
|
+
};
|
|
1398
|
+
}
|
|
1399
|
+
if (err instanceof EngineSetupTerminalStatusError) {
|
|
1400
|
+
return {
|
|
1401
|
+
reason: err.kind,
|
|
1402
|
+
engine_status: err.engineStatus
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1405
|
+
return { reason: "api_error" };
|
|
1406
|
+
}
|
|
1407
|
+
async function retrySetupAction(name) {
|
|
1408
|
+
const currentProjectId = getConfig("currentProjectId");
|
|
1409
|
+
if (!currentProjectId) {
|
|
1410
|
+
console.error("\u2717 No current project set. Switch to a project first:");
|
|
1411
|
+
console.error(" ardent project list");
|
|
1412
|
+
console.error(" ardent project switch <name>");
|
|
1413
|
+
process.exit(1);
|
|
1414
|
+
}
|
|
1415
|
+
let connector;
|
|
1416
|
+
try {
|
|
1417
|
+
const result = await api.get(
|
|
1418
|
+
`/v1/cli/connectors?project_id=${currentProjectId}`
|
|
1419
|
+
);
|
|
1420
|
+
if (!result.connectors) {
|
|
1421
|
+
throw new Error("API returned invalid response: missing connectors array");
|
|
1422
|
+
}
|
|
1423
|
+
setCacheEntry("connectors", result.connectors);
|
|
1424
|
+
connector = result.connectors.find((candidate) => candidate.name === name);
|
|
1425
|
+
} catch (err) {
|
|
1426
|
+
if (isNetworkError(err)) {
|
|
1427
|
+
console.error("\u2717 Cannot run connector retry-setup while offline");
|
|
1428
|
+
process.exit(1);
|
|
1429
|
+
}
|
|
1430
|
+
throw err;
|
|
1431
|
+
}
|
|
1432
|
+
if (!connector) {
|
|
1433
|
+
console.error(`\u2717 Connector "${name}" not found`);
|
|
1434
|
+
console.log(" Run: ardent connector list");
|
|
1435
|
+
process.exit(1);
|
|
1436
|
+
}
|
|
1437
|
+
trackEvent("CLI: connector retry-setup initiated", {
|
|
1438
|
+
connector_id: connector.id,
|
|
1439
|
+
starting_engine_status: connector.branching_engine_status ?? null
|
|
1440
|
+
});
|
|
1441
|
+
console.log(`Setting up branching engine for ${name}...`);
|
|
1442
|
+
try {
|
|
1443
|
+
const { dispatched } = await runEngineSetupWithPolling(connector.id, name);
|
|
1444
|
+
if (dispatched) {
|
|
1445
|
+
trackEvent("CLI: connector retry-setup dispatched", {
|
|
1446
|
+
connector_id: connector.id
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
trackEvent("CLI: connector retry-setup succeeded", {
|
|
1450
|
+
connector_id: connector.id,
|
|
1451
|
+
dispatched
|
|
1452
|
+
});
|
|
1453
|
+
try {
|
|
1454
|
+
const refreshed = await api.get(
|
|
1455
|
+
`/v1/cli/connectors?project_id=${currentProjectId}`
|
|
1456
|
+
);
|
|
1457
|
+
if (refreshed.connectors) {
|
|
1458
|
+
setCacheEntry("connectors", refreshed.connectors);
|
|
1459
|
+
}
|
|
1460
|
+
} catch {
|
|
1461
|
+
}
|
|
1462
|
+
if (!dispatched) {
|
|
1463
|
+
console.log("\u2713 Engine already set up \u2014 no work needed");
|
|
1464
|
+
} else {
|
|
1465
|
+
console.log("\u2713 Engine setup complete");
|
|
1466
|
+
}
|
|
1467
|
+
} catch (err) {
|
|
1468
|
+
const failureTelemetry = connectorRetrySetupFailureTelemetry(err);
|
|
1469
|
+
if (isPermissionError(err)) {
|
|
1470
|
+
trackEvent("CLI: connector retry-setup failed", failureTelemetry);
|
|
1471
|
+
console.error("\u2717 You don't have permission to run engine setup on this connector.");
|
|
1472
|
+
process.exit(1);
|
|
1473
|
+
}
|
|
1474
|
+
if (err instanceof EngineSetupTimeoutError) {
|
|
1475
|
+
trackEvent("CLI: connector retry-setup failed", failureTelemetry);
|
|
1476
|
+
console.error(`\u2717 ${err.message}`);
|
|
1477
|
+
process.exit(1);
|
|
1478
|
+
}
|
|
1479
|
+
trackEvent("CLI: connector retry-setup failed", failureTelemetry);
|
|
1280
1480
|
console.error("\u2717 Failed:", err instanceof Error ? err.message : err);
|
|
1281
1481
|
process.exit(1);
|
|
1282
1482
|
}
|
|
@@ -1355,7 +1555,13 @@ connectorCommand.command("create <type> [url]").description("Create a new connec
|
|
|
1355
1555
|
).action(createAction2);
|
|
1356
1556
|
connectorCommand.command("list").description("List your connectors").action(listAction2);
|
|
1357
1557
|
connectorCommand.command("switch <name>").description("Switch to a different connector").action(switchAction2);
|
|
1358
|
-
connectorCommand.command("
|
|
1558
|
+
connectorCommand.command("retry-setup <name>").description(
|
|
1559
|
+
"Retry branching engine setup for a connector that didn't finish (status: configuration_verified or validating)"
|
|
1560
|
+
).action(retrySetupAction);
|
|
1561
|
+
connectorCommand.command("delete <name>").description("Delete a connector by name").option(
|
|
1562
|
+
"--force",
|
|
1563
|
+
"Skip the in-flight WAL/Kafka drain wait. Any un-replicated changes are abandoned and recorded for operator triage."
|
|
1564
|
+
).action(deleteAction2);
|
|
1359
1565
|
|
|
1360
1566
|
// src/commands/invite/index.ts
|
|
1361
1567
|
import { Command as Command3 } from "commander";
|
|
@@ -2283,6 +2489,7 @@ CONNECTORS
|
|
|
2283
2489
|
connector create Connect a database (postgresql, snowflake, etc.)
|
|
2284
2490
|
connector list List your connectors (* = current)
|
|
2285
2491
|
connector switch Switch to a different connector
|
|
2492
|
+
connector retry-setup Retry branching engine setup for a connector
|
|
2286
2493
|
connector delete Delete a connector
|
|
2287
2494
|
|
|
2288
2495
|
BRANCHES
|