@vike-io/cli 0.7.1 → 0.7.2

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/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.7.1",
6
+ "version": "0.7.2",
7
7
  "description": "AI-agent CLI for vike.io on-chain analytics, perps, options, and prediction markets across 11 chains",
8
8
  "type": "module",
9
9
  "main": "src/index.js",
@@ -65,11 +65,11 @@
65
65
  "node": ">=20.0.0"
66
66
  },
67
67
  "dependencies": {
68
- "commander": "^12.1.0"
68
+ "commander": "^14.0.3"
69
69
  },
70
70
  "devDependencies": {
71
71
  "@changesets/cli": "^2.27.9",
72
- "eslint": "^9.15.0",
73
- "vitest": "^2.1.5"
72
+ "eslint": "^10.4.0",
73
+ "vitest": "^4.1.7"
74
74
  }
75
75
  }
package/src/index.js CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  getUpgradeNotice,
22
22
  scheduleUpdateCheck,
23
23
  } from './update-check.js';
24
+ import { trackCommandSucceeded, trackCommandFailed } from './telemetry.js';
24
25
 
25
26
  const version = readPackageVersion();
26
27
 
@@ -38,6 +39,19 @@ program
38
39
  .description('AI-agent CLI for vike.io on-chain analytics')
39
40
  .version(version, '-v, --version', 'Show CLI version');
40
41
 
42
+ // Telemetry: time each command from preAction to postAction; failure path is
43
+ // caught below via parseAsync().catch().
44
+ const _cmdStart = new WeakMap();
45
+ program.hook('preAction', (_thisCmd, actionCmd) => {
46
+ _cmdStart.set(actionCmd, Date.now());
47
+ });
48
+ program.hook('postAction', (_thisCmd, actionCmd) => {
49
+ const t0 = _cmdStart.get(actionCmd);
50
+ trackCommandSucceeded(actionCmd.name(), actionCmd.opts(), {
51
+ durationMs: t0 ? Date.now() - t0 : null,
52
+ });
53
+ });
54
+
41
55
  registerLogin(program);
42
56
  registerLogout(program);
43
57
  registerInit(program);
@@ -52,10 +66,16 @@ registerPolymarket(program);
52
66
  registerLabels(program);
53
67
  registerWeb(program);
54
68
 
69
+ const _parseT0 = Date.now();
55
70
  program.parseAsync(process.argv)
56
71
  .catch((err) => {
57
72
  console.error(`vike: ${err.message ?? err}`);
58
73
  process.exitCode = 1;
74
+ const cmd = program.args[0] || 'unknown';
75
+ trackCommandFailed(cmd, {}, {
76
+ errorCode: err?.code || err?.name || 'error',
77
+ durationMs: Date.now() - _parseT0,
78
+ });
59
79
  })
