@opendatalabs/connect 0.11.6 → 0.12.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
package/dist/cli/index.js CHANGED
@@ -35,6 +35,7 @@ import { listAvailableSkills, installSkill, readInstalledSkills, } from "../skil
35
35
  import { queryStatus, querySources, queryDataList, queryDataShow, queryDoctor, } from "./queries.js";
36
36
  import { checkForUpdate, readUpdateCheck, isNewerVersion, } from "./update-check.js";
37
37
  import { loadCredentials, saveCredentials, clearCredentials, isExpired, formatAddress, formatExpiresIn, getAuthTarget, resolvePersonalServerUrl, runDeviceCodeFlow, runSelfHostedLoginFlow, } from "./auth.js";
38
+ import { createCliTelemetrySession, flushTelemetryOutbox, getActiveTelemetrySession, getTelemetryStatus, setActiveTelemetrySession, setTelemetryEnabled, trackActiveTelemetryEvent, } from "./telemetry.js";
38
39
  function cleanDescription(desc) {
39
40
  return desc
40
41
  .replace(/ using Playwright browser automation\.?/i, ".")
@@ -53,6 +54,12 @@ export async function runCli(argv = process.argv) {
53
54
  const parsedOptions = extractGlobalOptions(normalizedArgv);
54
55
  const cliVersion = getCliVersion();
55
56
  const installMethod = getCliInstallMethod();
57
+ const telemetryBaseContext = {
58
+ cliVersion,
59
+ channel: getCliChannel(cliVersion),
60
+ installMethod,
61
+ options: parsedOptions,
62
+ };
56
63
  // Non-blocking update check — compute suppression flags early
57
64
  const shouldNotify = !parsedOptions.json &&
58
65
  process.stdout.isTTY &&
@@ -108,17 +115,18 @@ More:
108
115
  .description("Print CLI version")
109
116
  .option("--json", "Output machine-readable JSON")
110
117
  .action(async () => {
111
- if (parsedOptions.json) {
112
- process.stdout.write(`${JSON.stringify({
113
- cliVersion,
114
- channel: getCliChannel(cliVersion),
115
- installMethod: getCliInstallMethod(),
116
- })}\n`);
117
- process.exitCode = 0;
118
- return;
119
- }
120
- process.stdout.write(`${cliVersion} (${getCliChannel(cliVersion)}, ${formatInstallMethodLabel(getCliInstallMethod()).toLowerCase()})\n`);
121
- process.exitCode = 0;
118
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "version" }, async () => {
119
+ if (parsedOptions.json) {
120
+ process.stdout.write(`${JSON.stringify({
121
+ cliVersion,
122
+ channel: getCliChannel(cliVersion),
123
+ installMethod: getCliInstallMethod(),
124
+ })}\n`);
125
+ return 0;
126
+ }
127
+ process.stdout.write(`${cliVersion} (${getCliChannel(cliVersion)}, ${formatInstallMethodLabel(getCliInstallMethod()).toLowerCase()})\n`);
128
+ return 0;
129
+ });
122
130
  });
123
131
  const connectCommand = program
124
132
  .command("connect [source]")
@@ -130,13 +138,14 @@ More:
130
138
  .option("--quiet", "Reduce non-essential output")
131
139
  .option("--detach", "Run in the background")
132
140
  .action(async (source) => {
133
- if (parsedOptions.detach && source) {
134
- process.exitCode = await runDetached("connect", source, parsedOptions);
135
- return;
136
- }
137
- process.exitCode = source
138
- ? await runConnect(source, parsedOptions)
139
- : await runConnectEntry(parsedOptions);
141
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "connect", source }, async () => {
142
+ if (parsedOptions.detach && source) {
143
+ return runDetached("connect", source, parsedOptions);
144
+ }
145
+ return source
146
+ ? runConnect(source, parsedOptions)
147
+ : runConnectEntry(parsedOptions);
148
+ });
140
149
  });
