@egintegrations/telemetry 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +123 -10
  2. package/dist/cli.js +503 -0
  3. package/package.json +14 -4
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # @egi/telemetry
1
+ # @egintegrations/telemetry
2
2
 
3
3
  Official JavaScript/TypeScript telemetry SDK for EGIntegrations client engines.
4
4
 
@@ -6,19 +6,132 @@ Official JavaScript/TypeScript telemetry SDK for EGIntegrations client engines.
6
6
 
7
7
  ```bash
8
8
  # npm
9
- npm install @egi/telemetry
9
+ npm install @egintegrations/telemetry
10
10
 
11
11
  # pnpm
12
- pnpm add @egi/telemetry
12
+ pnpm add @egintegrations/telemetry
13
13
 
14
14
  # yarn
15
- yarn add @egi/telemetry
15
+ yarn add @egintegrations/telemetry
16
16
  ```
17
17
 
18
- ## Quick Start
18
+ ## Quick Setup with CLI
19
+
20
+ The fastest way to integrate telemetry into your project:
21
+
22
+ ### Interactive Setup
23
+
24
+ ```bash
25
+ npx @egintegrations/telemetry init
26
+ ```
27
+
28
+ The CLI will:
29
+ 1. Detect your framework (Next.js, Express, Fastify, or generic)
30
+ 2. Prompt for engine name, SKU version, and Control Center URL
31
+ 3. Generate configuration file (`.egi/telemetry.json`)
32
+ 4. Create integration code (`lib/telemetry.ts` for Next.js, `src/telemetry.ts` for Express)
33
+ 5. Add environment variables (`.env.local` or `.env`)
34
+ 6. Optionally generate status endpoint boilerplate
35
+
36
+ ### Non-Interactive Setup
37
+
38
+ For CI/CD or automated setups:
39
+
40
+ ```bash
41
+ npx @egintegrations/telemetry init \
42
+ --engine my-engine \
43
+ --sku 1.0.0 \
44
+ --url https://control-center.egintegrations.com \
45
+ --token your-auth-token
46
+ ```
47
+
48
+ ### CLI Options
49
+
50
+ | Option | Description | Default |
51
+ |--------|-------------|---------|
52
+ | `--engine <name>` | Engine name (required) | - |
53
+ | `--sku <version>` | SKU version (required) | - |
54
+ | `--url <url>` | Control Center URL (required) | - |
55
+ | `--token <token>` | Auth token (optional) | - |
56
+ | `--modules <modules>` | Comma-separated module list | - |
57
+ | `--output <path>` | Config file location | `.egi/telemetry.json` |
58
+ | `--framework <type>` | Force framework type (nextjs, express, fastify, generic) | Auto-detected |
59
+ | `--no-interactive` | Skip prompts, fail on missing args | Interactive mode |
60
+ | `--no-codegen` | Only generate config file | Code generation enabled |
61
+ | `--status-endpoint` | Generate status endpoint boilerplate | false |
62
+
63
+ ### Framework-Specific Examples
64
+
65
+ #### Next.js Project
66
+
67
+ ```bash
68
+ cd my-nextjs-app
69
+ npx @egintegrations/telemetry init
70
+ ```
71
+
72
+ **Generated Files:**
73
+ - `.egi/telemetry.json` - Configuration
74
+ - `lib/telemetry.ts` - Telemetry client singleton
75
+ - `.env.local` - Environment variables
76
+ - `app/api/engine-status/route.ts` - Status endpoint (if requested)
77
+
78
+ **Usage in Next.js:**
79
+ ```typescript
80
+ // app/api/data/route.ts
81
+ import telemetry from '@/lib/telemetry';
82
+
83
+ export async function GET() {
84
+ await telemetry.sendMetrics({
85
+ metrics: { api_calls: 1 }
86
+ });
87
+
88
+ return Response.json({ ok: true });
89
+ }
90
+ ```
91
+
92
+ #### Express Project
93
+
94
+ ```bash
95
+ cd my-express-app
96
+ npx @egintegrations/telemetry init
97
+ ```
98
+
99
+ **Generated Files:**
100
+ - `.egi/telemetry.json` - Configuration
101
+ - `src/telemetry.ts` - Telemetry client
102
+ - `.env` - Environment variables
103
+ - `src/routes/status.ts` - Status endpoint (if requested)
104
+
105
+ **Usage in Express:**
106
+ ```typescript
107
+ // src/index.ts
108
+ import express from 'express';
109
+ import telemetry from './telemetry';
110
+
111
+ const app = express();
112
+
113
+ // Auto-track requests
114
+ app.use(telemetry.middleware());
115
+
116
+ // Use in routes
117
+ app.get('/data', async (req, res) => {
118
+ await telemetry.sendMetrics({ metrics: { requests: 1 } });
119
+ res.json({ ok: true });
120
+ });
121
+
122
+ app.listen(3000, async () => {
123
+ await telemetry.register();
124
+ });
125
+ ```
126
+
127
+ ## Manual Setup (Advanced)
128
+
129
+ If you prefer manual setup instead of using the CLI:
130
+
131
+ ### Quick Start
19
132
 