60
80
  .finally(() => {
61
81
  // Fire-and-forget background check, detached subprocess. Survives our exit.
package/src/telemetry.js CHANGED
@@ -1,4 +1,4 @@
1
- // Anonymous, opt-out telemetry skeleton.
1
+ // Anonymous, opt-out telemetry.
2
2
  //
3
3
  // Design decisions:
4
4
  // - Honors DO_NOT_TRACK=1 (industry standard) AND VIKE_NO_TELEMETRY=1
@@ -7,25 +7,24 @@
7
7
  // - Rolling 30-min session ID — same session_id within a 30-min window
8
8
  // - AbortController + 1000ms timeout, timer.unref() so we can't keep
9
9
  // the process alive
10
- // - Payload includes: flag NAMES (no values), latency, success/error_code,
11
- // chain (if relevant). NEVER the actual data the user queried.
10
+ // - Payload includes: flag NAMES (no values), latency, success/error_code.
11
+ // NEVER the actual data the user queried (no addresses, no symbols).
12
12
  // - Fire-and-forget: errors are swallowed
13
- //
14
- // THE SINK IS NOT YET WIRED. `_send()` is a no-op until we have an
15
- // endpoint. Keeping the API shape stable now so we can flip the switch
16
- // later without a refactor.
17
13
 
18
- import { existsSync, mkdirSync, readFileSync, writeFileSync, statSync } from 'node:fs';
14
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
19
15
  import { homedir } from 'node:os';
20
16
  import { join } from 'node:path';
21
17
  import { randomUUID } from 'node:crypto';
18
+ import { readPackageVersion } from './util.js';
22
19
 
23
20
  const CONFIG_DIR = join(homedir(), '.vike');
24
21
  const ID_FILE = join(CONFIG_DIR, 'telemetry-id');
25
22
  const SESSION_FILE = join(CONFIG_DIR, 'session');
26
23
  const SESSION_TTL_MS = 30 * 60 * 1000;
27
- const ENDPOINT = process.env.VIKE_TELEMETRY_ENDPOINT || '';
24
+ const ENDPOINT = process.env.VIKE_TELEMETRY_ENDPOINT
25
+ || 'https://vike.io/api/cli-telemetry';
28
26
  const REQUEST_TIMEOUT_MS = 1000;
27
+ const CLI_VERSION = (() => { try { return readPackageVersion(); } catch { return ''; } })();
29
28
 
30
29
  function disabled() {
31
30
  return process.env.DO_NOT_TRACK === '1' ||
@@ -82,9 +81,13 @@ function _send(event, payload) {
82
81
  if (typeof timer.unref === 'function') timer.unref();
83
82
  fetch(ENDPOINT, {
84
83
  method: 'POST',
85
- headers: { 'Content-Type': 'application/json' },
84
+ headers: {
85
+ 'Content-Type': 'application/json',
86
+ 'User-Agent': `vike-cli/${CLI_VERSION}`,
87
+ },
86
88
  body: JSON.stringify({
87
89
  event,
90
+ cli_version: CLI_VERSION,
88
91
  device_id: getDeviceId(),
89
92
  session_id: getSessionId(),
90
93
  ts: Date.now(),
@@ -98,8 +101,11 @@ function _send(event, payload) {
98
101
 
99
102
  function flagNames(opts) {
100
103
  // Extract flag names from a commander options object. Values stripped.
104
+ // Convert camelCase to kebab-case for readability in analytics.
101
105
  if (!opts || typeof opts !== 'object') return [];
102
- return Object.keys(opts).filter((k) => !k.startsWith('_') && k !== 'parent');
106
+ return Object.keys(opts)
107
+ .filter((k) => !k.startsWith('_') && k !== 'parent')
108
+ .map((k) => '--' + k.replace(/[A-Z]/g, (m) => '-' + m.toLowerCase()));
103
109
  }
104
110
 
105
111
  export function trackCommandSucceeded(command, opts, { durationMs } = {}) {
@@ -11,8 +11,7 @@
11
11
  import { spawn } from 'node:child_process';
12
12
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
13
13
  import { homedir } from 'node:os';
14
- import { dirname, join } from 'node:path';
15
- import { fileURLToPath } from 'node:url';
14
+ import { join } from 'node:path';
16
15
 
17
16
  const CONFIG_DIR = join(homedir(), '.vike');
18
17
  const CHECK_FILE = join(CONFIG_DIR, 'update-check.json');
@@ -33,13 +32,6 @@ function readCache() {
33
32
  try { return JSON.parse(readFileSync(CHECK_FILE, 'utf8')); } catch { return null; }
34
33
  }
35
34
 
36
- function writeCache(latest) {
37
- try {
38
- if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
39
- writeFileSync(CHECK_FILE, JSON.stringify({ latest, checkedAt: Date.now() }, null, 2));
40
- } catch { /* ignore */ }
41
- }
42
-
43
35
  function cmpSemver(a, b) {
44
36
  const pa = a.split('.').map(Number);
45
37
  const pb = b.split('.').map(Number);
@@ -73,7 +65,6 @@ export function scheduleUpdateCheck() {
73
65
  const cache = readCache();
74
66
  if (cache && Date.now() - (cache.checkedAt || 0) < CHECK_INTERVAL_MS) return;
75
67
 
76
- const __filename = fileURLToPath(import.meta.url);
77
68
  // Inline child script — no separate file needed, no dependency on
78
69
  // package layout, survives `npm install -g` symlinking.
79
70
  const code = `