141
150
  connectCommand.addHelpText("after", `
142
151
  Examples:
@@ -150,9 +159,9 @@ Examples:
150
159
  .description("List supported sources, or show detail for one source")
151
160
  .option("--json", "Output machine-readable JSON")
152
161
  .action(async (source) => {
153
- process.exitCode = source
154
- ? await runSourceDetail(source, parsedOptions)
155
- : await runList(parsedOptions);
162
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "sources", source }, async () => source
163
+ ? runSourceDetail(source, parsedOptions)
164
+ : runList(parsedOptions));
156
165
  });
157
166
  sourcesCommand.addHelpText("after", `
158
167
  Examples:
@@ -171,13 +180,14 @@ Examples:
171
180
  .option("--detach", "Run in the background")
172
181
  .option("--all", "Collect from all connected sources")
173
182
  .action(async (source) => {
174
- if (parsedOptions.detach && source) {
175
- process.exitCode = await runDetached("collect", source, parsedOptions);
176
- return;
177
- }
178
- process.exitCode = source
179
- ? await runCollect(source, parsedOptions)
180
- : await runCollectAll(parsedOptions);
183
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "collect", source }, async () => {
184
+ if (parsedOptions.detach && source) {
185
+ return runDetached("collect", source, parsedOptions);
186
+ }
187
+ return source
188
+ ? runCollect(source, parsedOptions)
189
+ : runCollectAll(parsedOptions);
190
+ });
181
191
  });
182
192
  collectCommand.addHelpText("after", `
183
193
  Examples:
@@ -190,7 +200,7 @@ Examples:
190
200
  .description("Show runtime and Personal Server status")
191
201
  .option("--json", "Output machine-readable JSON")
192
202
  .action(async () => {
193
- process.exitCode = await runStatus(parsedOptions);
203
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "status" }, async () => runStatus(parsedOptions));
194
204
  });
195
205
  statusCommand.addHelpText("after", `
196
206
  Examples:
@@ -202,7 +212,7 @@ Examples:
202
212
  .description("Inspect local CLI, runtime, and install health")
203
213
  .option("--json", "Output machine-readable JSON")
204
214
  .action(async () => {
205
- process.exitCode = await runDoctor(parsedOptions);
215
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "doctor" }, async () => runDoctor(parsedOptions));
206
216
  });
207
217
  doctorCommand.addHelpText("after", `
208
218
  Examples:
@@ -215,7 +225,7 @@ Examples:
215
225
  .option("--json", "Output machine-readable JSON")
216
226
  .option("--yes", "Approve safe setup prompts automatically")
217
227
  .action(async () => {
218
- process.exitCode = await runSetup(parsedOptions);
228
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "setup" }, async () => runSetup(parsedOptions));
219
229
  });
220
230
  setupCommand.addHelpText("after", `
221
231
  Examples:
@@ -240,7 +250,7 @@ Examples:
240
250
  .description("List locally available collected datasets")
241
251
  .option("--json", "Output machine-readable JSON")
242
252
  .action(async () => {
243
- process.exitCode = await runDataList(parsedOptions);
253
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "data", subcommand: "list" }, async () => runDataList(parsedOptions));
244
254
  });