20
133
  ```typescript
21
- import { TelemetryClient } from '@egi/telemetry';
134
+ import { TelemetryClient } from '@egintegrations/telemetry';
22
135
 
23
136
  // Initialize the client
24
137
  const telemetry = new TelemetryClient({
@@ -57,11 +170,11 @@ await telemetry.sendMetrics({
57
170
  });
58
171
  ```
59
172
 
60
- ## Express/Fastify Integration
173
+ ### Express/Fastify Integration
61
174
 
62
175
  ```typescript
63
176
  import express from 'express';
64
- import { TelemetryClient } from '@egi/telemetry';
177
+ import { TelemetryClient } from '@egintegrations/telemetry';
65
178
 
66
179
  const app = express();
67
180
  const telemetry = new TelemetryClient({
@@ -76,7 +189,7 @@ app.use(telemetry.middleware());
76
189
  app.listen(3000);
77
190
  ```
78
191
 
79
- ## Automatic Health Checks
192
+ ### Automatic Health Checks
80
193
 
81
194
  ```typescript
82
195
  // Report health every 60 seconds
@@ -86,7 +199,7 @@ const healthCheckTimer = telemetry.startHealthCheck(60000);
86
199
  clearInterval(healthCheckTimer);
87
200
  ```
88
201
 
89
- ## Graceful Shutdown
202
+ ### Graceful Shutdown
90
203
 
