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