245
255
  dataListCommand.addHelpText("after", `
246
256
  Examples:
@@ -252,7 +262,12 @@ Examples:
252
262
  .description("Show a collected dataset")
253
263
  .option("--json", "Output machine-readable JSON")
254
264
  .action(async (source) => {
255
- process.exitCode = await runDataShow(source, parsedOptions);
265
+ process.exitCode = await runCommandWithTelemetry({
266
+ ...telemetryBaseContext,
267
+ command: "data",
268
+ subcommand: "show",
269
+ source,
270
+ }, async () => runDataShow(source, parsedOptions));
256
271
  });
257
272
  dataShowCommand.addHelpText("after", `
258
273
  Examples:
@@ -264,7 +279,12 @@ Examples:
264
279
  .description("Print the local path for a collected dataset")
265
280
  .option("--json", "Output machine-readable JSON")
266
281
  .action(async (source) => {
267
- process.exitCode = await runDataPath(source, parsedOptions);
282
+ process.exitCode = await runCommandWithTelemetry({
283
+ ...telemetryBaseContext,
284
+ command: "data",
285
+ subcommand: "path",
286
+ source,
287
+ }, async () => runDataPath(source, parsedOptions));
268
288
  });
269
289
  dataPathCommand.addHelpText("after", `
270
290
  Examples:
@@ -276,7 +296,7 @@ Examples:
276
296
  .description("Inspect stored connector run logs")
277
297
  .option("--json", "Output machine-readable JSON")
278
298
  .action(async (source) => {
279
- process.exitCode = await runLogs(source, parsedOptions);
299
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "logs", source }, async () => runLogs(source, parsedOptions));
280
300
  });
281
301
  logsCommand.addHelpText("after", `
282
302
  Examples:
@@ -296,62 +316,89 @@ Examples:
296
316
  vana server clear-url
297
317
  `);
298
318
  server.action(async () => {
299
- process.exitCode = await runServerStatus(parsedOptions);
319
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "server", subcommand: "status" }, async () => runServerStatus(parsedOptions));
300
320
  });
301
321
  server
302
322
  .command("status")
303
323
  .description("Show Personal Server status")
304
324
  .option("--json", "Output machine-readable JSON")
305
325
  .action(async () => {
306
- process.exitCode = await runServerStatus(parsedOptions);
326
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "server", subcommand: "status" }, async () => runServerStatus(parsedOptions));
307
327
  });
308
328
  server
309
329
  .command("set-url <url>")
310
330
  .description("Save a Personal Server URL")
311
331
  .option("--json", "Output machine-readable JSON")
312
332
  .action(async (url) => {
313
- process.exitCode = await runServerSetUrl(url, parsedOptions);
333
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "server", subcommand: "set-url" }, async () => runServerSetUrl(url, parsedOptions));
314
334
  });
315
335
  server
316
336
  .command("clear-url")
317
337
  .description("Remove the saved Personal Server URL")
318
338
  .option("--json", "Output machine-readable JSON")
319
339
  .action(async () => {
320
- process.exitCode = await runServerClearUrl(parsedOptions);
340
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "server", subcommand: "clear-url" }, async () => runServerClearUrl(parsedOptions));
321
341
  });
322
342
  server
323
343
  .command("sync")
324
344
  .description("Sync all local-only datasets to your Personal Server")
325
345
  .option("--json", "Output machine-readable JSON")
326
346
  .action(async () => {
327
- process.exitCode = await runServerSync(parsedOptions);
347
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "server", subcommand: "sync" }, async () => runServerSync(parsedOptions));
328
348
  });
329
349
  server
330
350
  .command("data [scope]")
331
351
  .description("List scopes stored in your Personal Server")
332
352
  .option("--json", "Output machine-readable JSON")
333
353
  .action(async (scope) => {
334
- process.exitCode = await runServerData(scope, parsedOptions);
354
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "server", subcommand: "data" }, async () => runServerData(scope, parsedOptions));
335
355
  });
336
356
  program
337
357
  .command("login")
338
358
  .description("Log in to your Vana account or a self-hosted Personal Server")
339
359
  .option("-s, --server <url>", "Self-hosted Personal Server URL")
340
360
  .action(async (loginOptions) => {
341
- process.exitCode = await runLogin(parsedOptions, loginOptions.server);
361
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "login" }, async () => runLogin(parsedOptions, loginOptions.server));
342
362
  });
343
363
  program
344
364
  .command("logout")
345
365
  .description("Log out and remove saved credentials")
346
366
  .action(async () => {
347
- process.exitCode = await runLogout(parsedOptions);
367
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "logout" }, async () => runLogout(parsedOptions));
368
+ });
369
+ const telemetry = program
370
+ .command("telemetry")
371
+ .description("Inspect and manage CLI telemetry");
372
+ telemetry.action(async () => {
373
+ process.exitCode = await runTelemetryStatus(parsedOptions);
374
+ });
375
+ telemetry
376
+ .command("status")
377
+ .description("Show telemetry state")
378
+ .option("--json", "Output machine-readable JSON")
379
+ .action(async () => {
380
+ process.exitCode = await runTelemetryStatus(parsedOptions);
381
+ });
382
+ telemetry
383
+ .command("enable")
384
+ .description("Enable telemetry")
385
+ .action(async () => {
386
+ process.exitCode = await runTelemetryEnable(parsedOptions);
387
+ });
388
+ telemetry
389
+ .command("disable")
390
+ .description("Disable telemetry")
391
+ .action(async () => {
392
+ process.exitCode = await runTelemetryDisable(parsedOptions);
348
393
  });
349
394
  program
350
395
  .command("mcp")
351
396
  .description("Start MCP server for agent integration")
352
397
  .action(async () => {
353
- const { startMcpServer } = await import("./mcp-server.js");
354
- await startMcpServer();
398
+ process.exitCode = await runLongRunningCommandWithTelemetry({ ...telemetryBaseContext, command: "mcp" }, async () => {
399
+ const { startMcpServer } = await import("./mcp-server.js");
400
+ await startMcpServer();
401
+ });
355
402
  });
356
403
  const skill = program
357
404
  .command("skills")
@@ -364,26 +411,26 @@ Examples:
364
411
  vana skills show connect-data
365
412
  `);
366
413
  skill.action(async () => {
367
- process.exitCode = await runSkillsGuidedPicker(parsedOptions);
414
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "skills" }, async () => runSkillsGuidedPicker(parsedOptions));
368
415
  });
