ardent-cli 0.0.8 → 0.0.10

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 +131 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -224,9 +224,59 @@ function isPermissionError(err) {
224
224
  return false;
225
225
  }
226
226
 
227
+ // src/lib/telemetry.ts
228
+ import { randomUUID } from "crypto";
229
+ function getAnonymousId() {
230
+ let anonymousId = getConfig("anonymousId");
231
+ if (!anonymousId) {
232
+ anonymousId = randomUUID();
233
+ setConfig("anonymousId", anonymousId);
234
+ }
235
+ return anonymousId;
236
+ }
237
+ function trackEvent(event, properties = {}) {
238
+ const distinctId = getConfig("userId") ?? getAnonymousId();
239
+ fetch(`${getApiUrl()}/v1/posthog/event`, {
240
+ method: "POST",
241
+ headers: { "Content-Type": "application/json" },
242
+ body: JSON.stringify({
243
+ events: [{
244
+ event,
245
+ properties: {
246
+ ...properties,
247
+ distinct_id: distinctId,
248
+ source: "cli"
249
+ }
250
+ }]
251
+ })
252
+ }).catch(() => {
253
+ });
254
+ }
255
+ function identifyUser(userId, personProperties = {}) {
256
+ const anonymousId = getAnonymousId();
257
+ setConfig("userId", userId);
258
+ fetch(`${getApiUrl()}/v1/posthog/event`, {
259
+ method: "POST",
260
+ headers: { "Content-Type": "application/json" },
261
+ body: JSON.stringify({
262
+ events: [{
263
+ event: "$identify",
264
+ properties: {
265
+ distinct_id: userId,
266
+ $anon_distinct_id: anonymousId,
267
+ source: "cli",
268
+ $set: personProperties
269
+ }
270
+ }]
271
+ })
272
+ }).catch(() => {
273
+ });
274
+ }
275
+
227
276
  // src/commands/branch/create.ts
228
277
  async function createAction(name, options) {
229
278
  try {
279
+ const startTime = performance.now();
230
280
  const job = await api.post("/v1/cli/jobs/create", {
231
281
  name
232
282
  });
@@ -254,16 +304,20 @@ async function createAction(name, options) {
254
304
  cachedBranches.push(branch);
255
305
  setCacheEntry("branches", cachedBranches);
256
306
  setCurrentBranch(name);
257
- console.log(`\u2713 Branch '${name}' created and checked out`);
307
+ const elapsed = ((performance.now() - startTime) / 1e3).toFixed(1);
308
+ trackEvent("CLI: branch create succeeded", { service_type: options.service, duration_seconds: parseFloat(elapsed) });
309
+ console.log(`\u2713 Branch '${name}' created and checked out in ${elapsed}s`);
258
310
  if (branch.branch_url) {
259
311
  console.log(`
260
312
  ${branch.branch_url}`);
261
313
  }
262
314
  } catch (err) {
263
315
  if (isNetworkError(err)) {
316
+ trackEvent("CLI: branch create failed", { reason: "offline" });
264
317
  console.error("\u2717 Cannot create branch while offline");
265
318
  process.exit(1);
266
319
  }
320
+ trackEvent("CLI: branch create failed", { reason: "api_error" });
267
321
  console.error("\u2717 Failed:", err instanceof Error ? err.message : err);
268
322
  process.exit(1);
269
323
  }
@@ -288,11 +342,14 @@ async function listAction() {
288
342
  branches = cached.data;
289
343
  fromCache = true;
290
344
  cacheTime = formatCacheTime(cached.updated_at);
345
+ trackEvent("CLI: branch list served from cache");
291
346
  } else {
347
+ trackEvent("CLI: branch list failed", { reason: "offline_no_cache" });
292
348
  console.error("\u2717 Offline and no cached data available");
293
349
  process.exit(1);
294
350
  }
295
351
  } else {
352
+ trackEvent("CLI: branch list failed", { reason: "api_error" });
296
353
  console.error("\u2717 Failed:", err instanceof Error ? err.message : err);
297
354
  process.exit(1);
298
355
  }
@@ -301,6 +358,7 @@ async function listAction() {
301
358
  console.log(`\u26A0 Offline - showing cached data from ${cacheTime}
302
359
  `);
303
360
  }
361
+ trackEvent("CLI: branch list succeeded", { branch_count: branches.length, from_cache: fromCache });
304
362
  if (branches.length === 0) {
305
363
  console.log("No branches found");
306
364
  console.log(" Create one with: ardent branch create <name>");
@@ -355,6 +413,7 @@ function infoAction(name) {
355
413
  console.log(`
356
414
  URL: ${branch.branch_url}`);
357
415
  }
416
+ trackEvent("CLI: branch info");
358
417
  }
359
418
 
360
419
  // src/commands/branch/delete.ts
@@ -373,12 +432,15 @@ async function deleteAction(name) {
373
432
  if (getCurrentBranch() === name) {
374
433
  clearCurrentBranch();
375
434
  }
435
+ trackEvent("CLI: branch delete succeeded");
376
436
  console.log("\u2713 Branch deleted");
377
437
  } catch (err) {
378
438
  if (isNetworkError(err)) {
439
+ trackEvent("CLI: branch delete failed", { reason: "offline" });
379
440
  console.error("\u2717 Cannot delete branch while offline");
380
441
  process.exit(1);
381
442
  }
443
+ trackEvent("CLI: branch delete failed", { reason: "api_error" });
382
444
  console.error("\u2717 Failed:", err instanceof Error ? err.message : err);
383
445
  process.exit(1);
384
446
  }
@@ -425,6 +487,7 @@ async function switchAction(name) {
425
487
  console.log(`
426
488
  ${branch.branch_url}`);
427
489
  }
490
+ trackEvent("CLI: branch switch");
428
491
  }
