ardent-cli 0.0.19 → 0.0.21
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 +332 -62
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -59,7 +59,9 @@ function getToken() {
|
|
|
59
59
|
}
|
|
60
60
|
function setCacheEntry(key, data) {
|
|
61
61
|
const config = loadConfig();
|
|
62
|
-
if (!config.cache)
|
|
62
|
+
if (!config.cache) {
|
|
63
|
+
config.cache = {};
|
|
64
|
+
}
|
|
63
65
|
config.cache[key] = {
|
|
64
66
|
data,
|
|
65
67
|
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -75,10 +77,16 @@ function formatCacheTime(isoString) {
|
|
|
75
77
|
const now = /* @__PURE__ */ new Date();
|
|
76
78
|
const diffMs = now.getTime() - date.getTime();
|
|
77
79
|
const diffMins = Math.floor(diffMs / 6e4);
|
|
78
|
-
if (diffMins < 1)
|
|
79
|
-
|
|
80
|
+
if (diffMins < 1) {
|
|
81
|
+
return "just now";
|
|
82
|
+
}
|
|
83
|
+
if (diffMins < 60) {
|
|
84
|
+
return `${diffMins}m ago`;
|
|
85
|
+
}
|
|
80
86
|
const diffHours = Math.floor(diffMins / 60);
|
|
81
|
-
if (diffHours < 24)
|
|
87
|
+
if (diffHours < 24) {
|
|
88
|
+
return `${diffHours}h ago`;
|
|
89
|
+
}
|
|
82
90
|
return date.toLocaleDateString();
|
|
83
91
|
}
|
|
84
92
|
function getCurrentBranch() {
|
|
@@ -94,42 +102,84 @@ function clearCurrentBranch() {
|
|
|
94
102
|
}
|
|
95
103
|
async function bootstrapCache() {
|
|
96
104
|
const token = getToken();
|
|
97
|
-
if (!token)
|
|
105
|
+
if (!token) {
|
|
106
|
+
throw new Error("Not authenticated");
|
|
107
|
+
}
|
|
108
|
+
const staleConfig = loadConfig();
|
|
109
|
+
delete staleConfig.user;
|
|
110
|
+
delete staleConfig.userId;
|
|
111
|
+
delete staleConfig.currentProjectId;
|
|
112
|
+
delete staleConfig.currentProjectName;
|
|
113
|
+
delete staleConfig.currentConnectorId;
|
|
114
|
+
delete staleConfig.currentConnectorName;
|
|
115
|
+
delete staleConfig.currentBranch;
|
|
116
|
+
delete staleConfig.cache;
|
|
117
|
+
saveConfig(staleConfig);
|
|
98
118
|
const apiUrl = getApiUrl();
|
|
99
119
|
const headers = {
|
|
100
120
|
"Content-Type": "application/json",
|
|
101
121
|
"Authorization": `Bearer ${token}`
|
|
102
122
|
};
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
123
|
+
const userInfoResponse = await fetch(`${apiUrl}/v1/cli/me`, { headers });
|
|
124
|
+
if (!userInfoResponse.ok) {
|
|
125
|
+
throw new Error("Failed to fetch user info");
|
|
126
|
+
}
|
|
127
|
+
const user = await userInfoResponse.json();
|
|
128
|
+
setConfig("user", user);
|
|
129
|
+
const bootstrap = { projects: [], connectors: [], branches: [] };
|
|
130
|
+
if (!user.org_id) {
|
|
131
|
+
return bootstrap;
|
|
132
|
+
}
|
|
133
|
+
const projectsResponse = await fetch(`${apiUrl}/v1/projects?org_id=${user.org_id}`, { headers });
|
|
134
|
+
if (projectsResponse.ok) {
|
|
135
|
+
const projectsPayload = await projectsResponse.json();
|
|
136
|
+
if (!Array.isArray(projectsPayload.projects)) {
|
|
137
|
+
throw new Error("API returned invalid response: missing projects array");
|
|
138
|
+
}
|
|
139
|
+
bootstrap.projects = projectsPayload.projects;
|
|
140
|
+
setCacheEntry("projects", bootstrap.projects);
|
|
114
141
|
}
|
|
115
|
-
if (
|
|
116
|
-
const
|
|
117
|
-
setConfig("
|
|
142
|
+
if (bootstrap.projects.length === 1) {
|
|
143
|
+
const project = bootstrap.projects[0];
|
|
144
|
+
setConfig("currentProjectId", project.id);
|
|
145
|
+
setConfig("currentProjectName", project.name);
|
|
146
|
+
}
|
|
147
|
+
const currentProjectId = getConfig("currentProjectId");
|
|
148
|
+
if (currentProjectId) {
|
|
149
|
+
const connectorsResponse = await fetch(
|
|
150
|
+
`${apiUrl}/v1/cli/connectors?project_id=${currentProjectId}`,
|
|
151
|
+
{ headers }
|
|
152
|
+
);
|
|
153
|
+
if (connectorsResponse.ok) {
|
|
154
|
+
const connectorsPayload = await connectorsResponse.json();
|
|
155
|
+
if (!Array.isArray(connectorsPayload.connectors)) {
|
|
156
|
+
throw new Error("API returned invalid response: missing connectors array");
|
|
157
|
+
}
|
|
158
|
+
bootstrap.connectors = connectorsPayload.connectors;
|
|
159
|
+
setCacheEntry("connectors", bootstrap.connectors);
|
|
160
|
+
}
|
|
161
|
+
if (bootstrap.connectors.length === 1) {
|
|
162
|
+
const connector = bootstrap.connectors[0];
|
|
163
|
+
setConfig("currentConnectorId", connector.id);
|
|
164
|
+
setConfig("currentConnectorName", connector.name);
|
|
165
|
+
}
|
|
118
166
|
}
|
|
119
167
|
const currentConnectorId = getConfig("currentConnectorId");
|
|
120
168
|
if (currentConnectorId) {
|
|
121
|
-
const
|
|
169
|
+
const branchesResponse = await fetch(
|
|
122
170
|
`${apiUrl}/v1/cli/branches?connector_id=${currentConnectorId}`,
|
|
123
171
|
{ headers }
|
|
124
172
|
);
|
|
125
|
-
if (
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
173
|
+
if (branchesResponse.ok) {
|
|
174
|
+
const branchesPayload = await branchesResponse.json();
|
|
175
|
+
if (!Array.isArray(branchesPayload.branches)) {
|
|
176
|
+
throw new Error("API returned invalid response: missing branches array");
|
|
177
|
+
}
|
|
178
|
+
bootstrap.branches = branchesPayload.branches;
|
|
179
|
+
setCacheEntry("branches", bootstrap.branches);
|
|
130
180
|
}
|
|
131
181
|
}
|
|
132
|
-
return
|
|
182
|
+
return bootstrap;
|
|
133
183
|
}
|
|
134
184
|
|
|
135
185
|
// src/lib/api.ts
|
|
@@ -257,6 +307,19 @@ function isPermissionError(err) {
|
|
|
257
307
|
}
|
|
258
308
|
return false;
|
|
259
309
|
}
|
|
310
|
+
var API_ERROR_PREFIX = /^api error \d{3}:/;
|
|
311
|
+
function isGatewayTimeoutError(err) {
|
|
312
|
+
if (err instanceof Error) {
|
|
313
|
+
const msg = err.message.toLowerCase();
|
|
314
|
+
if (msg.includes("api error 502") || msg.includes("api error 503") || msg.includes("api error 504")) {
|
|
315
|
+
return true;
|
|
316
|
+
}
|
|
317
|
+
if (API_ERROR_PREFIX.test(msg)) {
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return isNetworkError(err);
|
|
322
|
+
}
|
|
260
323
|
|
|
261
324
|
// src/lib/telemetry.ts
|
|
262
325
|
import { randomUUID } from "crypto";
|
|
@@ -679,6 +742,52 @@ Example:
|
|
|
679
742
|
}
|
|
680
743
|
|
|
681
744
|
// src/commands/connector/create.ts
|
|
745
|
+
var EngineSetupTimeoutError = class extends Error {
|
|
746
|
+
constructor(message) {
|
|
747
|
+
super(message);
|
|
748
|
+
this.name = "EngineSetupTimeoutError";
|
|
749
|
+
}
|
|
750
|
+
};
|
|
751
|
+
async function runEngineSetupWithPolling(connectorId) {
|
|
752
|
+
try {
|
|
753
|
+
await api.post(`/v1/connectors/${connectorId}/engine-setup`, {});
|
|
754
|
+
return;
|
|
755
|
+
} catch (err) {
|
|
756
|
+
if (!isGatewayTimeoutError(err)) {
|
|
757
|
+
throw err;
|
|
758
|
+
}
|
|
759
|
+
console.log(
|
|
760
|
+
" Backend engine setup is long-running; the gateway dropped the request but the work continues server-side. Polling for completion..."
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
const startedAt = Date.now();
|
|
764
|
+
const maxWaitMs = 20 * 60 * 1e3;
|
|
765
|
+
const pollIntervalMs = 30 * 1e3;
|
|
766
|
+
while (Date.now() - startedAt < maxWaitMs) {
|
|
767
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
768
|
+
let status;
|
|
769
|
+
try {
|
|
770
|
+
status = await api.get(`/v1/connectors/${connectorId}`);
|
|
771
|
+
} catch (pollErr) {
|
|
772
|
+
if (isGatewayTimeoutError(pollErr)) continue;
|
|
773
|
+
throw pollErr;
|
|
774
|
+
}
|
|
775
|
+
const engineStatus = status.branching_engine_status;
|
|
776
|
+
if (engineStatus === "healthy" || engineStatus === "degraded") {
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
if (engineStatus === "configuration_failed" || engineStatus === "failed_validation") {
|
|
780
|
+
throw new Error(
|
|
781
|
+
`Engine setup failed (status: ${engineStatus}). Check connector status: ardent connector list`
|
|
782
|
+
);
|
|
783
|
+
}
|
|
784
|
+
const elapsedSec = Math.round((Date.now() - startedAt) / 1e3);
|
|
785
|
+
console.log(` Still setting up... (${elapsedSec}s elapsed, status=${engineStatus ?? "unknown"})`);
|
|
786
|
+
}
|
|
787
|
+
throw new EngineSetupTimeoutError(
|
|
788
|
+
`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`
|
|
789
|
+
);
|
|
790
|
+
}
|
|
682
791
|
function parsePostgresUrl(url) {
|
|
683
792
|
const atIndex = url.lastIndexOf("@");
|
|
684
793
|
const credentialsPart = atIndex > 0 ? url.substring(0, atIndex) : url;
|
|
@@ -803,10 +912,12 @@ async function createAction2(type, url, options) {
|
|
|
803
912
|
if (isByoc) {
|
|
804
913
|
console.log("Setting up branching engine...");
|
|
805
914
|
try {
|
|
806
|
-
await
|
|
915
|
+
await runEngineSetupWithPolling(connectorId);
|
|
807
916
|
} catch (setupErr) {
|
|
808
|
-
|
|
809
|
-
|
|
917
|
+
if (!(setupErr instanceof EngineSetupTimeoutError)) {
|
|
918
|
+
console.error("\u2717 Engine setup failed. To retry, delete and recreate:");
|
|
919
|
+
console.error(` ardent connector delete ${connectorName}`);
|
|
920
|
+
}
|
|
810
921
|
throw setupErr;
|
|
811
922
|
}
|
|
812
923
|
} else {
|
|
@@ -832,7 +943,7 @@ async function createAction2(type, url, options) {
|
|
|
832
943
|
const connector = await api.get(`/v1/connectors/${connectorId}`);
|
|
833
944
|
if (connector.branching_engine_status === "configuration_verified") {
|
|
834
945
|
console.log("Setting up branching engine...");
|
|
835
|
-
await
|
|
946
|
+
await runEngineSetupWithPolling(connectorId);
|
|
836
947
|
}
|
|
837
948
|
}
|
|
838
949
|
const finalConnector = await api.get(`/v1/connectors/${connectorId}`);
|
|
@@ -915,8 +1026,10 @@ async function listAction2() {
|
|
|
915
1026
|
`);
|
|
916
1027
|
}
|
|
917
1028
|
if (connectors.length === 0) {
|
|
1029
|
+
const green3 = "\x1B[32m";
|
|
1030
|
+
const reset3 = "\x1B[0m";
|
|
918
1031
|
console.log("No connectors found");
|
|
919
|
-
console.log(
|
|
1032
|
+
console.log(`${green3} Create one with: ardent connector create postgresql <url>${reset3}`);
|
|
920
1033
|
return;
|
|
921
1034
|
}
|
|
922
1035
|
const currentConnectorId = getConfig("currentConnectorId");
|
|
@@ -1290,9 +1403,11 @@ async function createAction3(name) {
|
|
|
1290
1403
|
setCacheEntry("projects", cachedProjects);
|
|
1291
1404
|
setConfig("currentProjectId", project.id);
|
|
1292
1405
|
setConfig("currentProjectName", project.name);
|
|
1406
|
+
setConfig("currentConnectorId", void 0);
|
|
1407
|
+
setConfig("currentConnectorName", void 0);
|
|
1408
|
+
clearCurrentBranch();
|
|
1293
1409
|
trackEvent("CLI: project create succeeded");
|
|
1294
1410
|
console.log(`\u2713 Project '${name}' created`);
|
|
1295
|
-
console.log(` ID: ${project.id}`);
|
|
1296
1411
|
} catch (err) {
|
|
1297
1412
|
if (isNetworkError(err)) {
|
|
1298
1413
|
trackEvent("CLI: project create failed", { reason: "offline" });
|
|
@@ -1368,10 +1483,8 @@ async function listAction4() {
|
|
|
1368
1483
|
const isCurrent = project.id === currentProjectId;
|
|
1369
1484
|
if (isCurrent) {
|
|
1370
1485
|
console.log(`${green2}* ${project.name}${reset2}`);
|
|
1371
|
-
console.log(`${green2} ${project.id}${reset2}`);
|
|
1372
1486
|
} else {
|
|
1373
1487
|
console.log(` ${project.name}`);
|
|
1374
|
-
console.log(`${dim2} ${project.id}${reset2}`);
|
|
1375
1488
|
}
|
|
1376
1489
|
console.log();
|
|
1377
1490
|
}
|
|
@@ -1434,9 +1547,114 @@ async function switchAction3(name) {
|
|
|
1434
1547
|
} else {
|
|
1435
1548
|
console.log(`Switched to project '${name}'`);
|
|
1436
1549
|
}
|
|
1550
|
+
try {
|
|
1551
|
+
const result = await api.get(`/v1/cli/connectors?project_id=${project.id}`);
|
|
1552
|
+
if (!Array.isArray(result.connectors)) {
|
|
1553
|
+
throw new Error("API returned invalid response: missing connectors array");
|
|
1554
|
+
}
|
|
1555
|
+
const connectors = result.connectors;
|
|
1556
|
+
setCacheEntry("connectors", connectors);
|
|
1557
|
+
if (connectors.length === 1) {
|
|
1558
|
+
const connector = connectors[0];
|
|
1559
|
+
setConfig("currentConnectorId", connector.id);
|
|
1560
|
+
setConfig("currentConnectorName", connector.name);
|
|
1561
|
+
console.log("");
|
|
1562
|
+
console.log(` Auto-selected connector '${connector.name}'`);
|
|
1563
|
+
console.log("");
|
|
1564
|
+
} else if (connectors.length === 0) {
|
|
1565
|
+
const green2 = "\x1B[32m";
|
|
1566
|
+
const reset2 = "\x1B[0m";
|
|
1567
|
+
console.log("");
|
|
1568
|
+
console.log(" No connectors found.");
|
|
1569
|
+
console.log(`${green2} Create one with: ardent connector create postgresql <url>${reset2}`);
|
|
1570
|
+
console.log("");
|
|
1571
|
+
}
|
|
1572
|
+
} catch (err) {
|
|
1573
|
+
let reason;
|
|
1574
|
+
if (isNetworkError(err)) {
|
|
1575
|
+
reason = "offline";
|
|
1576
|
+
console.error(" \u26A0 Couldn't fetch connectors (offline). Run 'ardent connector list' when online.");
|
|
1577
|
+
} else if (isPermissionError(err)) {
|
|
1578
|
+
reason = "permission_denied";
|
|
1579
|
+
console.error(" \u26A0 No permission to list connectors in this project.");
|
|
1580
|
+
} else {
|
|
1581
|
+
reason = "api_error";
|
|
1582
|
+
console.error(" \u26A0 Couldn't fetch connectors:", err instanceof Error ? err.message : err);
|
|
1583
|
+
}
|
|
1584
|
+
trackEvent("CLI: project switch autoselect failed", { reason });
|
|
1585
|
+
}
|
|
1437
1586
|
trackEvent("CLI: project switch");
|
|
1438
1587
|
}
|
|
1439
1588
|
|
|
1589
|
+
// src/commands/project/delete.ts
|
|
1590
|
+
async function deleteAction4(name) {
|
|
1591
|
+
const user = getConfig("user");
|
|
1592
|
+
if (!user?.org_id) {
|
|
1593
|
+
console.error("\u2717 No organization found. Run: ardent login");
|
|
1594
|
+
process.exit(1);
|
|
1595
|
+
}
|
|
1596
|
+
const cached = getCacheEntry("projects");
|
|
1597
|
+
let project = cached?.data.find((p) => p.name === name);
|
|
1598
|
+
if (!project) {
|
|
1599
|
+
try {
|
|
1600
|
+
const result = await api.get(`/v1/projects?org_id=${user.org_id}`);
|
|
1601
|
+
if (!Array.isArray(result.projects)) {
|
|
1602
|
+
throw new Error("API returned invalid response: missing projects array");
|
|
1603
|
+
}
|
|
1604
|
+
project = result.projects.find((p) => p.name === name);
|
|
1605
|
+
} catch (err) {
|
|
1606
|
+
if (isNetworkError(err)) {
|
|
1607
|
+
console.error(`\u2717 Project "${name}" not found in cache (offline)`);
|
|
1608
|
+
console.error(" Run 'ardent project list' when online to refresh");
|
|
1609
|
+
process.exit(1);
|
|
1610
|
+
}
|
|
1611
|
+
console.error("\u2717 Failed:", err instanceof Error ? err.message : err);
|
|
1612
|
+
process.exit(1);
|
|
1613
|
+
}
|
|
1614
|
+
}
|
|
1615
|
+
if (!project) {
|
|
1616
|
+
console.error(`\u2717 Project "${name}" not found`);
|
|
1617
|
+
console.error(" Run: ardent project list");
|
|
1618
|
+
process.exit(1);
|
|
1619
|
+
}
|
|
1620
|
+
const projectId = project.id;
|
|
1621
|
+
const projectName = project.name;
|
|
1622
|
+
try {
|
|
1623
|
+
console.log("Deleting project...");
|
|
1624
|
+
await api.delete(`/v1/projects/${projectId}`);
|
|
1625
|
+
const cachedAfter = getCacheEntry("projects");
|
|
1626
|
+
if (cachedAfter?.data) {
|
|
1627
|
+
const remaining = cachedAfter.data.filter((p) => p.id !== projectId);
|
|
1628
|
+
setCacheEntry("projects", remaining);
|
|
1629
|
+
}
|
|
1630
|
+
const currentId = getConfig("currentProjectId");
|
|
1631
|
+
if (currentId === projectId) {
|
|
1632
|
+
setConfig("currentProjectId", void 0);
|
|
1633
|
+
setConfig("currentProjectName", void 0);
|
|
1634
|
+
setConfig("currentConnectorId", void 0);
|
|
1635
|
+
setConfig("currentConnectorName", void 0);
|
|
1636
|
+
clearCurrentBranch();
|
|
1637
|
+
}
|
|
1638
|
+
trackEvent("CLI: project delete succeeded");
|
|
1639
|
+
console.log("\u2713 Project deleted");
|
|
1640
|
+
console.log(` Name: ${projectName}`);
|
|
1641
|
+
} catch (err) {
|
|
1642
|
+
if (isNetworkError(err)) {
|
|
1643
|
+
trackEvent("CLI: project delete failed", { reason: "offline" });
|
|
1644
|
+
console.error("\u2717 Cannot delete project while offline");
|
|
1645
|
+
process.exit(1);
|
|
1646
|
+
}
|
|
1647
|
+
if (isPermissionError(err)) {
|
|
1648
|
+
trackEvent("CLI: project delete failed", { reason: "permission_denied" });
|
|
1649
|
+
console.error("\u2717 You don't have permission to delete this project.");
|
|
1650
|
+
process.exit(1);
|
|
1651
|
+
}
|
|
1652
|
+
trackEvent("CLI: project delete failed", { reason: "api_error" });
|
|
1653
|
+
console.error("\u2717 Failed:", err instanceof Error ? err.message : err);
|
|
1654
|
+
process.exit(1);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1440
1658
|
// src/commands/project/index.ts
|
|
1441
1659
|
var projectCommand = new Command5("project").description(
|
|
1442
1660
|
"Manage projects"
|
|
@@ -1444,6 +1662,7 @@ var projectCommand = new Command5("project").description(
|
|
|
1444
1662
|
projectCommand.command("create <name>").description("Create a new project").action(createAction3);
|
|
1445
1663
|
projectCommand.command("list").description("List your projects").action(listAction4);
|
|
1446
1664
|
projectCommand.command("switch <name>").description("Switch to a different project").action(switchAction3);
|
|
1665
|
+
projectCommand.command("delete <name>").description("Delete a project by name").action(deleteAction4);
|
|
1447
1666
|
|
|
1448
1667
|
// src/commands/settings/index.ts
|
|
1449
1668
|
import { Command as Command6 } from "commander";
|
|
@@ -1614,26 +1833,82 @@ settingsCommand.command("remove <key>").description("Remove a setting. Keys: def
|
|
|
1614
1833
|
import { Command as Command7 } from "commander";
|
|
1615
1834
|
|
|
1616
1835
|
// src/commands/auth/login.ts
|
|
1836
|
+
function identifyAndTrack(userId) {
|
|
1837
|
+
const user = getConfig("user");
|
|
1838
|
+
const personProperties = {};
|
|
1839
|
+
if (user?.email) {
|
|
1840
|
+
personProperties.email = user.email;
|
|
1841
|
+
}
|
|
1842
|
+
if (user?.full_name) {
|
|
1843
|
+
personProperties.$name = user.full_name;
|
|
1844
|
+
}
|
|
1845
|
+
if (user?.org_name) {
|
|
1846
|
+
personProperties.org_name = user.org_name;
|
|
1847
|
+
}
|
|
1848
|
+
const resolvedUserId = userId || user?.user_id;
|
|
1849
|
+
if (resolvedUserId) {
|
|
1850
|
+
identifyUser(resolvedUserId, personProperties);
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
function printBootstrapFailureGuidance() {
|
|
1854
|
+
console.log("\n Couldn't load your projects. Try:");
|
|
1855
|
+
console.log(" ardent project list");
|
|
1856
|
+
}
|
|
1857
|
+
function printAutoSelection(bootstrap) {
|
|
1858
|
+
const { projects, connectors } = bootstrap;
|
|
1859
|
+
const green2 = "\x1B[32m";
|
|
1860
|
+
const reset2 = "\x1B[0m";
|
|
1861
|
+
if (projects.length === 0) {
|
|
1862
|
+
console.log(`
|
|
1863
|
+
${green2}No projects found. Create one:${reset2}`);
|
|
1864
|
+
console.log(`${green2} ardent project create <name>${reset2}`);
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
1867
|
+
if (projects.length === 1) {
|
|
1868
|
+
console.log(` Auto-selected project '${projects[0].name}'`);
|
|
1869
|
+
} else {
|
|
1870
|
+
console.log(`
|
|
1871
|
+
${projects.length} projects found.`);
|
|
1872
|
+
console.log("");
|
|
1873
|
+
console.log(`${green2} Next step: select a project: ardent project switch <name>${reset2}`);
|
|
1874
|
+
console.log("");
|
|
1875
|
+
console.log(` List your projects: ardent project list`);
|
|
1876
|
+
console.log("");
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
if (connectors.length === 0) {
|
|
1880
|
+
console.log(`
|
|
1881
|
+
${green2}No connectors found. Create one:${reset2}`);
|
|
1882
|
+
console.log(`${green2} ardent connector create postgresql <url>${reset2}`);
|
|
1883
|
+
return;
|
|
1884
|
+
}
|
|
1885
|
+
if (connectors.length === 1) {
|
|
1886
|
+
console.log(` Auto-selected connector '${connectors[0].name}'`);
|
|
1887
|
+
console.log(`
|
|
1888
|
+
${green2}Ready to branch:${reset2}`);
|
|
1889
|
+
console.log(`${green2} ardent branch create <name>${reset2}`);
|
|
1890
|
+
} else {
|
|
1891
|
+
console.log(`
|
|
1892
|
+
${connectors.length} connectors found.`);
|
|
1893
|
+
console.log("");
|
|
1894
|
+
console.log(`${green2} Next step: select a connector: ardent connector switch <name>${reset2}`);
|
|
1895
|
+
console.log("");
|
|
1896
|
+
console.log(` List your connectors: ardent connector list`);
|
|
1897
|
+
console.log("");
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1617
1900
|
async function loginAction(options) {
|
|
1618
1901
|
if (options.token) {
|
|
1619
1902
|
setConfig("token", options.token);
|
|
1620
1903
|
console.log("\u2713 Logged in successfully");
|
|
1621
1904
|
trackEvent("CLI: login succeeded", { method: "token" });
|
|
1622
1905
|
try {
|
|
1623
|
-
await bootstrapCache();
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
const tokenPersonProperties = {};
|
|
1627
|
-
if (user.email) tokenPersonProperties.email = user.email;
|
|
1628
|
-
if (user.full_name) tokenPersonProperties.$name = user.full_name;
|
|
1629
|
-
if (user.org_name) tokenPersonProperties.org_name = user.org_name;
|
|
1630
|
-
if (user.user_id) {
|
|
1631
|
-
identifyUser(user.user_id, tokenPersonProperties);
|
|
1632
|
-
}
|
|
1633
|
-
}
|
|
1906
|
+
const bootstrap = await bootstrapCache();
|
|
1907
|
+
identifyAndTrack(null);
|
|
1908
|
+
printAutoSelection(bootstrap);
|
|
1634
1909
|
} catch {
|
|
1910
|
+
printBootstrapFailureGuidance();
|
|
1635
1911
|
}
|
|
1636
|
-
showNextStep();
|
|
1637
1912
|
return;
|
|
1638
1913
|
}
|
|
1639
1914
|
console.log("Opening browser for authentication...");
|
|
@@ -1665,34 +1940,28 @@ async function loginAction(options) {
|
|
|
1665
1940
|
console.error("\u2717 Poll failed");
|
|
1666
1941
|
process.exit(1);
|
|
1667
1942
|
}
|
|
1668
|
-
const
|
|
1669
|
-
if (
|
|
1670
|
-
setConfig("token",
|
|
1943
|
+
const pollResult = await pollResponse.json();
|
|
1944
|
+
if (pollResult.status === "completed") {
|
|
1945
|
+
setConfig("token", pollResult.token);
|
|
1671
1946
|
console.log("\n\u2713 Logged in successfully");
|
|
1672
1947
|
trackEvent("CLI: login succeeded", { method: "browser" });
|
|
1673
1948
|
try {
|
|
1674
|
-
await bootstrapCache();
|
|
1949
|
+
const bootstrap = await bootstrapCache();
|
|
1950
|
+
identifyAndTrack(pollResult.user_id);
|
|
1951
|
+
printAutoSelection(bootstrap);
|
|
1675
1952
|
} catch {
|
|
1953
|
+
printBootstrapFailureGuidance();
|
|
1676
1954
|
}
|
|
1677
|
-
const user = getConfig("user");
|
|
1678
|
-
const personProperties = {};
|
|
1679
|
-
if (user?.email) personProperties.email = user.email;
|
|
1680
|
-
if (user?.full_name) personProperties.$name = user.full_name;
|
|
1681
|
-
if (user?.org_name) personProperties.org_name = user.org_name;
|
|
1682
|
-
if (result.user_id) {
|
|
1683
|
-
identifyUser(result.user_id, personProperties);
|
|
1684
|
-
}
|
|
1685
|
-
showNextStep();
|
|
1686
1955
|
return;
|
|
1687
1956
|
}
|
|
1688
|
-
if (
|
|
1957
|
+
if (pollResult.status === "expired") {
|
|
1689
1958
|
trackEvent("CLI: login failed", { method: "browser", reason: "expired" });
|
|
1690
1959
|
console.error("\n\u2717 Session expired. Please try again.");
|
|
1691
1960
|
process.exit(1);
|
|
1692
1961
|
}
|
|
1693
|
-
if (
|
|
1962
|
+
if (pollResult.status === "error") {
|
|
1694
1963
|
trackEvent("CLI: login failed", { method: "browser", reason: "error" });
|
|
1695
|
-
console.error("\n\u2717 Error:",
|
|
1964
|
+
console.error("\n\u2717 Error:", pollResult.message);
|
|
1696
1965
|
process.exit(1);
|
|
1697
1966
|
}
|
|
1698
1967
|
process.stdout.write(".");
|
|
@@ -1835,6 +2104,7 @@ PROJECTS
|
|
|
1835
2104
|
project create Create a new project
|
|
1836
2105
|
project list List your projects (* = current)
|
|
1837
2106
|
project switch Switch to a different project
|
|
2107
|
+
project delete Delete a project by name
|
|
1838
2108
|
|
|
1839
2109
|
CONNECTORS
|
|
1840
2110
|
connector create Connect a database (postgresql, snowflake, etc.)
|