369
416
  skill
370
417
  .command("list")
371
418
  .description("List available agent skills")
372
419
  .option("--json", "Output as JSON")
373
420
  .action(async () => {
374
- process.exitCode = await runSkillList(parsedOptions);
421
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "skills", subcommand: "list" }, async () => runSkillList(parsedOptions));
375
422
  });
376
423
  skill
377
424
  .command("install <name>")
378
425
  .description("Install a skill for your agent")
379
426
  .action(async (name) => {
380
- process.exitCode = await runSkillInstall(name, parsedOptions);
427
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "skills", subcommand: "install" }, async () => runSkillInstall(name, parsedOptions));
381
428
  });
382
429
  skill
383
430
  .command("show <name>")
384
431
  .description("Show skill details")
385
432
  .action(async (name) => {
386
- process.exitCode = await runSkillShow(name, parsedOptions);
433
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "skills", subcommand: "show" }, async () => runSkillShow(name, parsedOptions));
387
434
  });
388
435
  // --- Schedule commands ---
389
436
  const schedule = program
@@ -405,20 +452,20 @@ Examples:
405
452
  .description("Add a scheduled collection")
406
453
  .option("--every <interval>", "Collection interval (e.g. 24h, 12h, 1h)", "24h")
407
454
  .action(async (opts) => {
408
- process.exitCode = await runScheduleAdd(opts.every, parsedOptions);
455
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "schedule", subcommand: "add" }, async () => runScheduleAdd(opts.every, parsedOptions));
409
456
  });
410
457
  schedule
411
458
  .command("list")
412
459
  .description("Show scheduled tasks")
413
460
  .option("--json", "Output machine-readable JSON")
414
461
  .action(async () => {
415
- process.exitCode = await runScheduleList(parsedOptions);
462
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "schedule", subcommand: "list" }, async () => runScheduleList(parsedOptions));
416
463
  });
