ardent-cli 0.0.48 → 0.0.50
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 +65 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,6 +6,9 @@ import { Command as Command8 } from "commander";
|
|
|
6
6
|
// src/commands/branch/index.ts
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
|
|
9
|
+
// src/commands/branch/create.ts
|
|
10
|
+
import { randomUUID as randomUUID2 } from "crypto";
|
|
11
|
+
|
|
9
12
|
// src/lib/config.ts
|
|
10
13
|
import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync, unlinkSync } from "fs";
|
|
11
14
|
import { homedir } from "os";
|
|
@@ -137,6 +140,24 @@ function getCacheEntry(key) {
|
|
|
137
140
|
const config = loadConfig();
|
|
138
141
|
return config.cache?.[key];
|
|
139
142
|
}
|
|
143
|
+
function getPendingBranchCreateKey(scopeKey) {
|
|
144
|
+
return getConfig("pendingBranchCreateKeys")?.[scopeKey];
|
|
145
|
+
}
|
|
146
|
+
function setPendingBranchCreateKey(scopeKey, idempotencyKey) {
|
|
147
|
+
const config = loadConfig();
|
|
148
|
+
if (!config.pendingBranchCreateKeys) {
|
|
149
|
+
config.pendingBranchCreateKeys = {};
|
|
150
|
+
}
|
|
151
|
+
config.pendingBranchCreateKeys[scopeKey] = idempotencyKey;
|
|
152
|
+
saveConfig(config);
|
|
153
|
+
}
|
|
154
|
+
function clearPendingBranchCreateKey(scopeKey) {
|
|
155
|
+
const config = loadConfig();
|
|
156
|
+
if (config.pendingBranchCreateKeys && scopeKey in config.pendingBranchCreateKeys) {
|
|
157
|
+
delete config.pendingBranchCreateKeys[scopeKey];
|
|
158
|
+
saveConfig(config);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
140
161
|
function formatIdle(lastActivityIso) {
|
|
141
162
|
if (!lastActivityIso) {
|
|
142
163
|
return "unknown";
|
|
@@ -316,11 +337,14 @@ var ApiClient = class {
|
|
|
316
337
|
});
|
|
317
338
|
return this.handleResponse(response);
|
|
318
339
|
}
|
|
319
|
-
async post(path, body) {
|
|
340
|
+
async post(path, body, extraHeaders = {}) {
|
|
320
341
|
const url = `${getApiUrl()}${path}`;
|
|
321
342
|
const response = await fetch(url, {
|
|
322
343
|
method: "POST",
|
|
323
|
-
headers:
|
|
344
|
+
headers: {
|
|
345
|
+
...this.getHeaders(),
|
|
346
|
+
...extraHeaders
|
|
347
|
+
},
|
|
324
348
|
body: JSON.stringify(body)
|
|
325
349
|
});
|
|
326
350
|
return this.handleResponse(response);
|
|
@@ -384,6 +408,15 @@ function isGatewayTimeoutError(err) {
|
|
|
384
408
|
}
|
|
385
409
|
return isNetworkError(err);
|
|
386
410
|
}
|
|
411
|
+
function isTransientOperationPollError(err) {
|
|
412
|
+
if (isGatewayTimeoutError(err)) {
|
|
413
|
+
return true;
|
|
414
|
+
}
|
|
415
|
+
if (!(err instanceof Error)) {
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
return err.message.trim().toLowerCase() === "api error 500: internal server error";
|
|
419
|
+
}
|
|
387
420
|
|
|
388
421
|
// src/lib/connector_selection.ts
|
|
389
422
|
function clearSelectedConnector() {
|
|
@@ -672,16 +705,24 @@ async function createAction(name, options) {
|
|
|
672
705
|
console.error(`\u2717 ${message}`);
|
|
673
706
|
process.exit(1);
|
|
674
707
|
}
|
|
708
|
+
let idempotencyScopeKey;
|
|
675
709
|
try {
|
|
676
710
|
const startTime = performance.now();
|
|
677
711
|
const connectorId = await resolveCurrentConnectorId();
|
|
712
|
+
idempotencyScopeKey = `${connectorId}:${options.service}:${name}`;
|
|
713
|
+
let idempotencyKey = getPendingBranchCreateKey(idempotencyScopeKey);
|
|
714
|
+
if (!idempotencyKey) {
|
|
715
|
+
idempotencyKey = randomUUID2();
|
|
716
|
+
setPendingBranchCreateKey(idempotencyScopeKey, idempotencyKey);
|
|
717
|
+
}
|
|
678
718
|
const createResponse = await api.post(
|
|
679
719
|
"/v1/branch/create",
|
|
680
720
|
{
|
|
681
721
|
connector_id: connectorId,
|
|
682
722
|
service_type: options.service,
|
|
683
723
|
name
|
|
684
|
-
}
|
|
724
|
+
},
|
|
725
|
+
{ "X-Idempotency-Key": idempotencyKey }
|
|
685
726
|
);
|
|
686
727
|
const warning = createResponse?.warning;
|
|
687
728
|
const response = await api.get(`/v1/cli/branches?connector_id=${connectorId}`);
|
|
@@ -737,6 +778,7 @@ async function createAction(name, options) {
|
|
|
737
778
|
cachedBranches.push(branch);
|
|
738
779
|
setCacheEntry("branches", cachedBranches);
|
|
739
780
|
setCurrentBranch(name);
|
|
781
|
+
clearPendingBranchCreateKey(idempotencyScopeKey);
|
|
740
782
|
const elapsed = ((performance.now() - startTime) / 1e3).toFixed(1);
|
|
741
783
|
trackEvent("CLI: branch create succeeded", {
|
|
742
784
|
service_type: options.service,
|
|
@@ -785,6 +827,9 @@ ${url}`);
|
|
|
785
827
|
console.error("\u2717 Cannot create branch while offline");
|
|
786
828
|
process.exit(1);
|
|
787
829
|
}
|
|
830
|
+
if (idempotencyScopeKey) {
|
|
831
|
+
clearPendingBranchCreateKey(idempotencyScopeKey);
|
|
832
|
+
}
|
|
788
833
|
trackEvent("CLI: branch create failed", { reason: "api_error", output_mode: mode });
|
|
789
834
|
const message = err instanceof Error ? err.message : String(err);
|
|
790
835
|
if (mode === "json") {
|
|
@@ -1306,6 +1351,9 @@ function assertEngineSetupCompleted(operation, connectorName) {
|
|
|
1306
1351
|
}
|
|
1307
1352
|
|
|
1308
1353
|
// src/lib/engine_setup.ts
|
|
1354
|
+
var ENGINE_SETUP_MAX_WAIT_MS = 60 * 60 * 1e3;
|
|
1355
|
+
var ENGINE_SETUP_POLL_INTERVAL_MS = 5 * 1e3;
|
|
1356
|
+
var ENGINE_SETUP_TRANSIENT_WARN_EVERY = 6;
|
|
1309
1357
|
var EngineSetupTimeoutError = class extends Error {
|
|
1310
1358
|
constructor(message) {
|
|
1311
1359
|
super(message);
|
|
@@ -1322,7 +1370,7 @@ var EngineSetupOperationFailedError = class extends Error {
|
|
|
1322
1370
|
this.operationError = operationError;
|
|
1323
1371
|
}
|
|
1324
1372
|
};
|
|
1325
|
-
async function runEngineSetupWithPolling(connectorId, connectorName) {
|
|
1373
|
+
async function runEngineSetupWithPolling(connectorId, connectorName, options = {}) {
|
|
1326
1374
|
let dispatch;
|
|
1327
1375
|
try {
|
|
1328
1376
|
dispatch = await api.post(
|
|
@@ -1342,18 +1390,28 @@ async function runEngineSetupWithPolling(connectorId, connectorName) {
|
|
|
1342
1390
|
return { dispatched: false };
|
|
1343
1391
|
}
|
|
1344
1392
|
const startedAt = Date.now();
|
|
1345
|
-
const maxWaitMs =
|
|
1346
|
-
const pollIntervalMs =
|
|
1393
|
+
const maxWaitMs = options.maxWaitMs ?? ENGINE_SETUP_MAX_WAIT_MS;
|
|
1394
|
+
const pollIntervalMs = options.pollIntervalMs ?? ENGINE_SETUP_POLL_INTERVAL_MS;
|
|
1347
1395
|
let lastStage = null;
|
|
1396
|
+
let consecutiveTransientFailures = 0;
|
|
1348
1397
|
while (Date.now() - startedAt < maxWaitMs) {
|
|
1349
1398
|
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
1350
1399
|
let op;
|
|
1351
1400
|
try {
|
|
1352
1401
|
op = await api.get(`/v1/operations/${operationId}`);
|
|
1353
1402
|
} catch (pollErr) {
|
|
1354
|
-
if (
|
|
1403
|
+
if (isTransientOperationPollError(pollErr)) {
|
|
1404
|
+
consecutiveTransientFailures += 1;
|
|
1405
|
+
if (consecutiveTransientFailures % ENGINE_SETUP_TRANSIENT_WARN_EVERY === 0) {
|
|
1406
|
+
console.warn(
|
|
1407
|
+
` \u26A0 Status check has failed ${consecutiveTransientFailures} times in a row. Setup is still running server-side and the CLI will keep waiting \u2014 do NOT delete the connector.`
|
|
1408
|
+
);
|
|
1409
|
+
}
|
|
1410
|
+
continue;
|
|
1411
|
+
}
|
|
1355
1412
|
throw pollErr;
|
|
1356
1413
|
}
|
|
1414
|
+
consecutiveTransientFailures = 0;
|
|
1357
1415
|
if (op.stage && op.stage !== lastStage) {
|
|
1358
1416
|
const progressLabel = op.progress != null ? ` (${op.progress}%)` : "";
|
|
1359
1417
|
console.log(` ${operationStageDisplay(op.stage)}${progressLabel}`);
|