429
492
 
430
493
  // src/commands/branch/diff.ts
@@ -630,8 +693,14 @@ async function diffAction(options) {
630
693
  if ((!ddl || ddl.length === 0) && (!diff || diff.length === 0)) {
631
694
  console.log("No changes");
632
695
  }
696
+ trackEvent("CLI: branch diff succeeded", {
697
+ sql_mode: Boolean(options.sql),
698
+ ddl_count: ddl?.length ?? 0,
699
+ data_change_count: diff?.length ?? 0
700
+ });
633
701
  }
634
702
  } catch (err) {
703
+ trackEvent("CLI: branch diff failed", { reason: "api_error" });
635
704
  console.error("\u2717 Failed:", err instanceof Error ? err.message : err);
636
705
  process.exit(1);
637
706
  }
@@ -863,11 +932,13 @@ async function createAction2(type, url, options) {
863
932
  const cachedConnectors = cached?.data || [];
864
933
  cachedConnectors.push(newConnector);
865
934
  setCacheEntry("connectors", cachedConnectors);
935
+ trackEvent("CLI: connector create succeeded", { db_type: type, byoc: isByoc });
866
936
  console.log("\u2713 Connector created and ready");
867
937
  console.log(` ID: ${connectorId}`);
868
938
  showNextStep();
869
939
  } catch (err) {
870
940
  if (isPermissionError(err)) {
941
+ trackEvent("CLI: connector create failed", { reason: "permission_denied" });
871
942
  console.error("\u2717 You don't have permission to create connectors.");
872
943
  console.error("");
873
944
  console.error(" Ask your organization admin to either:");
@@ -875,6 +946,7 @@ async function createAction2(type, url, options) {
875
946
  console.error(" \u2022 Upgrade your role to Admin");
876
947
  process.exit(1);
877
948
  }
949
+ trackEvent("CLI: connector create failed", { reason: "api_error" });
878
950
  console.error("\u2717 Failed:", err instanceof Error ? err.message : err);
879
951
  process.exit(1);
880
952
  }
@@ -900,14 +972,17 @@ async function listAction2() {
900
972
  fromCache = true;
901
973
  cacheTime = formatCacheTime(cached.updated_at);
902
974
  } else {
975
+ trackEvent("CLI: connector list failed", { reason: "offline_no_cache" });
903
976
  console.error("\u2717 Offline and no cached data available");
904
977
  process.exit(1);
905
978
  }
906
979
  } else {
980
+ trackEvent("CLI: connector list failed", { reason: "api_error" });
907
981
  console.error("\u2717 Failed:", err instanceof Error ? err.message : err);
908
982
  process.exit(1);
909
983
  }
910
984
  }