417
464
  schedule
418
465
  .command("remove")
419
466
  .description("Remove the scheduled collection")
420
467
  .action(async () => {
421
- process.exitCode = await runScheduleRemove(parsedOptions);
468
+ process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "schedule", subcommand: "remove" }, async () => runScheduleRemove(parsedOptions));
422
469
  });
423
470
  try {
424
471
  await program.parseAsync(normalizedArgv);
@@ -457,6 +504,139 @@ Examples:
457
504
  }
458
505
  return Number(process.exitCode ?? 0);
459
506
  }
507
+ function classifyCommandFailure(error) {
508
+ if (error instanceof Error) {
509
+ const value = error.message.toLowerCase();
510
+ if (value.includes("auth"))
511
+ return "auth_failed";
512
+ if (value.includes("setup"))
513
+ return "setup_required";
514
+ if (value.includes("runtime"))
515
+ return "runtime_error";
516
+ if (value.includes("connector"))
517
+ return "connector_unavailable";
518
+ if (value.includes("ingest"))
519
+ return "ingest_failed";
520
+ }
521
+ return "unknown";
522
+ }
523
+ async function runCommandWithTelemetry(context, action) {
524
+ const session = await createCliTelemetrySession({
525
+ ...context,
526
+ options: {
527
+ json: Boolean(context.options.json),
528
+ noInput: Boolean(context.options.noInput),
529
+ quiet: Boolean(context.options.quiet),
530
+ detach: Boolean(context.options.detach),
531
+ ipc: Boolean(context.options.ipc),
532
+ },
533
+ localOnly: context.localOnly,
534
+ });
535
+ setActiveTelemetrySession(session);
536
+ await flushTelemetryOutbox();
537
+ try {
538
+ const exitCode = await action();
539
+ session.markCommandResult({ exitCode });
540
+ return exitCode;
541
+ }
542
+ catch (error) {
543
+ session.markCommandResult({
544
+ exitCode: 1,
545
+ errorClass: classifyCommandFailure(error),
546
+ });
547
+ throw error;
548
+ }
549
+ finally {
550
+ await session.persist();
551
+ await session.flush();
552
+ setActiveTelemetrySession(null);
553
+ }
554
+ }
555
+ async function runLongRunningCommandWithTelemetry(context, action) {
556
+ const session = await createCliTelemetrySession({
557
+ ...context,
558
+ options: {
559
+ json: Boolean(context.options.json),
560
+ noInput: Boolean(context.options.noInput),
561
+ quiet: Boolean(context.options.quiet),
562
+ detach: Boolean(context.options.detach),
563
+ ipc: Boolean(context.options.ipc),
564
+ },
565
+ });
566
+ setActiveTelemetrySession(session);
567
+ await flushTelemetryOutbox();
568
+ session.trackCustomEvent("mcp_started");
569
+ session.markCommandResult({ exitCode: 0, outcome: "started" });
570
+ await session.persist();
571
+ await session.flush();
572
+ try {
573
+ await action();
574
+ return 0;
575
+ }
576
+ finally {
577
+ setActiveTelemetrySession(null);
578
+ }
579
+ }
580
+ async function runTelemetryStatus(options) {
581
+ const status = await getTelemetryStatus();
582
+ const endpointHost = (() => {
583
+ try {
584
+ return new URL(status.endpoint).host;
585
+ }
586
+ catch {
587
+ return status.endpoint;
588
+ }
589
+ })();
590
+ if (options.json) {
591
+ process.stdout.write(`${JSON.stringify(status)}\n`);
592
+ return 0;
593
+ }
594
+ const emit = createEmitter(options);
595
+ emit.title("Telemetry");
596
+ emit.blank();
597
+ emit.keyValue("Enabled", status.enabled ? "yes" : "no");
598
+ emit.keyValue("Mode", status.mode);
599
+ emit.keyValue("Reason", status.reason.replaceAll("_", " "));
600
+ emit.keyValue("Endpoint", endpointHost);
601
+ emit.keyValue("Queued", String(status.queuedBatches));
602
+ emit.detail("Collected data stays local. Remote telemetry only includes small operational events.");
603
+ if (status.enabled) {
604
+ emit.detail(`Disable with: ${emit.code("vana telemetry disable")}`);
605
+ }
606
+ else {
607
+ emit.detail(`Enable with: ${emit.code("vana telemetry enable")}`);
608
+ }
609
+ if (process.env.VANA_TELEMETRY_DEBUG === "1") {
610
+ emit.detail(`Debug mode is active via ${emit.code("VANA_TELEMETRY_DEBUG=1")}. Events print to stderr and are not uploaded.`);
611
+ }
612
+ if (process.env.VANA_TELEMETRY_DISABLED === "1") {
613
+ emit.detail(`Telemetry is currently overridden by ${emit.code("VANA_TELEMETRY_DISABLED=1")}.`);
614
+ }
615
+ return 0;
616
+ }
617
+ async function runTelemetryEnable(options) {
618
+ await setTelemetryEnabled(true);
619
+ if (options.json) {
620
+ process.stdout.write(`${JSON.stringify({ enabled: true })}\n`);
621
+ return 0;
622
+ }
623
+ const emit = createEmitter(options);
624
+ emit.success("Telemetry enabled.");
625
+ if (process.env.VANA_TELEMETRY_DISABLED === "1") {
626
+ emit.detail(`The current shell still disables uploads via ${emit.code("VANA_TELEMETRY_DISABLED=1")}.`);
627
+ }
628
+ return 0;
629
+ }
630
+ async function runTelemetryDisable(options) {
631
+ await setTelemetryEnabled(false);
632
+ if (options.json) {
633
+ process.stdout.write(`${JSON.stringify({ enabled: false })}\n`);
634
+ return 0;
635
+ }
636
+ const emit = createEmitter(options);
637
+ emit.success("Telemetry disabled.");
638
+ return 0;
639
+ }
460
640
  async function runConnect(rawSource, options) {
461
641
  const source = rawSource.toLowerCase();
462
642
  const runtime = new ManagedPlaywrightRuntime();
@@ -510,7 +690,18 @@ async function runConnect(rawSource, options) {
510
690
  }
511
691
  process.stderr.write("\n");
512
692
  }
