@nitrostack/cli 1.0.9 → 1.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.
package/README.md CHANGED
@@ -37,7 +37,7 @@ npm install -g @nitrostack/cli
37
37
  nitrostack-cli init my-project
38
38
  ```
39
39
 
40
- ![nitrocli init](https://raw.githubusercontent.com/nitrocloudofficial/nitrostack/main/typescript/packages/cli/assets/gif/nitrocli.gif)
40
+ ![nitrocli init](./assets/gif/nitrocli.gif)
41
41
 
42
42
  ### Start Development Server
43
43
  ```bash
@@ -60,7 +60,7 @@ nitrostack-cli build
60
60
 
61
61
  NitroStudio works seamlessly with your projects. You don't even need to run a dev command—**opening your project in NitroStudio starts the server automatically.**
62
62
 
63
- ![NitroStudio](https://raw.githubusercontent.com/nitrocloudofficial/nitrostack/main/typescript/packages/cli/assets/gif/nitrostudio-main.gif)
63
+ ![NitroStudio](./assets/gif/nitrostudio-main.gif)
64
64
 
65
65
  **[Download NitroStudio](https://nitrostack.ai/studio)**
66
66
 
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Capture an analytics event. Non-blocking, fire-and-forget.
3
+ */
4
+ export declare function trackEvent(event: string, properties?: Record<string, unknown>): void;
5
+ /**
6
+ * Flush pending events and shut down. Call before process.exit().
7
+ */
8
+ export declare function shutdownAnalytics(): Promise<void>;
9
+ //# sourceMappingURL=posthog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"posthog.d.ts","sourceRoot":"","sources":["../../src/analytics/posthog.ts"],"names":[],"mappings":"AAyDA;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,IAAI,CAaxF;AAED;;GAEG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CASvD"}
@@ -0,0 +1,84 @@
1
+ import { PostHog } from 'posthog-node';
2
+ import { createHash } from 'crypto';
3
+ import os from 'os';
4
+ import { createRequire } from 'module';
5
+ const POSTHOG_API_KEY = 'phc_OufG1OuiamSbCBMVHfO70IyFWKBzsiaDpOWqcNwtz6G';
6
+ const POSTHOG_HOST = 'https://us.i.posthog.com';
7
+ let client = null;
8
+ let distinctId = null;
9
+ let cliVersion = null;
10
+ function getClient() {
11
+ if (!client) {
12
+ client = new PostHog(POSTHOG_API_KEY, {
13
+ host: POSTHOG_HOST,
14
+ flushAt: 1,
15
+ flushInterval: 0,
16
+ });
17
+ }
18
+ return client;
19
+ }
20
+ function getDistinctId() {
21
+ if (!distinctId) {
22
+ try {
23
+ const raw = `${os.hostname()}:${os.userInfo().username}`;
24
+ distinctId = createHash('sha256').update(raw).digest('hex').slice(0, 16);
25
+ }
26
+ catch {
27
+ distinctId = 'anonymous';
28
+ }
29
+ }
30
+ return distinctId;
31
+ }
32
+ function getCliVersion() {
33
+ if (!cliVersion) {
34
+ try {
35
+ const req = createRequire(import.meta.url);
36
+ const pkg = req('../../package.json');
37
+ cliVersion = pkg.version ?? 'unknown';
38
+ }
39
+ catch {
40
+ cliVersion = 'unknown';
41
+ }
42
+ }
43
+ return cliVersion;
44
+ }
45
+ function getGlobalProperties() {
46
+ return {
47
+ cli_version: getCliVersion(),
48
+ node_version: process.version,
49
+ os_platform: process.platform,
50
+ os_arch: process.arch,
51
+ };
52
+ }
53
+ /**
54
+ * Capture an analytics event. Non-blocking, fire-and-forget.
55
+ */
56
+ export function trackEvent(event, properties = {}) {
57
+ try {
58
+ getClient().capture({
59
+ distinctId: getDistinctId(),
60
+ event,
61
+ properties: {
62
+ ...getGlobalProperties(),
63
+ ...properties,
64
+ },
65
+ });
66
+ }
67
+ catch {
68
+ // Never let analytics break CLI functionality
69
+ }
70
+ }
71
+ /**
72
+ * Flush pending events and shut down. Call before process.exit().
73
+ */
74
+ export async function shutdownAnalytics() {
75
+ if (client) {
76
+ try {
77
+ await client.shutdown();
78
+ }
79
+ catch {
80
+ // Ignore shutdown errors
81
+ }
82
+ client = null;
83
+ }
84
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAkBA,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,iBAiNvD"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAmBA,UAAU,YAAY;IACpB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,iBAmOvD"}
@@ -3,10 +3,15 @@ import path from 'path';
3
3
  import fs from 'fs';
4
4
  import * as esbuild from 'esbuild';
5
5
  import { createHeader, createSuccessBox, createErrorBox, NitroSpinner, spacer, nextSteps, NITRO_BANNER_FULL, showFooter } from '../ui/branding.js';
6
+ import { trackEvent, shutdownAnalytics } from '../analytics/posthog.js';
6
7
  export async function buildCommand(options) {
7
8
  console.log(NITRO_BANNER_FULL);
8
9
  console.log(createHeader('Build', 'Production bundle'));
9
10
  const startTime = Date.now();
11
+ trackEvent('cli_command_invoked', {
12
+ command: 'build',
13
+ options: Object.keys(options).filter(k => options[k] !== undefined),
14
+ });
10
15
  // Validate project
11
16
  const packageJsonPath = path.join(process.cwd(), 'package.json');
12
17
  if (!fs.existsSync(packageJsonPath)) {
@@ -184,11 +189,22 @@ function init() {
184
189
  'nitrostack start - Alternative start command',
185
190
  ]);
186
191
  showFooter();
192
+ trackEvent('cli_build_completed', {
193
+ has_widgets: hasWidgets,
194
+ widget_count: buildArtifacts.length - 1,
195
+ duration_ms: Date.now() - startTime,
196
+ });
197
+ await shutdownAnalytics();
187
198
  }
188
199
  catch (error) {
189
200
  console.log(NITRO_BANNER_FULL);
190
201
  spacer();
191
202
  console.log(createErrorBox('Build Failed', error instanceof Error ? error.message : String(error)));
203
+ trackEvent('cli_build_failed', {
204
+ error: (error instanceof Error ? error.message : String(error)).slice(0, 200),
205
+ stage: hasWidgets ? 'widgets_or_typescript' : 'typescript',
206
+ });
207
+ await shutdownAnalytics();
192
208
  process.exit(1);
193
209
  }
194
210
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AA0KA,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,UAAU,iBAoPnD"}
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"AA2KA,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE,UAAU,iBAsQnD"}
@@ -7,6 +7,7 @@ import { dirname } from 'path';
7
7
  import chokidar from 'chokidar';
8
8
  import ora from 'ora';
9
9
  import { NITRO_BANNER_FULL, createHeader, createBox, createErrorBox, showFooter } from '../ui/branding.js';
10
+ import { trackEvent, shutdownAnalytics } from '../analytics/posthog.js';
10
11
  // ═══════════════════════════════════════════════════════════════════════════
11
12
  // BRANDING & UI COMPONENTS
12
13
  // ═══════════════════════════════════════════════════════════════════════════
@@ -134,11 +135,21 @@ export async function devCommand(options) {
134
135
  let tscWatchProcess = null;
135
136
  let widgetsDevProcess = null;
136
137
  let isShuttingDown = false;
137
- const shutdown = (code = 0) => {
138
+ const devStartTime = Date.now();
139
+ trackEvent('cli_command_invoked', {
140
+ command: 'dev',
141
+ options: Object.keys(options).filter(k => options[k] !== undefined),
142
+ });
143
+ const shutdown = async (code = 0) => {
138
144
  if (isShuttingDown)
139
145
  return;
140
146
  isShuttingDown = true;
141
147
  ui.showShutdown();
148
+ trackEvent('cli_dev_stopped', {
149
+ session_duration_ms: Date.now() - devStartTime,
150
+ exit_reason: code === 0 ? 'sigint' : 'error',
151
+ });
152
+ await shutdownAnalytics();
142
153
  if (tscWatchProcess) {
143
154
  tscWatchProcess.kill();
144
155
  tscWatchProcess = null;
@@ -319,6 +330,11 @@ export async function devCommand(options) {
319
330
  mcpServerPath: distIndexPath,
320
331
  projectPath,
321
332
  });
333
+ trackEvent('cli_dev_started', {
334
+ has_widgets: hasWidgets,
335
+ port: widgetsPort,
336
+ startup_duration_ms: Date.now() - devStartTime,
337
+ });
322
338
  // Set up log forwarding for errors only
323
339
  widgetsDevProcess?.stderr?.on('data', (data) => {
324
340
  const output = data.toString();
@@ -1 +1 @@
1
- {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAgUA,UAAU,eAAe;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsGvG"}
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../src/commands/generate.ts"],"names":[],"mappings":"AAiUA,UAAU,eAAe;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwHvG"}
@@ -2,6 +2,7 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import chalk from 'chalk';
4
4
  import { createHeader, createSuccessBox, createErrorBox, log, spacer, nextSteps, NITRO_BANNER_FULL, showFooter } from '../ui/branding.js';
5
+ import { trackEvent, shutdownAnalytics } from '../analytics/posthog.js';
5
6
  /**
6
7
  * Generator templates
7
8
  */
@@ -296,6 +297,10 @@ function getFilePath(type, name, module) {
296
297
  */
297
298
  export async function generate(type, name, options = {}) {
298
299
  console.log(NITRO_BANNER_FULL);
300
+ trackEvent('cli_command_invoked', {
301
+ command: 'generate',
302
+ options: Object.keys(options).filter(k => options[k] !== undefined),
303
+ });
299
304
  // Special case for types generation
300
305
  if (type === 'types') {
301
306
  const { generateTypes } = await import('./generate-types.js');
@@ -308,6 +313,8 @@ export async function generate(type, name, options = {}) {
308
313
  if (!validTypes.includes(type)) {
309
314
  spacer();
310
315
  console.log(createErrorBox('Invalid Type', `Valid types: ${validTypes.join(', ')}, types`));
316
+ trackEvent('cli_generate_failed', { component_type: type, error: 'Invalid type' });
317
+ await shutdownAnalytics();
311
318
  process.exit(1);
312
319
  }
313
320
  // Get template
@@ -337,6 +344,8 @@ export async function generate(type, name, options = {}) {
337
344
  spacer();
338
345
  console.log(createErrorBox('File Exists', 'Use --force to overwrite if you want to replace the existing file.'));
339
346
  log(`Path: ${path.relative(process.cwd(), filePath)}`, 'dim');
347
+ trackEvent('cli_generate_failed', { component_type: type, error: 'File exists' });
348
+ await shutdownAnalytics();
340
349
  process.exit(1);
341
350
  }
342
351
  // Write file
@@ -380,4 +389,11 @@ export async function generate(type, name, options = {}) {
380
389
  'Build and test your changes',
381
390
  ]);
382
391
  showFooter();
392
+ trackEvent('cli_generate_completed', {
393
+ component_type: type,
394
+ has_module_flag: !!options.module,
395
+ files_generated: generatedFiles.length,
396
+ used_force: !!options.force,
397
+ });
398
+ await shutdownAnalytics();
383
399
  }
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAoDA,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,WAAW,iBA4NtF"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAqDA,UAAU,WAAW;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,wBAAsB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,WAAW,iBA+OtF"}
@@ -6,6 +6,7 @@ import chalk from 'chalk';
6
6
  import inquirer from 'inquirer';
7
7
  import { execSync } from 'child_process';
8
8
  import { NITRO_BANNER_FULL, createSuccessBox, NitroSpinner, log, spacer, nextSteps, brand, showFooter } from '../ui/branding.js';
9
+ import { trackEvent, shutdownAnalytics } from '../analytics/posthog.js';
9
10
  // ES module equivalent of __dirname
10
11
  const __filename = fileURLToPath(import.meta.url);
11
12
  const __dirname = dirname(__filename);
@@ -37,6 +38,11 @@ function isLocalDevelopment() {
37
38
  }
38
39
  export async function initCommand(projectName, options) {
39
40
  let spinner = null;
41
+ const startTime = Date.now();
42
+ trackEvent('cli_command_invoked', {
43
+ command: 'init',
44
+ options: Object.keys(options).filter(k => options[k] !== undefined),
45
+ });
40
46
  try {
41
47
  // Show banner
42
48
  console.log(NITRO_BANNER_FULL);
@@ -227,6 +233,13 @@ export async function initCommand(projectName, options) {
227
233
  }
228
234
  console.log(chalk.dim(' Happy coding! 🎉\n'));
229
235
  showFooter();
236
+ trackEvent('cli_init_completed', {
237
+ template: finalTemplate,
238
+ skip_install: !!options.skipInstall,
239
+ has_custom_description: finalDescription !== 'My awesome MCP server',
240
+ duration_ms: Date.now() - startTime,
241
+ });
242
+ await shutdownAnalytics();
230
243
  }
231
244
  catch (error) {
232
245
  if (spinner) {
@@ -234,6 +247,10 @@ export async function initCommand(projectName, options) {
234
247
  }
235
248
  spacer();
236
249
  log(error instanceof Error ? error.message : String(error), 'error');
250
+ trackEvent('cli_init_failed', {
251
+ error: (error instanceof Error ? error.message : String(error)).slice(0, 200),
252
+ });
253
+ await shutdownAnalytics();
237
254
  process.exit(1);
238
255
  }
239
256
  }
@@ -1 +1 @@
1
- {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAiBA,UAAU,cAAc;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAoBD;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAkE3E"}
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/commands/install.ts"],"names":[],"mappings":"AAkBA,UAAU,cAAc;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAoBD;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAsF3E"}
@@ -2,6 +2,7 @@ import { execSync } from 'child_process';
2
2
  import path from 'path';
3
3
  import fs from 'fs-extra';
4
4
  import { createHeader, createSuccessBox, createErrorBox, NitroSpinner, log, spacer, nextSteps, NITRO_BANNER_FULL, showFooter } from '../ui/branding.js';
5
+ import { trackEvent, shutdownAnalytics } from '../analytics/posthog.js';
5
6
  /**
6
7
  * Run npm install silently
7
8
  */
@@ -24,6 +25,11 @@ function hasPackageJson(directory) {
24
25
  export async function installCommand(options) {
25
26
  console.log(NITRO_BANNER_FULL);
26
27
  console.log(createHeader('Install', 'Install project dependencies'));
28
+ const startTime = Date.now();
29
+ trackEvent('cli_command_invoked', {
30
+ command: 'install',
31
+ options: Object.keys(options).filter(k => options[k] !== undefined),
32
+ });
27
33
  const projectRoot = process.cwd();
28
34
  const rootPackageJsonPath = path.join(projectRoot, 'package.json');
29
35
  const widgetsPath = path.join(projectRoot, 'src', 'widgets');
@@ -49,6 +55,11 @@ export async function installCommand(options) {
49
55
  catch (error) {
50
56
  rootSpinner.fail('Failed to install root dependencies');
51
57
  console.log(createErrorBox('Installation Failed', error instanceof Error ? error.message : String(error)));
58
+ trackEvent('cli_install_failed', {
59
+ error: (error instanceof Error ? error.message : String(error)).slice(0, 200),
60
+ stage: 'root',
61
+ });
62
+ await shutdownAnalytics();
52
63
  process.exit(1);
53
64
  }
54
65
  // Install widget dependencies if they exist
@@ -79,4 +90,11 @@ export async function installCommand(options) {
79
90
  'npm run upgrade - Upgrade nitrostack',
80
91
  ]);
81
92
  showFooter();
93
+ trackEvent('cli_install_completed', {
94
+ has_widgets: hasPackageJson(widgetsPath),
95
+ skip_widgets: skipWidgets,
96
+ production,
97
+ duration_ms: Date.now() - startTime,
98
+ });
99
+ await shutdownAnalytics();
82
100
  }
@@ -1 +1 @@
1
- {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAgBA,UAAU,YAAY;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,iBA6EvD"}
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAiBA,UAAU,YAAY;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,iBAwFvD"}
@@ -3,10 +3,15 @@ import { spawn } from 'child_process';
3
3
  import path from 'path';
4
4
  import fs from 'fs';
5
5
  import { createHeader, createBox, createInfoBox, createErrorBox, log, spacer, NITRO_BANNER_FULL, showFooter } from '../ui/branding.js';
6
+ import { trackEvent } from '../analytics/posthog.js';
6
7
  export async function startCommand(options) {
7
8
  console.log(NITRO_BANNER_FULL);
8
9
  console.log(createHeader('Start', 'Production mode'));
9
10
  const port = options.port || '3000';
11
+ trackEvent('cli_command_invoked', {
12
+ command: 'start',
13
+ options: Object.keys(options).filter(k => options[k] !== undefined),
14
+ });
10
15
  // Check if dist/index.js exists
11
16
  const distIndexPath = path.join(process.cwd(), 'dist', 'index.js');
12
17
  if (!fs.existsSync(distIndexPath)) {
@@ -34,6 +39,11 @@ export async function startCommand(options) {
34
39
  ]));
35
40
  log('Starting server...', 'info');
36
41
  spacer();
42
+ trackEvent('cli_start_executed', {
43
+ has_built_widgets: hasBuiltWidgets,
44
+ port: Number(port),
45
+ build_exists: true,
46
+ });
37
47
  // Start the MCP server
38
48
  const serverProcess = spawn('node', [distIndexPath], {
39
49
  cwd: process.cwd(),
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AAyBA,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAwHD;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAqI3E"}
1
+ {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../../src/commands/upgrade.ts"],"names":[],"mappings":"AA0BA,UAAU,cAAc;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAwHD;;GAEG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA2J3E"}
@@ -3,6 +3,7 @@ import { execSync } from 'child_process';
3
3
  import path from 'path';
4
4
  import fs from 'fs-extra';
5
5
  import { createHeader, createBox, createSuccessBox, createErrorBox, NitroSpinner, log, spacer, nextSteps, NITRO_BANNER_FULL, showFooter } from '../ui/branding.js';
6
+ import { trackEvent, shutdownAnalytics } from '../analytics/posthog.js';
6
7
  /**
7
8
  * Get the latest version of a package from npm registry
8
9
  */
@@ -104,6 +105,10 @@ function runNpmInstall(directory) {
104
105
  export async function upgradeCommand(options) {
105
106
  console.log(NITRO_BANNER_FULL);
106
107
  console.log(createHeader('Upgrade', 'Update @nitrostack packages to latest'));
108
+ trackEvent('cli_command_invoked', {
109
+ command: 'upgrade',
110
+ options: Object.keys(options).filter(k => options[k] !== undefined),
111
+ });
107
112
  const projectRoot = process.cwd();
108
113
  const rootPackageJsonPath = path.join(projectRoot, 'package.json');
109
114
  const widgetsPath = path.join(projectRoot, 'src', 'widgets');
@@ -137,6 +142,14 @@ export async function upgradeCommand(options) {
137
142
  `Current version: ${currentParsedVersion}`,
138
143
  `Latest version: ${latestVersion}`,
139
144
  ]));
145
+ trackEvent('cli_upgrade_completed', {
146
+ packages_upgraded: 0,
147
+ from_version: currentParsedVersion,
148
+ to_version: latestVersion,
149
+ dry_run: !!options.dryRun,
150
+ already_current: true,
151
+ });
152
+ await shutdownAnalytics();
140
153
  return;
141
154
  }
142
155
  const allResults = [];
@@ -225,5 +238,13 @@ export async function upgradeCommand(options) {
225
238
  ]);
226
239
  }
227
240
  showFooter();
241
+ trackEvent('cli_upgrade_completed', {
242
+ packages_upgraded: allResults.length,
243
+ from_version: currentParsedVersion,
244
+ to_version: latestVersion,
245
+ dry_run: dryRun,
246
+ already_current: false,
247
+ });
248
+ await shutdownAnalytics();
228
249
  }
229
250
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nitrostack/cli",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "CLI for NitroStack - Create and manage MCP server projects",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -47,7 +47,8 @@
47
47
  "fs-extra": "^11.3.2",
48
48
  "inquirer": "^9.3.7",
49
49
  "open": "^10.1.0",
50
- "ora": "^8.1.1"
50
+ "ora": "^8.1.1",
51
+ "posthog-node": "^5.21.2"
51
52
  },
52
53
  "devDependencies": {
53
54
  "@types/fs-extra": "^11.0.4",
@@ -67,4 +68,4 @@
67
68
  "url": "https://github.com/nitrocloudofficial/nitrostack/issues"
68
69
  },
69
70
  "homepage": "https://nitrostack.ai"
70
- }
71
+ }