91
204
  ```typescript
92
205
  process.on('SIGTERM', async () => {
package/dist/cli.js ADDED
@@ -0,0 +1,503 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/cli.ts
27
+ var import_commander = require("commander");
28
+
29
+ // src/cli/detector.ts
30
+ var fs = __toESM(require("fs"));
31
+ var path = __toESM(require("path"));
32
+ async function detectFramework(cwd) {
33
+ const pkgJsonPath = path.join(cwd, "package.json");
34
+ if (!fs.existsSync(pkgJsonPath)) {
35
+ return { type: "generic", rootDir: cwd };
36
+ }
37
+ try {
38
+ const pkgContent = fs.readFileSync(pkgJsonPath, "utf-8");
39
+ const pkg = JSON.parse(pkgContent);
40
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
41
+ if (deps.next) {
42
+ const hasAppDir = fs.existsSync(path.join(cwd, "app"));
43
+ return {
44
+ type: "nextjs",
45
+ version: deps.next,
46
+ appDir: hasAppDir,
47
+ rootDir: cwd
48
+ };
49
+ }
50
+ if (deps.express) {
51
+ return {
52
+ type: "express",
53
+ version: deps.express,
54
+ rootDir: cwd
55
+ };
56
+ }
57
+ if (deps.fastify) {
58
+ return {
59
+ type: "fastify",
60
+ version: deps.fastify,
61
+ rootDir: cwd
62
+ };
63
+ }
64
+ return { type: "generic", rootDir: cwd };
65
+ } catch (error) {
66
+ console.warn("Failed to parse package.json, assuming generic project");
67
+ return { type: "generic", rootDir: cwd };
68
+ }
69
+ }
70
+
71
+ // src/cli/prompts.ts
72
+ var import_prompts2 = __toESM(require("prompts"));
73
+
74
+ // src/cli/utils.ts
75
+ var fs2 = __toESM(require("fs"));
76
+ var path2 = __toESM(require("path"));
77
+ var import_prompts = __toESM(require("prompts"));
78
+ async function writeFile(file) {
79
+ const dir = path2.dirname(file.path);
80
+ await fs2.promises.mkdir(dir, { recursive: true });
81
+ if (fs2.existsSync(file.path) && file.overwrite !== true) {
82
+ const { confirm } = await (0, import_prompts.default)({
83
+ type: "confirm",
84
+ name: "confirm",
85
+ message: `File ${file.path} already exists. Overwrite?`,
86
+ initial: false
87
+ });
88
+ if (!confirm) {
89
+ console.log(`\u23ED\uFE0F Skipped: ${file.path}`);
90
+ return;
91
+ }
92
+ }
93
+ if (file.path.endsWith(".env") || file.path.endsWith(".env.local")) {
94
+ await appendEnvFile(file.path, file.content);
95
+ } else {
96
+ await fs2.promises.writeFile(file.path, file.content, "utf-8");
97
+ console.log(`\u2713 Created: ${file.path}`);
98
+ }
99
+ }
100
+ async function appendEnvFile(envPath, content) {
101
+ let existing = "";
102
+ if (fs2.existsSync(envPath)) {
103
+ existing = await fs2.promises.readFile(envPath, "utf-8");
104
+ }
105
+ if (existing.includes("EGI_ENGINE_NAME")) {
106
+ console.log(`\u26A0\uFE0F ${envPath} already contains EGI variables, skipping`);
107
+ return;
108
+ }
109
+ const separator = existing.trim() ? "\n\n" : "";
110
+ const updated = existing + separator + content;
111
+ await fs2.promises.writeFile(envPath, updated, "utf-8");
112
+ console.log(`\u2713 Updated: ${envPath}`);
113
+ }
114
+ function validateSku(sku) {
115
+ return /^\d+\.\d+\.\d+/.test(sku);
116
+ }
117
+
118
+ // src/cli/prompts.ts
119
+ async function gatherOptions(cliArgs, detected) {
120
+ const questions = [];
121
+ if (!cliArgs.engine) {
122
+ questions.push({
123
+ type: "text",
124
+ name: "engine",
125
+ message: "Engine name (e.g., hs-and-c):",
126
+ validate: (v) => v.length > 0 || "Engine name is required"
127
+ });
128
+ }
129
+ if (!cliArgs.sku) {
130
+ questions.push({
131
+ type: "text",
132
+ name: "sku",
133
+ message: "SKU version (e.g., 1.0.0):",
134
+ validate: (v) => validateSku(v) || "Invalid SKU format (expected: X.Y.Z...)"
135
+ });
136
+ }
137
+ if (!cliArgs.url) {
138
+ questions.push({
139
+ type: "text",
140
+ name: "url",
141
+ message: "Control Center URL:",
142
+ initial: "https://control-center.egintegrations.com"
143
+ });
144
+ }
145
+ if (cliArgs.token === void 0) {
146
+ questions.push({
147
+ type: "password",
148
+ name: "token",
149
+ message: "Auth token (optional, press Enter to skip):"
150
+ });
151
+ }
152
+ if (!cliArgs.framework) {
153
+ const frameworkChoices = [
154
+ { title: `${detected.type} (detected)`, value: detected.type },
155
+ { title: "Next.js", value: "nextjs", disabled: detected.type === "nextjs" },
156
+ { title: "Express", value: "express", disabled: detected.type === "express" },
157
+ { title: "Fastify", value: "fastify", disabled: detected.type === "fastify" },
158
+ { title: "Generic/Other", value: "generic", disabled: detected.type === "generic" }
159
+ ].filter((choice) => !choice.disabled);
160
+ if (frameworkChoices.length > 1) {
161
+ questions.push({
162
+ type: "select",
163
+ name: "framework",
164
+ message: "Select framework:",
165
+ choices: frameworkChoices,
166
+ initial: 0
167
+ });
168
+ }
169
+ }
170
+ if (cliArgs.codegen !== false && detected.type !== "generic") {
171
+ questions.push({
172
+ type: "confirm",
173
+ name: "statusEndpoint",
174
+ message: "Generate status endpoint boilerplate?",
175
+ initial: true
176
+ });
177
+ }
178
+ const answers = await (0, import_prompts2.default)(questions, {
179
+ onCancel: () => {
180
+ console.log("\n\u274C Setup cancelled");
181
+ process.exit(0);
182
+ }
183
+ });
184
+ return {
185
+ engine: cliArgs.engine || answers.engine,
186
+ sku: cliArgs.sku || answers.sku,
187
+ url: cliArgs.url || answers.url,
188
+ token: cliArgs.token || answers.token || void 0,
189
+ modules: cliArgs.modules,
190
+ output: cliArgs.output || ".egi/telemetry.json",
191
+ framework: cliArgs.framework || answers.framework || detected.type,
192
+ codegen: cliArgs.codegen !== false,
193
+ statusEndpoint: cliArgs.statusEndpoint || answers.statusEndpoint || false
194
+ };
195
+ }
196
+
197
+ // src/cli/generators/nextjs.ts
198
+ var path3 = __toESM(require("path"));
199
+
200
+ // src/cli/generators/templates.ts
201
+ function generateConfigFile(ctx) {
202
+ return JSON.stringify({
203
+ engine: ctx.engine,
204
+ sku: ctx.sku,
205
+ url: ctx.url,
206
+ ...ctx.token && { token: ctx.token },
207
+ ...ctx.modules && { modules: ctx.modules }
208
+ }, null, 2);
209
+ }
210
+ function generateNextjsTelemetryClient(ctx) {
211
+ return `import { TelemetryClient } from '@egintegrations/telemetry';
212
+
213
+ const telemetry = new TelemetryClient({
214
+ engineName: process.env.EGI_ENGINE_NAME || '${ctx.engine}',
215
+ skuVersion: process.env.EGI_SKU_VERSION || '${ctx.sku}',
216
+ controlCenterUrl: process.env.EGI_CONTROL_CENTER_URL || '${ctx.url}',
217
+ ${ctx.token ? `authToken: process.env.EGI_AUTH_TOKEN,` : "// authToken: process.env.EGI_AUTH_TOKEN, // Optional"}
218
+ enabled: process.env.NODE_ENV === 'production'
219
+ });
220
+
221
+ // Auto-register on first import in production
222
+ if (process.env.NODE_ENV === 'production') {
223
+ telemetry.register().catch(err => {
224
+ console.error('[EGI Telemetry] Failed to register:', err);
225
+ });
226
+ }
227
+
228
+ export default telemetry;
229
+ `;
230
+ }
231
+ function generateNextjsStatusEndpoint() {
232
+ return `import { NextResponse } from 'next/server';
233
+ import telemetry from '@/lib/telemetry';
234
+
235
+ export async function GET() {
236
+ try {
237
+ await telemetry.reportStatus({
238
+ status: 'healthy',
239
+ metadata: {
240
+ timestamp: new Date().toISOString(),
241
+ uptime: process.uptime()
242
+ },
243
+ metrics: {},
244
+ message: 'Engine is healthy'
245
+ });
246
+
247
+ return NextResponse.json({
248
+ status: 'ok',
249
+ timestamp: new Date().toISOString()
250
+ });
251
+ } catch (error) {
252
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
253
+ return NextResponse.json(
254
+ { status: 'error', message: errorMessage },
255
+ { status: 500 }
256
+ );
257
+ }
258
+ }
259
+ `;
260
+ }
261
+ function generateExpressTelemetryClient(ctx) {
262
+ return `import { TelemetryClient } from '@egintegrations/telemetry';
263
+
264
+ const telemetry = new TelemetryClient({
265
+ engineName: process.env.EGI_ENGINE_NAME || '${ctx.engine}',
266
+ skuVersion: process.env.EGI_SKU_VERSION || '${ctx.sku}',
267
+ controlCenterUrl: process.env.EGI_CONTROL_CENTER_URL || '${ctx.url}',
268
+ ${ctx.token ? `authToken: process.env.EGI_AUTH_TOKEN,` : "// authToken: process.env.EGI_AUTH_TOKEN, // Optional"}
269
+ enabled: process.env.NODE_ENV === 'production'
270
+ });
271
+
272
+ export default telemetry;
273
+ `;
274
+ }
275
+ function generateExpressStatusRoute() {
276
+ return `import express from 'express';
277
+ import telemetry from '../telemetry';
278
+
279
+ const router = express.Router();
280
+
281
+ router.get('/engine-status', async (req, res) => {
282
+ try {
283
+ await telemetry.reportStatus({
284
+ status: 'healthy',
285
+ metadata: {
286
+ uptime: process.uptime(),
287
+ memory: process.memoryUsage()
288
+ },
289
+ metrics: {},
290
+ message: 'Engine is healthy'
291
+ });
292
+
293
+ res.json({
294
+ status: 'ok',
295
+ timestamp: new Date().toISOString()
296
+ });
297
+ } catch (error) {
298
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
299
+ res.status(500).json({
300
+ status: 'error',
301
+ message: errorMessage
302
+ });
303
+ }
304
+ });
305
+
306
+ export default router;
307
+ `;
308
+ }
309
+ function generateEnvVars(ctx) {
310
+ const lines = [
311
+ "# EGI Telemetry Configuration",
312
+ `EGI_ENGINE_NAME=${ctx.engine}`,
313
+ `EGI_SKU_VERSION=${ctx.sku}`,
314
+ `EGI_CONTROL_CENTER_URL=${ctx.url}`
315
+ ];
316
+ if (ctx.token) {
317
+ lines.push(`EGI_AUTH_TOKEN=${ctx.token}`);
318
+ }
319
+ return lines.join("\n");
320
+ }
321
+
322
+ // src/cli/generators/nextjs.ts
323
+ var nextjsGenerator = {
324
+ name: "Next.js",
325
+ supports: (ctx) => ctx.framework.type === "nextjs",
326
+ async generate(ctx) {
327
+ const files = [];
328
+ files.push({
329
+ path: path3.join(ctx.rootDir, ctx.options.output),
330
+ content: generateConfigFile(ctx.options)
331
+ });
332
+ if (ctx.options.codegen) {
333
+ const libDir = path3.join(ctx.rootDir, "lib");
334
+ files.push({
335
+ path: path3.join(libDir, "telemetry.ts"),
336
+ content: generateNextjsTelemetryClient(ctx.options),
337
+ overwrite: false
338
+ // Ask if exists
339
+ });
340
+ if (ctx.framework.appDir && ctx.options.statusEndpoint) {
341
+ files.push({
342
+ path: path3.join(ctx.rootDir, "app/api/engine-status/route.ts"),
343
+ content: generateNextjsStatusEndpoint(),
344
+ overwrite: false
345
+ });
346
+ }
347
+ files.push({
348
+ path: path3.join(ctx.rootDir, ".env.local"),
349
+ content: generateEnvVars(ctx.options),
350
+ overwrite: false
351
+ // Append, don't replace
352
+ });
353
+ }
354
+ return files;
355
+ }
356
+ };
357
+
358
+ // src/cli/generators/express.ts
359
+ var path4 = __toESM(require("path"));
360
+ var fs3 = __toESM(require("fs"));
361
+ var expressGenerator = {
362
+ name: "Express",
363
+ supports: (ctx) => ctx.framework.type === "express" || ctx.framework.type === "fastify",
364
+ async generate(ctx) {
365
+ const files = [];
366
+ files.push({
367
+ path: path4.join(ctx.rootDir, ctx.options.output),
368
+ content: generateConfigFile(ctx.options)
369
+ });
370
+ if (ctx.options.codegen) {
371
+ const hasSrcDir = fs3.existsSync(path4.join(ctx.rootDir, "src"));
372
+ const baseDir = hasSrcDir ? "src" : ".";
373
+ files.push({
374
+ path: path4.join(ctx.rootDir, baseDir, "telemetry.ts"),
375
+ content: generateExpressTelemetryClient(ctx.options),
376
+ overwrite: false
377
+ });
378
+ if (ctx.options.statusEndpoint) {
379
+ const routesDir = path4.join(ctx.rootDir, baseDir, "routes");
380
+ files.push({
381
+ path: path4.join(routesDir, "status.ts"),
382
+ content: generateExpressStatusRoute(),
383
+ overwrite: false
384
+ });
385
+ }
386
+ files.push({
387
+ path: path4.join(ctx.rootDir, ".env"),
388
+ content: generateEnvVars(ctx.options),
389
+ overwrite: false
390
+ });
391
+ }
392
+ return files;
393
+ }
394
+ };
395
+
396
+ // src/cli/generators/generic.ts
397
+ var path5 = __toESM(require("path"));
398
+ var genericGenerator = {
399
+ name: "Generic Node.js",
400
+ supports: () => true,
401
+ // Fallback generator, always supports
402
+ async generate(ctx) {
403
+ const files = [];
404
+ files.push({
405
+ path: path5.join(ctx.rootDir, ctx.options.output),
406
+ content: generateConfigFile(ctx.options)
407
+ });
408
+ console.log(`
409
+ \u26A0\uFE0F Generic project detected - only config file generated.
410
+
411
+ To use the telemetry SDK, manually:
412
+ 1. Import the SDK: import { TelemetryClient } from '@egintegrations/telemetry';
413
+ 2. Create a client with your config
414
+ 3. Call telemetry.register() on startup
415
+
416
+ Example:
417
+ import { TelemetryClient } from '@egintegrations/telemetry';
418
+
419
+ const telemetry = new TelemetryClient({
420
+ engineName: '${ctx.options.engine}',
421
+ skuVersion: '${ctx.options.sku}',
422
+ controlCenterUrl: '${ctx.options.url}',
423
+ ${ctx.options.token ? `authToken: '${ctx.options.token}',` : ""}
424
+ enabled: true
425
+ });
426
+
427
+ await telemetry.register();
428
+
429
+ See README for full documentation.
430
+ `);
431
+ return files;
432
+ }
433
+ };
434
+
435
+ // src/cli/generators/index.ts
436
+ var generators = [
437
+ nextjsGenerator,
438
+ expressGenerator,
439
+ genericGenerator
440
+ // Always last as fallback
441
+ ];
442
+ async function runGenerator(ctx) {
443
+ const generator = generators.find((g) => g.supports(ctx));
444
+ if (!generator) {
445
+ throw new Error(`No generator found for framework: ${ctx.framework.type}`);
446
+ }
447
+ console.log(`
448
+ \u{1F3A8} Using ${generator.name} generator
449
+ `);
450
+ return generator.generate(ctx);
451
+ }
452
+
453
+ // src/cli/commands.ts
454
+ async function initCommand(options) {
455
+ console.log("\u{1F680} EGI Telemetry SDK Initialization\n");
456
+ try {
457
+ const cwd = process.cwd();
458
+ console.log("\u{1F50D} Detecting framework...");
459
+ const detected = await detectFramework(cwd);
460
+ console.log(` Found: ${detected.type}${detected.version ? ` v${detected.version}` : ""}`);
461
+ if (detected.type === "nextjs" && detected.appDir) {
462
+ console.log(" Next.js App Router detected");
463
+ }
464
+ console.log("");
465
+ const telemetryOptions = await gatherOptions(options, detected);
466
+ console.log("");
467
+ const ctx = {
468
+ options: telemetryOptions,
469
+ framework: detected,
470
+ rootDir: cwd
471
+ };
472
+ const files = await runGenerator(ctx);
473
+ for (const file of files) {
474
+ await writeFile(file);
475
+ }
476
+ console.log("\n\u2705 Telemetry SDK initialized successfully!\n");
477
+ if (telemetryOptions.codegen && detected.type !== "generic") {
478
+ console.log("\u{1F4DD} Next steps:");
479
+ console.log(" 1. Review the generated files");
480
+ console.log(" 2. Import telemetry in your code:");
481
+ if (detected.type === "nextjs") {
482
+ console.log(" import telemetry from '@/lib/telemetry';");
483
+ } else {
484
+ console.log(" import telemetry from './telemetry';");
485
+ }
486
+ console.log(" 3. Start using telemetry.sendMetrics() in your app");
487
+ console.log("");
488
+ }
489
+ } catch (error) {
490
+ if (error instanceof Error) {
491
+ console.error("\n\u274C Error:", error.message);
492
+ } else {
493
+ console.error("\n\u274C Unknown error occurred");
494
+ }
495
+ process.exit(1);
496
+ }
497
+ }
498
+
499
+ // src/cli.ts
500
+ var program = new import_commander.Command();
501
+ program.name("egi-telemetry").description("EGIntegrations Telemetry SDK CLI").version("0.2.0");
502
+ program.command("init").description("Initialize telemetry in your project").option("--engine <name>", "Engine name").option("--sku <version>", "SKU version").option("--url <url>", "Control Center URL").option("--token <token>", "Auth token (optional)").option("--modules <modules>", "Comma-separated module list").option("--output <path>", "Config output path", ".egi/telemetry.json").option("--framework <type>", "Framework (nextjs, express, fastify, generic)").option("--no-interactive", "Skip prompts, fail if args missing").option("--no-codegen", "Only generate config, skip integration code").option("--status-endpoint", "Generate status endpoint boilerplate").action(initCommand);
503
+ program.parse();
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@egintegrations/telemetry",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Telemetry SDK for EGIntegrations client engines",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
7
7
  "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "egi-telemetry": "./dist/cli.js"
10
+ },
8
11
  "exports": {
9
12
  ".": {
10
13
  "require": "./dist/index.js",
@@ -13,9 +16,12 @@
13
16
  }
14
17
  },
15
18
  "scripts": {
16
- "build": "tsup src/index.ts --format cjs,esm --dts --clean",
19
+ "build": "npm run build:lib && npm run build:cli",
20
+ "build:lib": "tsup src/index.ts --format cjs,esm --dts --clean",
21
+ "build:cli": "tsup src/cli.ts --format cjs --no-dts --shims",
17
22
  "prepublishOnly": "npm run build",
18
- "test": "echo \"No tests yet\""
23
+ "test": "echo \"No tests yet\"",
24
+ "dev:cli": "tsx src/cli.ts"
19
25
  },
20
26
  "keywords": [
21
27
  "telemetry",
@@ -26,11 +32,15 @@
26
32
  "author": "EGIntegrations",
27
33
  "license": "PROPRIETARY",
28
34
  "dependencies": {
29
- "axios": "^1.6.0"
35
+ "axios": "^1.6.0",
36
+ "commander": "^12.0.0",
37
+ "prompts": "^2.4.2"
30
38
  },
31
39
  "devDependencies": {
32
40
  "@types/node": "^20.0.0",
41
+ "@types/prompts": "^2.4.9",
33
42
  "tsup": "^8.0.0",
43
+ "tsx": "^4.0.0",
34
44
  "typescript": "^5.3.0"
35
45
  },
36
46
  "files": [