513
- const installResult = await runtime.ensureInstalled(Boolean(options.yes));
693
+ trackActiveTelemetryEvent("runtime_install_started", { source });
694
+ let installResult;
695
+ try {
696
+ installResult = await runtime.ensureInstalled(Boolean(options.yes));
697
+ }
698
+ catch (error) {
699
+ trackActiveTelemetryEvent("runtime_install_failed", {
700
+ source,
701
+ errorClass: classifyCommandFailure(error),
702
+ });
703
+ throw error;
704
+ }
514
705
  setupLogPath = installResult.logPath;
515
706
  emit.event({
516
707
  type: "setup-complete",
@@ -599,6 +790,13 @@ async function runConnect(rawSource, options) {
599
790
  }
600
791
  }
601
792
  if (fetched.updated && fetched.previousVersion) {
793
+ trackActiveTelemetryEvent("connector_update_applied", {
794
+ source,
795
+ metadata: {
796
+ previousVersion: fetched.previousVersion,
797
+ connectorVersion: fetched.version,
798
+ },
799
+ });
602
800
  renderer?.detail(`Updated connector (${fetched.previousVersion} → ${fetched.version}).`);
603
801
  }
604
802
  fetchLogPath = fetched.logPath;
@@ -613,6 +811,10 @@ async function runConnect(rawSource, options) {
613
811
  connectorPath: resolution.connectorPath,
614
812
  logPath: fetched.logPath,
615
813
  });