985
+ trackEvent("CLI: connector list succeeded", { connector_count: connectors.length, from_cache: fromCache });
911
986
  if (fromCache) {
912
987
  console.log(`\u26A0 Offline - showing cached data from ${cacheTime}
913
988
  `);
@@ -958,13 +1033,16 @@ async function deleteAction2(name) {
958
1033
  const updatedConnectors = currentCache.data.filter((c) => c.id !== connector.id);
959
1034
  setCacheEntry("connectors", updatedConnectors);
960
1035
  }
1036
+ trackEvent("CLI: connector delete succeeded");
961
1037
  console.log("\u2713 Connector deleted");
962
1038
  } catch (err) {
963
1039
  if (isNetworkError(err)) {
1040
+ trackEvent("CLI: connector delete failed", { reason: "offline" });
964
1041
  console.error("\u2717 Cannot delete connector while offline");
965
1042
  process.exit(1);
966
1043
  }
967
1044
  if (isPermissionError(err)) {
1045
+ trackEvent("CLI: connector delete failed", { reason: "permission_denied" });
968
1046
  console.error("\u2717 You don't have permission to delete connectors.");
969
1047
  console.error("");
970
1048
  console.error(" Ask your organization admin to either:");
@@ -972,6 +1050,7 @@ async function deleteAction2(name) {
972
1050
  console.error(" \u2022 Upgrade your role to Admin");
973
1051
  process.exit(1);
974
1052
  }
1053
+ trackEvent("CLI: connector delete failed", { reason: "api_error" });
975
1054
  console.error("\u2717 Failed:", err instanceof Error ? err.message : err);
976
1055
  process.exit(1);
977
1056
  }
@@ -999,15 +1078,18 @@ async function sendAction(email, role) {
999
1078
  email,
1000
1079
  role: role.toLowerCase()
1001
1080
  });
1081
+ trackEvent("CLI: invite send succeeded", { role: result.role });
1002
1082
  console.log(`\u2713 Invite sent to ${result.email}`);
1003
1083
  console.log(` Role: ${result.role}`);
1004
1084
  console.log(` Org: ${result.org_name}`);
