@opendatalabs/connect 0.11.4 → 0.11.6-canary.1807d4e
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/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +313 -60
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/telemetry.d.ts +76 -0
- package/dist/cli/telemetry.d.ts.map +1 -0
- package/dist/cli/telemetry.js +444 -0
- package/dist/cli/telemetry.js.map +1 -0
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/paths.d.ts +2 -0
- package/dist/core/paths.d.ts.map +1 -1
- package/dist/core/paths.js +6 -0
- package/dist/core/paths.js.map +1 -1
- package/dist/core/state-store.d.ts +2 -0
- package/dist/core/state-store.d.ts.map +1 -1
- package/dist/core/state-store.js.map +1 -1
- package/dist/personal-server/scope-resolver.d.ts.map +1 -1
- package/dist/personal-server/scope-resolver.js +15 -3
- package/dist/personal-server/scope-resolver.js.map +1 -1
- package/dist/skills/registry.js +2 -2
- package/dist/skills/registry.js.map +1 -1
- package/package.json +1 -1
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, 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,9 +54,16 @@ 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 &&
|
|
66
|
+
installMethod !== "development" &&
|
|
59
67
|
!process.env.VANA_NO_UPDATE_NOTIFIER &&
|
|
60
68
|
!process.env.CI &&
|
|
61
69
|
!process.env.AGENT &&
|
|
@@ -107,17 +115,18 @@ More:
|
|
|
107
115
|
.description("Print CLI version")
|
|
108
116
|
.option("--json", "Output machine-readable JSON")
|
|
109
117
|
.action(async () => {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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
|
+
});
|
|
121
130
|
});
|
|
122
131
|
const connectCommand = program
|
|
123
132
|
.command("connect [source]")
|
|
@@ -129,13 +138,14 @@ More:
|
|
|
129
138
|
.option("--quiet", "Reduce non-essential output")
|
|
130
139
|
.option("--detach", "Run in the background")
|
|
131
140
|
.action(async (source) => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
+
});
|
|
139
149
|
});
|
|
140
150
|
connectCommand.addHelpText("after", `
|
|
141
151
|
Examples:
|
|
@@ -149,9 +159,9 @@ Examples:
|
|
|
149
159
|
.description("List supported sources, or show detail for one source")
|
|
150
160
|
.option("--json", "Output machine-readable JSON")
|
|
151
161
|
.action(async (source) => {
|
|
152
|
-
process.exitCode = source
|
|
153
|
-
?
|
|
154
|
-
:
|
|
162
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "sources", source }, async () => source
|
|
163
|
+
? runSourceDetail(source, parsedOptions)
|
|
164
|
+
: runList(parsedOptions));
|
|
155
165
|
});
|
|
156
166
|
sourcesCommand.addHelpText("after", `
|
|
157
167
|
Examples:
|
|
@@ -170,13 +180,14 @@ Examples:
|
|
|
170
180
|
.option("--detach", "Run in the background")
|
|
171
181
|
.option("--all", "Collect from all connected sources")
|
|
172
182
|
.action(async (source) => {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
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
|
+
});
|
|
180
191
|
});
|
|
181
192
|
collectCommand.addHelpText("after", `
|
|
182
193
|
Examples:
|
|
@@ -189,7 +200,7 @@ Examples:
|
|
|
189
200
|
.description("Show runtime and Personal Server status")
|
|
190
201
|
.option("--json", "Output machine-readable JSON")
|
|
191
202
|
.action(async () => {
|
|
192
|
-
process.exitCode = await runStatus(parsedOptions);
|
|
203
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "status" }, async () => runStatus(parsedOptions));
|
|
193
204
|
});
|
|
194
205
|
statusCommand.addHelpText("after", `
|
|
195
206
|
Examples:
|
|
@@ -201,7 +212,7 @@ Examples:
|
|
|
201
212
|
.description("Inspect local CLI, runtime, and install health")
|
|
202
213
|
.option("--json", "Output machine-readable JSON")
|
|
203
214
|
.action(async () => {
|
|
204
|
-
process.exitCode = await runDoctor(parsedOptions);
|
|
215
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "doctor" }, async () => runDoctor(parsedOptions));
|
|
205
216
|
});
|
|
206
217
|
doctorCommand.addHelpText("after", `
|
|
207
218
|
Examples:
|
|
@@ -214,7 +225,7 @@ Examples:
|
|
|
214
225
|
.option("--json", "Output machine-readable JSON")
|
|
215
226
|
.option("--yes", "Approve safe setup prompts automatically")
|
|
216
227
|
.action(async () => {
|
|
217
|
-
process.exitCode = await runSetup(parsedOptions);
|
|
228
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "setup" }, async () => runSetup(parsedOptions));
|
|
218
229
|
});
|
|
219
230
|
setupCommand.addHelpText("after", `
|
|
220
231
|
Examples:
|
|
@@ -239,7 +250,7 @@ Examples:
|
|
|
239
250
|
.description("List locally available collected datasets")
|
|
240
251
|
.option("--json", "Output machine-readable JSON")
|
|
241
252
|
.action(async () => {
|
|
242
|
-
process.exitCode = await runDataList(parsedOptions);
|
|
253
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "data", subcommand: "list" }, async () => runDataList(parsedOptions));
|
|
243
254
|
});
|
|
244
255
|
dataListCommand.addHelpText("after", `
|
|
245
256
|
Examples:
|
|
@@ -251,7 +262,12 @@ Examples:
|
|
|
251
262
|
.description("Show a collected dataset")
|
|
252
263
|
.option("--json", "Output machine-readable JSON")
|
|
253
264
|
.action(async (source) => {
|
|
254
|
-
process.exitCode = await
|
|
265
|
+
process.exitCode = await runCommandWithTelemetry({
|
|
266
|
+
...telemetryBaseContext,
|
|
267
|
+
command: "data",
|
|
268
|
+
subcommand: "show",
|
|
269
|
+
source,
|
|
270
|
+
}, async () => runDataShow(source, parsedOptions));
|
|
255
271
|
});
|
|
256
272
|
dataShowCommand.addHelpText("after", `
|
|
257
273
|
Examples:
|
|
@@ -263,7 +279,12 @@ Examples:
|
|
|
263
279
|
.description("Print the local path for a collected dataset")
|
|
264
280
|
.option("--json", "Output machine-readable JSON")
|
|
265
281
|
.action(async (source) => {
|
|
266
|
-
process.exitCode = await
|
|
282
|
+
process.exitCode = await runCommandWithTelemetry({
|
|
283
|
+
...telemetryBaseContext,
|
|
284
|
+
command: "data",
|
|
285
|
+
subcommand: "path",
|
|
286
|
+
source,
|
|
287
|
+
}, async () => runDataPath(source, parsedOptions));
|
|
267
288
|
});
|
|
268
289
|
dataPathCommand.addHelpText("after", `
|
|
269
290
|
Examples:
|
|
@@ -275,7 +296,7 @@ Examples:
|
|
|
275
296
|
.description("Inspect stored connector run logs")
|
|
276
297
|
.option("--json", "Output machine-readable JSON")
|
|
277
298
|
.action(async (source) => {
|
|
278
|
-
process.exitCode = await runLogs(source, parsedOptions);
|
|
299
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "logs", source }, async () => runLogs(source, parsedOptions));
|
|
279
300
|
});
|
|
280
301
|
logsCommand.addHelpText("after", `
|
|
281
302
|
Examples:
|
|
@@ -295,62 +316,89 @@ Examples:
|
|
|
295
316
|
vana server clear-url
|
|
296
317
|
`);
|
|
297
318
|
server.action(async () => {
|
|
298
|
-
process.exitCode = await runServerStatus(parsedOptions);
|
|
319
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "server", subcommand: "status" }, async () => runServerStatus(parsedOptions));
|
|
299
320
|
});
|
|
300
321
|
server
|
|
301
322
|
.command("status")
|
|
302
323
|
.description("Show Personal Server status")
|
|
303
324
|
.option("--json", "Output machine-readable JSON")
|
|
304
325
|
.action(async () => {
|
|
305
|
-
process.exitCode = await runServerStatus(parsedOptions);
|
|
326
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "server", subcommand: "status" }, async () => runServerStatus(parsedOptions));
|
|
306
327
|
});
|
|
307
328
|
server
|
|
308
329
|
.command("set-url <url>")
|
|
309
330
|
.description("Save a Personal Server URL")
|
|
310
331
|
.option("--json", "Output machine-readable JSON")
|
|
311
332
|
.action(async (url) => {
|
|
312
|
-
process.exitCode = await runServerSetUrl(url, parsedOptions);
|
|
333
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "server", subcommand: "set-url" }, async () => runServerSetUrl(url, parsedOptions));
|
|
313
334
|
});
|
|
314
335
|
server
|
|
315
336
|
.command("clear-url")
|
|
316
337
|
.description("Remove the saved Personal Server URL")
|
|
317
338
|
.option("--json", "Output machine-readable JSON")
|
|
318
339
|
.action(async () => {
|
|
319
|
-
process.exitCode = await runServerClearUrl(parsedOptions);
|
|
340
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "server", subcommand: "clear-url" }, async () => runServerClearUrl(parsedOptions));
|
|
320
341
|
});
|
|
321
342
|
server
|
|
322
343
|
.command("sync")
|
|
323
344
|
.description("Sync all local-only datasets to your Personal Server")
|
|
324
345
|
.option("--json", "Output machine-readable JSON")
|
|
325
346
|
.action(async () => {
|
|
326
|
-
process.exitCode = await runServerSync(parsedOptions);
|
|
347
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "server", subcommand: "sync" }, async () => runServerSync(parsedOptions));
|
|
327
348
|
});
|
|
328
349
|
server
|
|
329
350
|
.command("data [scope]")
|
|
330
351
|
.description("List scopes stored in your Personal Server")
|
|
331
352
|
.option("--json", "Output machine-readable JSON")
|
|
332
353
|
.action(async (scope) => {
|
|
333
|
-
process.exitCode = await runServerData(scope, parsedOptions);
|
|
354
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "server", subcommand: "data" }, async () => runServerData(scope, parsedOptions));
|
|
334
355
|
});
|
|
335
356
|
program
|
|
336
357
|
.command("login")
|
|
337
358
|
.description("Log in to your Vana account or a self-hosted Personal Server")
|
|
338
359
|
.option("-s, --server <url>", "Self-hosted Personal Server URL")
|
|
339
360
|
.action(async (loginOptions) => {
|
|
340
|
-
process.exitCode = await runLogin(parsedOptions, loginOptions.server);
|
|
361
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "login" }, async () => runLogin(parsedOptions, loginOptions.server));
|
|
341
362
|
});
|
|
342
363
|
program
|
|
343
364
|
.command("logout")
|
|
344
365
|
.description("Log out and remove saved credentials")
|
|
345
366
|
.action(async () => {
|
|
346
|
-
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);
|
|
347
393
|
});
|
|
348
394
|
program
|
|
349
395
|
.command("mcp")
|
|
350
396
|
.description("Start MCP server for agent integration")
|
|
351
397
|
.action(async () => {
|
|
352
|
-
|
|
353
|
-
|
|
398
|
+
process.exitCode = await runLongRunningCommandWithTelemetry({ ...telemetryBaseContext, command: "mcp" }, async () => {
|
|
399
|
+
const { startMcpServer } = await import("./mcp-server.js");
|
|
400
|
+
await startMcpServer();
|
|
401
|
+
});
|
|
354
402
|
});
|
|
355
403
|
const skill = program
|
|
356
404
|
.command("skills")
|
|
@@ -363,26 +411,26 @@ Examples:
|
|
|
363
411
|
vana skills show connect-data
|
|
364
412
|
`);
|
|
365
413
|
skill.action(async () => {
|
|
366
|
-
process.exitCode = await runSkillsGuidedPicker(parsedOptions);
|
|
414
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "skills" }, async () => runSkillsGuidedPicker(parsedOptions));
|
|
367
415
|
});
|
|
368
416
|
skill
|
|
369
417
|
.command("list")
|
|
370
418
|
.description("List available agent skills")
|
|
371
419
|
.option("--json", "Output as JSON")
|
|
372
420
|
.action(async () => {
|
|
373
|
-
process.exitCode = await runSkillList(parsedOptions);
|
|
421
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "skills", subcommand: "list" }, async () => runSkillList(parsedOptions));
|
|
374
422
|
});
|
|
375
423
|
skill
|
|
376
424
|
.command("install <name>")
|
|
377
425
|
.description("Install a skill for your agent")
|
|
378
426
|
.action(async (name) => {
|
|
379
|
-
process.exitCode = await runSkillInstall(name, parsedOptions);
|
|
427
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "skills", subcommand: "install" }, async () => runSkillInstall(name, parsedOptions));
|
|
380
428
|
});
|
|
381
429
|
skill
|
|
382
430
|
.command("show <name>")
|
|
383
431
|
.description("Show skill details")
|
|
384
432
|
.action(async (name) => {
|
|
385
|
-
process.exitCode = await runSkillShow(name, parsedOptions);
|
|
433
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "skills", subcommand: "show" }, async () => runSkillShow(name, parsedOptions));
|
|
386
434
|
});
|
|
387
435
|
// --- Schedule commands ---
|
|
388
436
|
const schedule = program
|
|
@@ -404,20 +452,20 @@ Examples:
|
|
|
404
452
|
.description("Add a scheduled collection")
|
|
405
453
|
.option("--every <interval>", "Collection interval (e.g. 24h, 12h, 1h)", "24h")
|
|
406
454
|
.action(async (opts) => {
|
|
407
|
-
process.exitCode = await runScheduleAdd(opts.every, parsedOptions);
|
|
455
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "schedule", subcommand: "add" }, async () => runScheduleAdd(opts.every, parsedOptions));
|
|
408
456
|
});
|
|
409
457
|
schedule
|
|
410
458
|
.command("list")
|
|
411
459
|
.description("Show scheduled tasks")
|
|
412
460
|
.option("--json", "Output machine-readable JSON")
|
|
413
461
|
.action(async () => {
|
|
414
|
-
process.exitCode = await runScheduleList(parsedOptions);
|
|
462
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "schedule", subcommand: "list" }, async () => runScheduleList(parsedOptions));
|
|
415
463
|
});
|
|
416
464
|
schedule
|
|
417
465
|
.command("remove")
|
|
418
466
|
.description("Remove the scheduled collection")
|
|
419
467
|
.action(async () => {
|
|
420
|
-
process.exitCode = await runScheduleRemove(parsedOptions);
|
|
468
|
+
process.exitCode = await runCommandWithTelemetry({ ...telemetryBaseContext, command: "schedule", subcommand: "remove" }, async () => runScheduleRemove(parsedOptions));
|
|
421
469
|
});
|
|
422
470
|
try {
|
|
423
471
|
await program.parseAsync(normalizedArgv);
|
|
@@ -456,6 +504,139 @@ Examples:
|
|
|
456
504
|
}
|
|
457
505
|
return Number(process.exitCode ?? 0);
|
|
458
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
|
+
}
|
|
459
640
|
async function runConnect(rawSource, options) {
|
|
460
641
|
const source = rawSource.toLowerCase();
|
|
461
642
|
const runtime = new ManagedPlaywrightRuntime();
|
|
@@ -509,7 +690,18 @@ async function runConnect(rawSource, options) {
|
|
|
509
690
|
}
|
|
510
691
|
process.stderr.write("\n");
|
|
511
692
|
}
|
|
512
|
-
|
|
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
|
+
}
|
|
513
705
|
setupLogPath = installResult.logPath;
|
|
514
706
|
emit.event({
|
|
515
707
|
type: "setup-complete",
|
|
@@ -598,6 +790,13 @@ async function runConnect(rawSource, options) {
|
|
|
598
790
|
}
|
|
599
791
|
}
|
|
600
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
|
+
});
|
|
601
800
|
renderer?.detail(`Updated connector (${fetched.previousVersion} → ${fetched.version}).`);
|
|
602
801
|
}
|
|
603
802
|
fetchLogPath = fetched.logPath;
|
|
@@ -1217,7 +1416,7 @@ async function runStatus(options) {
|
|
|
1217
1416
|
// Auth state
|
|
1218
1417
|
const authCreds = loadCredentials();
|
|
1219
1418
|
if (authCreds && !isExpired(authCreds)) {
|
|
1220
|
-
emit.keyValue("Account",
|
|
1419
|
+
emit.keyValue("Account", authCreds.account.address, "success");
|
|
1221
1420
|
emit.keyValue("Auth", `Authenticated (expires in ${formatExpiresIn(authCreds.account.expires_at)})`, "success");
|
|
1222
1421
|
}
|
|
1223
1422
|
else {
|
|
@@ -2195,7 +2394,11 @@ async function runCollectAll(options) {
|
|
|
2195
2394
|
async function runServerSync(options) {
|
|
2196
2395
|
const emit = createEmitter(options);
|
|
2197
2396
|
const target = await detectPersonalServerTarget();
|
|
2397
|
+
trackActiveTelemetryEvent("server_sync_started");
|
|
2198
2398
|
if (target.state !== "available") {
|
|
2399
|
+
trackActiveTelemetryEvent("server_sync_failed", {
|
|
2400
|
+
errorClass: "personal_server_unavailable",
|
|
2401
|
+
});
|
|
2199
2402
|
if (options.json) {
|
|
2200
2403
|
process.stdout.write(`${JSON.stringify({
|
|
2201
2404
|
error: "personal_server_unavailable",
|
|
@@ -2208,7 +2411,16 @@ async function runServerSync(options) {
|
|
|
2208
2411
|
return 1;
|
|
2209
2412
|
}
|
|
2210
2413
|
const syncResult = await syncPendingSources(target, "manual");
|
|
2414
|
+
const storedScopeCount = syncResult.sourceResults.reduce((total, entry) => total +
|
|
2415
|
+
(entry.scopeResults?.filter((scopeResult) => scopeResult.status === "stored").length ?? 0), 0);
|
|
2416
|
+
const failedScopeCount = syncResult.sourceResults.reduce((total, entry) => total +
|
|
2417
|
+
(entry.scopeResults?.filter((scopeResult) => scopeResult.status === "failed").length ?? 0), 0);
|
|
2211
2418
|
if (syncResult.sourceResults.length === 0) {
|
|
2419
|
+
trackActiveTelemetryEvent("server_sync_completed", {
|
|
2420
|
+
storedScopeCount: 0,
|
|
2421
|
+
failedScopeCount: 0,
|
|
2422
|
+
metadata: { syncedSources: 0 },
|
|
2423
|
+
});
|
|
2212
2424
|
if (options.json) {
|
|
2213
2425
|
process.stdout.write(`${JSON.stringify({ message: "No pending datasets to sync.", syncedCount: 0 })}\n`);
|
|
2214
2426
|
}
|
|
@@ -2231,7 +2443,7 @@ async function runServerSync(options) {
|
|
|
2231
2443
|
emit.info(` ${renderer.theme.success("\u2713")} ${sr.scope}`);
|
|
2232
2444
|
}
|
|
2233
2445
|
else {
|
|
2234
|
-
const errDetail = sr.error
|
|
2446
|
+
const errDetail = sr.error ? humanizeIssue(sr.error) : "Failed";
|
|
2235
2447
|
emit.info(` ${renderer.theme.error("\u2717")} ${sr.scope} ${renderer.theme.muted(`\u2014 ${errDetail}`)}`);
|
|
2236
2448
|
}
|
|
2237
2449
|
}
|
|
@@ -2249,6 +2461,11 @@ async function runServerSync(options) {
|
|
|
2249
2461
|
emit.next("vana server sync");
|
|
2250
2462
|
}
|
|
2251
2463
|
}
|
|
2464
|
+
trackActiveTelemetryEvent("server_sync_completed", {
|
|
2465
|
+
storedScopeCount,
|
|
2466
|
+
failedScopeCount,
|
|
2467
|
+
metadata: { syncedSources: syncResult.syncedCount },
|
|
2468
|
+
});
|
|
2252
2469
|
return 0;
|
|
2253
2470
|
}
|
|
2254
2471
|
async function runServerData(scope, options) {
|
|
@@ -3559,6 +3776,7 @@ async function runDetached(command, source, options) {
|
|
|
3559
3776
|
stdio: ["ignore", logFd, logFd],
|
|
3560
3777
|
env: { ...process.env, VANA_DETACHED: "1" },
|
|
3561
3778
|
});
|
|
3779
|
+
trackActiveTelemetryEvent("detached_run_spawned", { source });
|
|
3562
3780
|
child.unref();
|
|
3563
3781
|
fs.closeSync(logFd);
|
|
3564
3782
|
// Write session file
|
|
@@ -3763,6 +3981,9 @@ async function runScheduleAdd(interval, options) {
|
|
|
3763
3981
|
emit.detail(`launchctl load "${LAUNCHD_PLIST_PATH}"`);
|
|
3764
3982
|
return 1;
|
|
3765
3983
|
}
|
|
3984
|
+
trackActiveTelemetryEvent("schedule_added", {
|
|
3985
|
+
metadata: { interval: intervalLabel, mechanism: "launchd" },
|
|
3986
|
+
});
|
|
3766
3987
|
if (options.json) {
|
|
3767
3988
|
process.stdout.write(`${JSON.stringify({ ok: true, interval: intervalLabel, mechanism: "launchd", plistPath: LAUNCHD_PLIST_PATH })}\n`);
|
|
3768
3989
|
return 0;
|
|
@@ -3803,6 +4024,9 @@ async function runScheduleAdd(interval, options) {
|
|
|
3803
4024
|
emit.detail(entry);
|
|
3804
4025
|
return 1;
|
|
3805
4026
|
}
|
|
4027
|
+
trackActiveTelemetryEvent("schedule_added", {
|
|
4028
|
+
metadata: { interval: intervalLabel, mechanism: "cron" },
|
|
4029
|
+
});
|
|
3806
4030
|
if (options.json) {
|
|
3807
4031
|
process.stdout.write(`${JSON.stringify({ ok: true, interval: intervalLabel, mechanism: "cron" })}\n`);
|
|
3808
4032
|
return 0;
|
|
@@ -3844,6 +4068,9 @@ async function runScheduleAdd(interval, options) {
|
|
|
3844
4068
|
emit.detail(`schtasks /Create /TN "${WINDOWS_TASK_NAME}" /TR "${trCmd}" /SC DAILY /ST 09:00 /F`);
|
|
3845
4069
|
return 1;
|
|
3846
4070
|
}
|
|
4071
|
+
trackActiveTelemetryEvent("schedule_added", {
|
|
4072
|
+
metadata: { interval: intervalLabel, mechanism: "schtasks" },
|
|
4073
|
+
});
|
|
3847
4074
|
if (options.json) {
|
|
3848
4075
|
process.stdout.write(`${JSON.stringify({ ok: true, interval: intervalLabel, mechanism: "schtasks" })}\n`);
|
|
3849
4076
|
return 0;
|
|
@@ -3961,6 +4188,9 @@ async function runScheduleRemove(options) {
|
|
|
3961
4188
|
// Already unloaded
|
|
3962
4189
|
}
|
|
3963
4190
|
await fsp.unlink(LAUNCHD_PLIST_PATH);
|
|
4191
|
+
trackActiveTelemetryEvent("schedule_removed", {
|
|
4192
|
+
metadata: { mechanism: "launchd" },
|
|
4193
|
+
});
|
|
3964
4194
|
if (options.json) {
|
|
3965
4195
|
process.stdout.write(`${JSON.stringify({ ok: true, removed: true })}\n`);
|
|
3966
4196
|
return 0;
|
|
@@ -3986,6 +4216,9 @@ async function runScheduleRemove(options) {
|
|
|
3986
4216
|
input: `${filtered.trimEnd()}\n`,
|
|
3987
4217
|
encoding: "utf8",
|
|
3988
4218
|
});
|
|
4219
|
+
trackActiveTelemetryEvent("schedule_removed", {
|
|
4220
|
+
metadata: { mechanism: "cron" },
|
|
4221
|
+
});
|
|
3989
4222
|
if (options.json) {
|
|
3990
4223
|
process.stdout.write(`${JSON.stringify({ ok: true, removed: true })}\n`);
|
|
3991
4224
|
return 0;
|
|
@@ -4003,6 +4236,9 @@ async function runScheduleRemove(options) {
|
|
|
4003
4236
|
execSync(`schtasks /Delete /TN "${WINDOWS_TASK_NAME}" /F`, {
|
|
4004
4237
|
stdio: "ignore",
|
|
4005
4238
|
});
|
|
4239
|
+
trackActiveTelemetryEvent("schedule_removed", {
|
|
4240
|
+
metadata: { mechanism: "schtasks" },
|
|
4241
|
+
});
|
|
4006
4242
|
if (options.json) {
|
|
4007
4243
|
process.stdout.write(`${JSON.stringify({ ok: true, removed: true })}\n`);
|
|
4008
4244
|
return 0;
|
|
@@ -4157,6 +4393,9 @@ async function runSkillInstall(name, options) {
|
|
|
4157
4393
|
const emit = createEmitter(options);
|
|
4158
4394
|
try {
|
|
4159
4395
|
const { installedPath } = await installSkill(name);
|
|
4396
|
+
trackActiveTelemetryEvent("skill_installed", {
|
|
4397
|
+
metadata: { skillName: name },
|
|
4398
|
+
});
|
|
4160
4399
|
if (options.json) {
|
|
4161
4400
|
process.stdout.write(`${JSON.stringify({ ok: true, id: name, installedPath })}\n`);
|
|
4162
4401
|
return 0;
|
|
@@ -4233,11 +4472,14 @@ async function runLogin(options, serverUrl) {
|
|
|
4233
4472
|
// If self-hosted, use /auth/device flow against the PS
|
|
4234
4473
|
if (authTarget === "self-hosted" && psUrl) {
|
|
4235
4474
|
const renderer = !options.json && !options.quiet ? createLoginRenderer() : null;
|
|
4475
|
+
const humanRenderer = createHumanRenderer();
|
|
4236
4476
|
renderer?.title(psUrl);
|
|
4237
4477
|
try {
|
|
4238
4478
|
const result = await runSelfHostedLoginFlow(psUrl, (url) => {
|
|
4239
|
-
|
|
4240
|
-
|
|
4479
|
+
if (!options.json && !options.quiet) {
|
|
4480
|
+
process.stderr.write(` ${humanRenderer.theme.muted("Open this URL in your browser:")}\n`);
|
|
4481
|
+
process.stderr.write(` ${humanRenderer.theme.muted(url)}\n`);
|
|
4482
|
+
}
|
|
4241
4483
|
renderer?.scopeActive("Waiting for authorization");
|
|
4242
4484
|
// Try to open browser — use spawn with args array to prevent shell injection
|
|
4243
4485
|
// (a malicious self-hosted PS could return a URL with shell metacharacters)
|
|
@@ -4353,12 +4595,23 @@ async function runLogin(options, serverUrl) {
|
|
|
4353
4595
|
}
|
|
4354
4596
|
// Interactive mode
|
|
4355
4597
|
const renderer = createLoginRenderer();
|
|
4598
|
+
const humanRenderer = createHumanRenderer();
|
|
4599
|
+
const writeStaticLoginNotes = (lines) => {
|
|
4600
|
+
if (options.json || options.quiet) {
|
|
4601
|
+
return;
|
|
4602
|
+
}
|
|
4603
|
+
for (const line of lines) {
|
|
4604
|
+
process.stderr.write(` ${humanRenderer.theme.muted(line)}\n`);
|
|
4605
|
+
}
|
|
4606
|
+
};
|
|
4356
4607
|
renderer.title("Vana");
|
|
4357
4608
|
const creds = await runDeviceCodeFlow({
|
|
4358
4609
|
onCode: (code, uri) => {
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4610
|
+
writeStaticLoginNotes([
|
|
4611
|
+
"Open this URL in your browser:",
|
|
4612
|
+
uri,
|
|
4613
|
+
`Enter this code: ${code}`,
|
|
4614
|
+
]);
|
|
4362
4615
|
},
|
|
4363
4616
|
onWaiting: () => {
|
|
4364
4617
|
renderer.scopeActive("Waiting for authorization");
|