814
+ trackActiveTelemetryEvent("connector_version_detected", {
815
+ source: resolution.source,
816
+ connectorVersion: fetched.version,
817
+ });
616
818
  // --- Phase 3: Pre-connection validation (silent) ---
617
819
  const profilePath = path.join(getBrowserProfilesDir(), `${path.basename(resolution.connectorPath, path.extname(resolution.connectorPath))}`);
618
820
  if (sourceDetails?.authMode === "legacy" &&
@@ -2196,7 +2398,11 @@ async function runCollectAll(options) {
2196
2398
  async function runServerSync(options) {
2197
2399
  const emit = createEmitter(options);
2198
2400
  const target = await detectPersonalServerTarget();
2401
+ trackActiveTelemetryEvent("server_sync_started");
2199
2402
  if (target.state !== "available") {
2403
+ trackActiveTelemetryEvent("server_sync_failed", {
2404
+ errorClass: "personal_server_unavailable",
2405
+ });
2200
2406
  if (options.json) {
2201
2407
  process.stdout.write(`${JSON.stringify({
2202
2408
  error: "personal_server_unavailable",
@@ -2209,7 +2415,16 @@ async function runServerSync(options) {
2209
2415
  return 1;
2210
2416
  }
2211
2417
  const syncResult = await syncPendingSources(target, "manual");
2418
+ const storedScopeCount = syncResult.sourceResults.reduce((total, entry) => total +
2419
+ (entry.scopeResults?.filter((scopeResult) => scopeResult.status === "stored").length ?? 0), 0);
2420
+ const failedScopeCount = syncResult.sourceResults.reduce((total, entry) => total +
2421
+ (entry.scopeResults?.filter((scopeResult) => scopeResult.status === "failed").length ?? 0), 0);
2212
2422
  if (syncResult.sourceResults.length === 0) {
2423
+ trackActiveTelemetryEvent("server_sync_completed", {
2424
+ storedScopeCount: 0,
2425
+ failedScopeCount: 0,
2426
+ metadata: { syncedSources: 0 },
2427
+ });
2213
2428
  if (options.json) {
2214
2429
  process.stdout.write(`${JSON.stringify({ message: "No pending datasets to sync.", syncedCount: 0 })}\n`);
2215
2430
  }
@@ -2250,6 +2465,11 @@ async function runServerSync(options) {
2250
2465
  emit.next("vana server sync");
2251
2466
  }
2252
2467
  }
2468
+ trackActiveTelemetryEvent("server_sync_completed", {
2469
+ storedScopeCount,
2470
+ failedScopeCount,
2471
+ metadata: { syncedSources: syncResult.syncedCount },
2472
+ });
2253
2473
  return 0;
2254
2474
  }
2255
2475
  async function runServerData(scope, options) {
@@ -2414,6 +2634,7 @@ function createEmitter(options) {
2414
2634
  const renderer = createHumanRenderer();
2415
2635
  return {
2416
2636
  event(event) {
2637
+ getActiveTelemetrySession()?.trackCliEvent(event);
2417
2638
  if (options.json) {
2418
2639
  process.stdout.write(`${JSON.stringify(event)}\n`);
2419
2640
  }
@@ -3560,6 +3781,7 @@ async function runDetached(command, source, options) {
3560
3781
  stdio: ["ignore", logFd, logFd],
3561
3782
  env: { ...process.env, VANA_DETACHED: "1" },
3562
3783
  });
3784
+ trackActiveTelemetryEvent("detached_run_spawned", { source });
3563
3785
  child.unref();
3564
3786
  fs.closeSync(logFd);
3565
3787
  // Write session file
@@ -3764,6 +3986,9 @@ async function runScheduleAdd(interval, options) {
3764
3986
  emit.detail(`launchctl load "${LAUNCHD_PLIST_PATH}"`);
3765
3987
  return 1;
3766
3988
  }
3989
+ trackActiveTelemetryEvent("schedule_added", {
3990
+ metadata: { interval: intervalLabel, mechanism: "launchd" },
3991
+ });
3767
3992
  if (options.json) {
3768
3993
  process.stdout.write(`${JSON.stringify({ ok: true, interval: intervalLabel, mechanism: "launchd", plistPath: LAUNCHD_PLIST_PATH })}\n`);
3769
3994
  return 0;
@@ -3804,6 +4029,9 @@ async function runScheduleAdd(interval, options) {
3804
4029
  emit.detail(entry);
3805
4030
  return 1;
3806
4031
  }
4032
+ trackActiveTelemetryEvent("schedule_added", {
4033
+ metadata: { interval: intervalLabel, mechanism: "cron" },
4034
+ });
3807
4035
  if (options.json) {
3808
4036
  process.stdout.write(`${JSON.stringify({ ok: true, interval: intervalLabel, mechanism: "cron" })}\n`);
3809
4037
  return 0;
@@ -3845,6 +4073,9 @@ async function runScheduleAdd(interval, options) {
3845
4073
  emit.detail(`schtasks /Create /TN "${WINDOWS_TASK_NAME}" /TR "${trCmd}" /SC DAILY /ST 09:00 /F`);
3846
4074
  return 1;
3847
4075
  }
4076
+ trackActiveTelemetryEvent("schedule_added", {
4077
+ metadata: { interval: intervalLabel, mechanism: "schtasks" },
4078
+ });
3848
4079
  if (options.json) {
3849
4080
  process.stdout.write(`${JSON.stringify({ ok: true, interval: intervalLabel, mechanism: "schtasks" })}\n`);
3850
4081
  return 0;
@@ -3962,6 +4193,9 @@ async function runScheduleRemove(options) {
3962
4193
  // Already unloaded
3963
4194
  }
3964
4195
  await fsp.unlink(LAUNCHD_PLIST_PATH);
4196
+ trackActiveTelemetryEvent("schedule_removed", {
4197
+ metadata: { mechanism: "launchd" },
4198
+ });
3965
4199
  if (options.json) {
3966
4200
  process.stdout.write(`${JSON.stringify({ ok: true, removed: true })}\n`);
3967
4201
  return 0;
@@ -3987,6 +4221,9 @@ async function runScheduleRemove(options) {
3987
4221
  input: `${filtered.trimEnd()}\n`,
3988
4222
  encoding: "utf8",
3989
4223
  });
4224
+ trackActiveTelemetryEvent("schedule_removed", {
4225
+ metadata: { mechanism: "cron" },
4226
+ });
3990
4227
  if (options.json) {
3991
4228
  process.stdout.write(`${JSON.stringify({ ok: true, removed: true })}\n`);
3992
4229
  return 0;
@@ -4004,6 +4241,9 @@ async function runScheduleRemove(options) {
4004
4241
  execSync(`schtasks /Delete /TN "${WINDOWS_TASK_NAME}" /F`, {
4005
4242
  stdio: "ignore",
4006
4243
  });
4244
+ trackActiveTelemetryEvent("schedule_removed", {
4245
+ metadata: { mechanism: "schtasks" },
4246
+ });
4007
4247
  if (options.json) {
4008
4248
  process.stdout.write(`${JSON.stringify({ ok: true, removed: true })}\n`);
4009
4249
  return 0;
@@ -4158,6 +4398,9 @@ async function runSkillInstall(name, options) {
4158
4398
  const emit = createEmitter(options);
4159
4399
  try {
4160
4400
  const { installedPath } = await installSkill(name);
4401
+ trackActiveTelemetryEvent("skill_installed", {
4402
+ metadata: { skillName: name },
4403
+ });
4161
4404
  if (options.json) {
4162
4405
  process.stdout.write(`${JSON.stringify({ ok: true, id: name, installedPath })}\n`);
4163
4406
  return 0;