1005
1085
  } catch (err) {
1006
1086
  if (isNetworkError(err)) {
1087
+ trackEvent("CLI: invite send failed", { reason: "offline" });
1007
1088
  console.error("\u2717 Cannot send invite while offline");
1008
1089
  process.exit(1);
1009
1090
  }
1010
1091
  if (isPermissionError(err)) {
1092
+ trackEvent("CLI: invite send failed", { reason: "permission_denied" });
1011
1093
  console.error("\u2717 You don't have permission to invite users.");
1012
1094
  console.error("");
1013
1095
  console.error(" Ask your organization admin to either:");
@@ -1015,6 +1097,7 @@ async function sendAction(email, role) {
1015
1097
  console.error(" \u2022 Upgrade your role to Admin");
1016
1098
  process.exit(1);
1017
1099
  }
1100
+ trackEvent("CLI: invite send failed", { reason: "api_error" });
1018
1101
  console.error("\u2717 Failed:", err instanceof Error ? err.message : err);
1019
1102
  process.exit(1);
1020
1103
  }
@@ -1028,6 +1111,7 @@ async function listAction3() {
1028
1111
  throw new Error("API returned invalid response: missing invites array");
1029
1112
  }
1030
1113
  const invites = result.invites;
1114
+ trackEvent("CLI: invite list succeeded", { invite_count: invites.length });
1031
1115
  if (invites.length === 0) {
1032
1116
  console.log("No pending invites");
1033
1117
  return;
@@ -1041,9 +1125,11 @@ async function listAction3() {
1041
1125
  }
1042
1126
  } catch (err) {
1043
1127
  if (isNetworkError(err)) {
1128
+ trackEvent("CLI: invite list failed", { reason: "offline" });
1044
1129
  console.error("\u2717 Cannot list invites while offline");
1045
1130
  process.exit(1);
1046
1131
  }
1132
+ trackEvent("CLI: invite list failed", { reason: "api_error" });
1047
1133
  console.error("\u2717 Failed:", err instanceof Error ? err.message : err);
1048
1134
  process.exit(1);
1049
1135
  }
@@ -1053,12 +1139,15 @@ async function listAction3() {
1053
1139
  async function deleteAction3(email) {
1054
1140
  try {
1055
1141
  await api.delete("/v1/cli/invites", { email });
1142
+ trackEvent("CLI: invite delete succeeded");
1056
1143
  console.log(`\u2713 Invite for ${email} deleted`);
1057
1144
  } catch (err) {
1058
1145
  if (isNetworkError(err)) {
1146
+ trackEvent("CLI: invite delete failed", { reason: "offline" });
1059
1147
  console.error("\u2717 Cannot delete invite while offline");
1060
1148
  process.exit(1);
1061
1149
  }
1150
+ trackEvent("CLI: invite delete failed", { reason: "api_error" });
1062
1151
  console.error("\u2717 Failed:", err instanceof Error ? err.message : err);
1063
1152
  process.exit(1);
1064
1153
  }
@@ -1082,6 +1171,7 @@ import { Command as Command4 } from "commander";
1082
1171
  async function membersAction() {
1083
1172
  try {
1084
1173
  const result = await api.get("/v1/cli/members");
1174
+ trackEvent("CLI: org members succeeded", { member_count: result.members?.length ?? 0 });
1085
1175
  if (!result.members || result.members.length === 0) {
1086
1176
  console.log("No members found");
1087
1177
  return;
@@ -1095,9 +1185,11 @@ async function membersAction() {
1095
1185
  }
1096
1186
  } catch (err) {
1097
1187
  if (isNetworkError(err)) {
1188
+ trackEvent("CLI: org members failed", { reason: "offline" });
1098
1189
  console.error("\u2717 Cannot list members while offline");
1099
1190
  process.exit(1);
1100
1191
  }
1192
+ trackEvent("CLI: org members failed", { reason: "api_error" });
1101
1193
  console.error("\u2717 Failed:", err instanceof Error ? err.message : err);
1102
1194
  process.exit(1);
1103
1195
  }
@@ -1113,18 +1205,22 @@ async function setRoleAction(email, role) {
1113
1205
  }
1114
1206
  try {
1115
1207
  const result = await api.patch("/v1/cli/members/role", { email, role });
1208
+ trackEvent("CLI: org set-role succeeded", { role: result.new_role });
1116
1209
  console.log(`\u2713 Updated ${result.email} to ${result.new_role}`);
1117
1210
  } catch (err) {
1118
1211
  if (isNetworkError(err)) {
1212
+ trackEvent("CLI: org set-role failed", { reason: "offline" });
1119
1213
  console.error("\u2717 Cannot update role while offline");
1120
1214
  process.exit(1);
1121
1215
  }
1122
1216
  if (isPermissionError(err)) {
1217
+ trackEvent("CLI: org set-role failed", { reason: "permission_denied" });
1123
1218
  console.error("\u2717 You don't have permission to update member roles.");
1124
1219
  console.error("");
1125
1220
  console.error(" Only org admins and owners can change roles.");
1126
1221
  process.exit(1);
1127
1222
  }
1223
+ trackEvent("CLI: org set-role failed", { reason: "api_error" });
1128
1224
  console.error("\u2717 Failed:", err instanceof Error ? err.message : err);
1129
1225
  process.exit(1);
1130
1226
  }
@@ -1134,18 +1230,22 @@ async function setRoleAction(email, role) {
1134
1230
  async function removeAction(email) {
1135
1231
  try {
1136
1232
  await api.delete("/v1/cli/members", { email });
1233
+ trackEvent("CLI: org remove member succeeded");
1137
1234
  console.log(`\u2713 Removed ${email} from organization`);
1138
1235
  } catch (err) {
1139
1236
  if (isNetworkError(err)) {
1237
+ trackEvent("CLI: org remove member failed", { reason: "offline" });
1140
1238
  console.error("\u2717 Cannot remove member while offline");
1141
1239
  process.exit(1);
1142
1240
  }
1143
1241
  if (isPermissionError(err)) {
1242
+ trackEvent("CLI: org remove member failed", { reason: "permission_denied" });
1144
1243
  console.error("\u2717 You don't have permission to remove members.");
1145
1244
  console.error("");
1146
1245
  console.error(" Only org admins and owners can remove members.");
1147
1246
  process.exit(1);
1148
1247
  }
1248
+ trackEvent("CLI: org remove member failed", { reason: "api_error" });
1149
1249
  console.error("\u2717 Failed:", err instanceof Error ? err.message : err);
1150
1250
  process.exit(1);
1151
1251
  }
@@ -1165,8 +1265,19 @@ async function loginAction(options) {
1165
1265
  if (options.token) {
1166
1266
  setConfig("token", options.token);
1167
1267
  console.log("\u2713 Logged in successfully");
1268
+ trackEvent("CLI: login succeeded", { method: "token" });
1168
1269
  try {
1169
1270
  await bootstrapCache();
1271
+ const user = getConfig("user");
1272
+ if (user) {
1273
+ const tokenPersonProperties = {};
1274
+ if (user.email) tokenPersonProperties.email = user.email;
1275
+ if (user.full_name) tokenPersonProperties.$name = user.full_name;
1276
+ if (user.org_name) tokenPersonProperties.org_name = user.org_name;
1277
+ if (user.user_id) {
1278
+ identifyUser(user.user_id, tokenPersonProperties);
1279
+ }
1280
+ }
1170
1281
  } catch {
1171
1282
  }
1172
1283
  showNextStep();
@@ -1180,6 +1291,7 @@ async function loginAction(options) {
1180
1291
  });
1181
1292
  if (!initResponse.ok) {
1182
1293
  const error = await initResponse.text();
1294
+ trackEvent("CLI: login failed", { method: "browser", reason: "init_failed" });
1183
1295
  console.error("\u2717 Failed to initialize auth:", error);
1184
1296
  process.exit(1);
1185
1297
  }
@@ -1196,6 +1308,7 @@ async function loginAction(options) {
1196
1308
  `${getApiUrl()}/v1/cli/auth/poll?session=${session_id}`
1197
1309
  );
1198
1310
  if (!pollResponse.ok) {
1311
+ trackEvent("CLI: login failed", { method: "browser", reason: "poll_failed" });
1199
1312
  console.error("\u2717 Poll failed");
1200
1313
  process.exit(1);
1201
1314
  }
@@ -1203,26 +1316,39 @@ async function loginAction(options) {
1203
1316
  if (result.status === "completed") {
1204
1317
  setConfig("token", result.token);
1205
1318
  console.log("\n\u2713 Logged in successfully");
1319
+ trackEvent("CLI: login succeeded", { method: "browser" });
1206
1320
  try {
1207
1321
  await bootstrapCache();
1208
1322
  } catch {
1209
1323
  }
1324
+ const user = getConfig("user");
1325
+ const personProperties = {};
1326
+ if (user?.email) personProperties.email = user.email;
1327
+ if (user?.full_name) personProperties.$name = user.full_name;
1328
+ if (user?.org_name) personProperties.org_name = user.org_name;
1329
+ if (result.user_id) {
1330
+ identifyUser(result.user_id, personProperties);
1331
+ }
1210
1332
  showNextStep();
1211
1333
  return;
1212
1334
  }
1213
1335
  if (result.status === "expired") {
1336
+ trackEvent("CLI: login failed", { method: "browser", reason: "expired" });
1214
1337
  console.error("\n\u2717 Session expired. Please try again.");
1215
1338
  process.exit(1);
1216
1339
  }
1217
1340
  if (result.status === "error") {
1341
+ trackEvent("CLI: login failed", { method: "browser", reason: "error" });
1218
1342
  console.error("\n\u2717 Error:", result.message);
1219
1343
  process.exit(1);
1220
1344
  }
1221
1345
  process.stdout.write(".");
1222
1346
  }
1347
+ trackEvent("CLI: login failed", { method: "browser", reason: "timeout" });
1223
1348
  console.error("\n\u2717 Timed out. Please try again.");
1224
1349
  process.exit(1);
1225
1350
  } catch (error) {
1351
+ trackEvent("CLI: login failed", { method: "browser", reason: "network_error" });
1226
1352
  console.error("\u2717 Login failed:", error instanceof Error ? error.message : error);
1227
1353
  process.exit(1);
1228
1354
  }
@@ -1230,6 +1356,7 @@ async function loginAction(options) {
1230
1356
 
1231
1357
  // src/commands/auth/logout.ts
1232
1358
  function logoutAction() {
1359
+ trackEvent("CLI: logout");
1233
1360
  clearConfig();
1234
1361
  console.log("\u2713 Logged out");
1235
1362
  }
@@ -1238,6 +1365,7 @@ function logoutAction() {
1238
1365
  function statusAction() {
1239
1366
  const token = getConfig("token");
1240
1367
  if (!token) {
1368
+ trackEvent("CLI: auth status", { authenticated: false });
1241
1369
  console.log("\u2717 Not authenticated");
1242
1370
  console.log(" Run: ardent login");
1243
1371
  return;
@@ -1257,6 +1385,7 @@ function statusAction() {
1257
1385
  console.log(` Token: ${token.slice(0, 8)}...${token.slice(-4)}`);
1258
1386
  console.log(" Run: ardent login to refresh profile info");
1259
1387
  }
1388
+ trackEvent("CLI: auth status", { authenticated: true });
1260
1389
  }
1261
1390
 
1262
1391
  // src/commands/auth/index.ts
@@ -1350,4 +1479,5 @@ if (args.length === 0) {
1350
1479
  }
1351
1480
  program.help();
1352
1481
  }
1482
+ getAnonymousId();
1353
1483
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ardent-cli",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "description": "Git for Data infrastructure",
5
5
  "type": "module",
6
6
  "bin": {