@egintegrations/telemetry 0.1.0 → 0.2.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.
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,544 @@
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 loadSkuStandards() {
115
+ const standardsDir = path2.join(__dirname, "standards");
116
+ const systemTypesPath = path2.join(standardsDir, "system_types.json");
117
+ const platformsPath = path2.join(standardsDir, "platforms.json");
118
+ const systemTypes = JSON.parse(
119
+ fs2.readFileSync(systemTypesPath, "utf-8")
120
+ );
121
+ const platforms = JSON.parse(
122
+ fs2.readFileSync(platformsPath, "utf-8")
123
+ );
124
+ return { systemTypes, platforms };
125
+ }
126
+ function buildSku(version, systemType, platform, date) {
127
+ const skuDate = date || (/* @__PURE__ */ new Date()).toISOString().slice(0, 10).replace(/-/g, "");
128
+ return `${version}.${systemType}.${platform}.${skuDate}`;
129
+ }
130
+
131
+ // src/cli/prompts.ts
132
+ async function gatherOptions(cliArgs, detected) {
133
+ const questions = [];
134
+ if (!cliArgs.engine) {
135
+ questions.push({
136
+ type: "text",
137
+ name: "engine",
138
+ message: "Engine name (e.g., hs-and-c):",
139
+ validate: (v) => v.length > 0 || "Engine name is required"
140
+ });
141
+ }
142
+ let skuComponents = {};
143
+ if (!cliArgs.sku) {
144
+ const standards = loadSkuStandards();
145
+ questions.push({
146
+ type: "text",
147
+ name: "skuVersion",
148
+ message: "Version (MAJOR.MINOR.PATCH, e.g., 1.0.0):",
149
+ validate: (v) => /^\d+\.\d+\.\d+$/.test(v) || "Invalid version format (expected: X.Y.Z)"
150
+ });
151
+ questions.push({
152
+ type: "select",
153
+ name: "skuType",
154
+ message: "System Type:",
155
+ choices: standards.systemTypes.map((t) => ({
156
+ title: `${t.code} - ${t.label}`,
157
+ description: t.description,
158
+ value: t.code
159
+ }))
160
+ });
161
+ questions.push({
162
+ type: "select",
163
+ name: "skuPlatform",
164
+ message: "Platform:",
165
+ choices: standards.platforms.map((p) => ({
166
+ title: `${p.code} - ${p.label}`,
167
+ description: p.description,
168
+ value: p.code
169
+ }))
170
+ });
171
+ }
172
+ if (!cliArgs.url) {
173
+ questions.push({
174
+ type: "text",
175
+ name: "url",
176
+ message: "Control Center URL:",
177
+ initial: "https://control-center.egintegrations.com"
178
+ });
179
+ }
180
+ if (cliArgs.token === void 0) {
181
+ questions.push({
182
+ type: "password",
183
+ name: "token",
184
+ message: "Auth token (optional, press Enter to skip):"
185
+ });
186
+ }
187
+ if (!cliArgs.framework) {
188
+ const frameworkChoices = [
189
+ { title: `${detected.type} (detected)`, value: detected.type },
190
+ { title: "Next.js", value: "nextjs", disabled: detected.type === "nextjs" },
191
+ { title: "Express", value: "express", disabled: detected.type === "express" },
192
+ { title: "Fastify", value: "fastify", disabled: detected.type === "fastify" },
193
+ { title: "Generic/Other", value: "generic", disabled: detected.type === "generic" }
194
+ ].filter((choice) => !choice.disabled);
195
+ if (frameworkChoices.length > 1) {
196
+ questions.push({
197
+ type: "select",
198
+ name: "framework",
199
+ message: "Select framework:",
200
+ choices: frameworkChoices,
201
+ initial: 0
202
+ });
203
+ }
204
+ }
205
+ if (cliArgs.codegen !== false && detected.type !== "generic") {
206
+ questions.push({
207
+ type: "confirm",
208
+ name: "statusEndpoint",
209
+ message: "Generate status endpoint boilerplate?",
210
+ initial: true
211
+ });
212
+ }
213
+ const answers = await (0, import_prompts2.default)(questions, {
214
+ onCancel: () => {
215
+ console.log("\n\u274C Setup cancelled");
216
+ process.exit(0);
217
+ }
218
+ });
219
+ let finalSku = cliArgs.sku;
220
+ if (!finalSku && answers.skuVersion && answers.skuType && answers.skuPlatform) {
221
+ finalSku = buildSku(answers.skuVersion, answers.skuType, answers.skuPlatform);
222
+ console.log(`
223
+ \u{1F4E6} Generated SKU: ${finalSku}`);
224
+ }
225
+ return {
226
+ engine: cliArgs.engine || answers.engine,
227
+ sku: finalSku,
228
+ url: cliArgs.url || answers.url,
229
+ token: cliArgs.token || answers.token || void 0,
230
+ modules: cliArgs.modules,
231
+ output: cliArgs.output || ".egi/telemetry.json",
232
+ framework: cliArgs.framework || answers.framework || detected.type,
233
+ codegen: cliArgs.codegen !== false,
234
+ statusEndpoint: cliArgs.statusEndpoint || answers.statusEndpoint || false
235
+ };
236
+ }
237
+
238
+ // src/cli/generators/nextjs.ts
239
+ var path3 = __toESM(require("path"));
240
+
241
+ // src/cli/generators/templates.ts
242
+ function generateConfigFile(ctx) {
243
+ return JSON.stringify({
244
+ engine: ctx.engine,
245
+ sku: ctx.sku,
246
+ url: ctx.url,
247
+ ...ctx.token && { token: ctx.token },
248
+ ...ctx.modules && { modules: ctx.modules }
249
+ }, null, 2);
250
+ }
251
+ function generateNextjsTelemetryClient(ctx) {
252
+ return `import { TelemetryClient } from '@egintegrations/telemetry';
253
+
254
+ const telemetry = new TelemetryClient({
255
+ engineName: process.env.EGI_ENGINE_NAME || '${ctx.engine}',
256
+ skuVersion: process.env.EGI_SKU_VERSION || '${ctx.sku}',
257
+ controlCenterUrl: process.env.EGI_CONTROL_CENTER_URL || '${ctx.url}',
258
+ ${ctx.token ? `authToken: process.env.EGI_AUTH_TOKEN,` : "// authToken: process.env.EGI_AUTH_TOKEN, // Optional"}
259
+ enabled: process.env.NODE_ENV === 'production'
260
+ });
261
+
262
+ // Auto-register on first import in production
263
+ if (process.env.NODE_ENV === 'production') {
264
+ telemetry.register().catch(err => {
265
+ console.error('[EGI Telemetry] Failed to register:', err);
266
+ });
267
+ }
268
+
269
+ export default telemetry;
270
+ `;
271
+ }
272
+ function generateNextjsStatusEndpoint() {
273
+ return `import { NextResponse } from 'next/server';
274
+ import telemetry from '@/lib/telemetry';
275
+
276
+ export async function GET() {
277
+ try {
278
+ await telemetry.reportStatus({
279
+ status: 'healthy',
280
+ metadata: {
281
+ timestamp: new Date().toISOString(),
282
+ uptime: process.uptime()
283
+ },
284
+ metrics: {},
285
+ message: 'Engine is healthy'
286
+ });
287
+
288
+ return NextResponse.json({
289
+ status: 'ok',
290
+ timestamp: new Date().toISOString()
291
+ });
292
+ } catch (error) {
293
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
294
+ return NextResponse.json(
295
+ { status: 'error', message: errorMessage },
296
+ { status: 500 }
297
+ );
298
+ }
299
+ }
300
+ `;
301
+ }
302
+ function generateExpressTelemetryClient(ctx) {
303
+ return `import { TelemetryClient } from '@egintegrations/telemetry';
304
+
305
+ const telemetry = new TelemetryClient({
306
+ engineName: process.env.EGI_ENGINE_NAME || '${ctx.engine}',
307
+ skuVersion: process.env.EGI_SKU_VERSION || '${ctx.sku}',
308
+ controlCenterUrl: process.env.EGI_CONTROL_CENTER_URL || '${ctx.url}',
309
+ ${ctx.token ? `authToken: process.env.EGI_AUTH_TOKEN,` : "// authToken: process.env.EGI_AUTH_TOKEN, // Optional"}
310
+ enabled: process.env.NODE_ENV === 'production'
311
+ });
312
+
313
+ export default telemetry;
314
+ `;
315
+ }
316
+ function generateExpressStatusRoute() {
317
+ return `import express from 'express';
318
+ import telemetry from '../telemetry';
319
+
320
+ const router = express.Router();
321
+
322
+ router.get('/engine-status', async (req, res) => {
323
+ try {
324
+ await telemetry.reportStatus({
325
+ status: 'healthy',
326
+ metadata: {
327
+ uptime: process.uptime(),
328
+ memory: process.memoryUsage()
329
+ },
330
+ metrics: {},
331
+ message: 'Engine is healthy'
332
+ });
333
+
334
+ res.json({
335
+ status: 'ok',
336
+ timestamp: new Date().toISOString()
337
+ });
338
+ } catch (error) {
339
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
340
+ res.status(500).json({
341
+ status: 'error',
342
+ message: errorMessage
343
+ });
344
+ }
345
+ });
346
+
347
+ export default router;
348
+ `;
349
+ }
350
+ function generateEnvVars(ctx) {
351
+ const lines = [
352
+ "# EGI Telemetry Configuration",
353
+ `EGI_ENGINE_NAME=${ctx.engine}`,
354
+ `EGI_SKU_VERSION=${ctx.sku}`,
355
+ `EGI_CONTROL_CENTER_URL=${ctx.url}`
356
+ ];
357
+ if (ctx.token) {
358
+ lines.push(`EGI_AUTH_TOKEN=${ctx.token}`);
359
+ }
360
+ return lines.join("\n");
361
+ }
362
+
363
+ // src/cli/generators/nextjs.ts
364
+ var nextjsGenerator = {
365
+ name: "Next.js",
366
+ supports: (ctx) => ctx.framework.type === "nextjs",
367
+ async generate(ctx) {
368
+ const files = [];
369
+ files.push({
370
+ path: path3.join(ctx.rootDir, ctx.options.output),
371
+ content: generateConfigFile(ctx.options)
372
+ });
373
+ if (ctx.options.codegen) {
374
+ const libDir = path3.join(ctx.rootDir, "lib");
375
+ files.push({
376
+ path: path3.join(libDir, "telemetry.ts"),
377
+ content: generateNextjsTelemetryClient(ctx.options),
378
+ overwrite: false
379
+ // Ask if exists
380
+ });
381
+ if (ctx.framework.appDir && ctx.options.statusEndpoint) {
382
+ files.push({
383
+ path: path3.join(ctx.rootDir, "app/api/engine-status/route.ts"),
384
+ content: generateNextjsStatusEndpoint(),
385
+ overwrite: false
386
+ });
387
+ }
388
+ files.push({
389
+ path: path3.join(ctx.rootDir, ".env.local"),
390
+ content: generateEnvVars(ctx.options),
391
+ overwrite: false
392
+ // Append, don't replace
393
+ });
394
+ }
395
+ return files;
396
+ }
397
+ };
398
+
399
+ // src/cli/generators/express.ts
400
+ var path4 = __toESM(require("path"));
401
+ var fs3 = __toESM(require("fs"));
402
+ var expressGenerator = {
403
+ name: "Express",
404
+ supports: (ctx) => ctx.framework.type === "express" || ctx.framework.type === "fastify",
405
+ async generate(ctx) {
406
+ const files = [];
407
+ files.push({
408
+ path: path4.join(ctx.rootDir, ctx.options.output),
409
+ content: generateConfigFile(ctx.options)
410
+ });
411
+ if (ctx.options.codegen) {
412
+ const hasSrcDir = fs3.existsSync(path4.join(ctx.rootDir, "src"));
413
+ const baseDir = hasSrcDir ? "src" : ".";
414
+ files.push({
415
+ path: path4.join(ctx.rootDir, baseDir, "telemetry.ts"),
416
+ content: generateExpressTelemetryClient(ctx.options),
417
+ overwrite: false
418
+ });
419
+ if (ctx.options.statusEndpoint) {
420
+ const routesDir = path4.join(ctx.rootDir, baseDir, "routes");
421
+ files.push({
422
+ path: path4.join(routesDir, "status.ts"),
423
+ content: generateExpressStatusRoute(),
424
+ overwrite: false
425
+ });
426
+ }
427
+ files.push({
428
+ path: path4.join(ctx.rootDir, ".env"),
429
+ content: generateEnvVars(ctx.options),
430
+ overwrite: false
431
+ });
432
+ }
433
+ return files;
434
+ }
435
+ };
436
+
437
+ // src/cli/generators/generic.ts
438
+ var path5 = __toESM(require("path"));
439
+ var genericGenerator = {
440
+ name: "Generic Node.js",
441
+ supports: () => true,
442
+ // Fallback generator, always supports
443
+ async generate(ctx) {
444
+ const files = [];
445
+ files.push({
446
+ path: path5.join(ctx.rootDir, ctx.options.output),
447
+ content: generateConfigFile(ctx.options)
448
+ });
449
+ console.log(`
450
+ \u26A0\uFE0F Generic project detected - only config file generated.
451
+
452
+ To use the telemetry SDK, manually:
453
+ 1. Import the SDK: import { TelemetryClient } from '@egintegrations/telemetry';
454
+ 2. Create a client with your config
455
+ 3. Call telemetry.register() on startup
456
+
457
+ Example:
458
+ import { TelemetryClient } from '@egintegrations/telemetry';
459
+
460
+ const telemetry = new TelemetryClient({
461
+ engineName: '${ctx.options.engine}',
462
+ skuVersion: '${ctx.options.sku}',
463
+ controlCenterUrl: '${ctx.options.url}',
464
+ ${ctx.options.token ? `authToken: '${ctx.options.token}',` : ""}
465
+ enabled: true
466
+ });
467
+
468
+ await telemetry.register();
469
+
470
+ See README for full documentation.
471
+ `);
472
+ return files;
473
+ }
474
+ };
475
+
476
+ // src/cli/generators/index.ts
477
+ var generators = [
478
+ nextjsGenerator,
479
+ expressGenerator,
480
+ genericGenerator
481
+ // Always last as fallback
482
+ ];
483
+ async function runGenerator(ctx) {
484
+ const generator = generators.find((g) => g.supports(ctx));
485
+ if (!generator) {
486
+ throw new Error(`No generator found for framework: ${ctx.framework.type}`);
487
+ }
488
+ console.log(`
489
+ \u{1F3A8} Using ${generator.name} generator
490
+ `);
491
+ return generator.generate(ctx);
492
+ }
493
+
494
+ // src/cli/commands.ts
495
+ async function initCommand(options) {
496
+ console.log("\u{1F680} EGI Telemetry SDK Initialization\n");
497
+ try {
498
+ const cwd = process.cwd();
499
+ console.log("\u{1F50D} Detecting framework...");
500
+ const detected = await detectFramework(cwd);
501
+ console.log(` Found: ${detected.type}${detected.version ? ` v${detected.version}` : ""}`);
502
+ if (detected.type === "nextjs" && detected.appDir) {
503
+ console.log(" Next.js App Router detected");
504
+ }
505
+ console.log("");
506
+ const telemetryOptions = await gatherOptions(options, detected);
507
+ console.log("");
508
+ const ctx = {
509
+ options: telemetryOptions,
510
+ framework: detected,
511
+ rootDir: cwd
512
+ };
513
+ const files = await runGenerator(ctx);
514
+ for (const file of files) {
515
+ await writeFile(file);
516
+ }
517
+ console.log("\n\u2705 Telemetry SDK initialized successfully!\n");
518
+ if (telemetryOptions.codegen && detected.type !== "generic") {
519
+ console.log("\u{1F4DD} Next steps:");
520
+ console.log(" 1. Review the generated files");
521
+ console.log(" 2. Import telemetry in your code:");
522
+ if (detected.type === "nextjs") {
523
+ console.log(" import telemetry from '@/lib/telemetry';");
524
+ } else {
525
+ console.log(" import telemetry from './telemetry';");
526
+ }
527
+ console.log(" 3. Start using telemetry.sendMetrics() in your app");
528
+ console.log("");
529
+ }
530
+ } catch (error) {
531
+ if (error instanceof Error) {
532
+ console.error("\n\u274C Error:", error.message);
533
+ } else {
534
+ console.error("\n\u274C Unknown error occurred");
535
+ }
536
+ process.exit(1);
537
+ }
538
+ }
539
+
540
+ // src/cli.ts
541
+ var program = new import_commander.Command();
542
+ program.name("egi-telemetry").description("EGIntegrations Telemetry SDK CLI").version("0.2.0");
543
+ 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);
544
+ 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.1",
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,15 +32,20 @@
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": [
37
47
  "dist",
38
- "README.md"
48
+ "README.md",
49
+ "src/cli/standards"
39
50
  ]
40
51
  }
@@ -0,0 +1,202 @@
1
+ [
2
+ {
3
+ "code": "EKS",
4
+ "label": "AWS EKS (Kubernetes)",
5
+ "description": "Amazon Elastic Kubernetes Service",
6
+ "provider": "AWS",
7
+ "type": "managed-kubernetes",
8
+ "notes": "Preferred for production containerized workloads on AWS"
9
+ },
10
+ {
11
+ "code": "GKE",
12
+ "label": "Google Kubernetes Engine",
13
+ "description": "Google's managed Kubernetes service",
14
+ "provider": "GCP",
15
+ "type": "managed-kubernetes",
16
+ "notes": "Preferred for production containerized workloads on GCP"
17
+ },
18
+ {
19
+ "code": "AKS",
20
+ "label": "Azure Kubernetes Service",
21
+ "description": "Microsoft Azure managed Kubernetes",
22
+ "provider": "Azure",
23
+ "type": "managed-kubernetes",
24
+ "notes": "Preferred for production containerized workloads on Azure"
25
+ },
26
+ {
27
+ "code": "K8S",
28
+ "label": "Kubernetes (generic)",
29
+ "description": "Self-managed or other Kubernetes distributions",
30
+ "provider": "Various",
31
+ "type": "kubernetes",
32
+ "notes": "Use for DigitalOcean, Linode, self-hosted K8s"
33
+ },
34
+ {
35
+ "code": "DK",
36
+ "label": "Docker (generic container build/runtime)",
37
+ "description": "Docker containers, docker-compose",
38
+ "provider": "Various",
39
+ "type": "container",
40
+ "notes": "For Docker-based deployments without orchestration"
41
+ },
42
+ {
43
+ "code": "DKO",
44
+ "label": "Docker Offline (air-gapped/off-net)",
45
+ "description": "Docker in air-gapped or offline environments",
46
+ "provider": "Various",
47
+ "type": "container",
48
+ "notes": "For secure/isolated environments"
49
+ },
50
+ {
51
+ "code": "LM",
52
+ "label": "AWS Lambda",
53
+ "description": "AWS serverless functions",
54
+ "provider": "AWS",
55
+ "type": "serverless",
56
+ "notes": "For event-driven, serverless workloads"
57
+ },
58
+ {
59
+ "code": "CF",
60
+ "label": "Cloudflare (Workers/Pages)",
61
+ "description": "Cloudflare Workers and Pages",
62
+ "provider": "Cloudflare",
63
+ "type": "edge-compute",
64
+ "notes": "Edge compute and static hosting"
65
+ },
66
+ {
67
+ "code": "VE",
68
+ "label": "Vercel",
69
+ "description": "Vercel serverless platform",
70
+ "provider": "Vercel",
71
+ "type": "serverless",
72
+ "notes": "Optimized for Next.js and frontend frameworks"
73
+ },
74
+ {
75
+ "code": "FH",
76
+ "label": "Firebase Hosting",
77
+ "description": "Google Firebase static hosting",
78
+ "provider": "GCP",
79
+ "type": "static-hosting",
80
+ "notes": "For static sites and SPAs"
81
+ },
82
+ {
83
+ "code": "EC2",
84
+ "label": "AWS EC2",
85
+ "description": "Amazon Elastic Compute Cloud VMs",
86
+ "provider": "AWS",
87
+ "type": "vm",
88
+ "notes": "Traditional VM-based deployments"
89
+ },
90
+ {
91
+ "code": "CR",
92
+ "label": "Google Cloud Run",
93
+ "description": "Google's container-as-a-service platform",
94
+ "provider": "GCP",
95
+ "type": "managed-container",
96
+ "notes": "Managed container runtime with auto-scaling"
97
+ },
98
+ {
99
+ "code": "ECS",
100
+ "label": "AWS ECS",
101
+ "description": "Amazon Elastic Container Service",
102
+ "provider": "AWS",
103
+ "type": "managed-container",
104
+ "notes": "AWS-native container orchestration"
105
+ },
106
+ {
107
+ "code": "AP",
108
+ "label": "AWS App Runner",
109
+ "description": "AWS fully managed container application service",
110
+ "provider": "AWS",
111
+ "type": "managed-container",
112
+ "notes": "Simplified container deployments"
113
+ },
114
+ {
115
+ "code": "EXP",
116
+ "label": "Expo (mobile)",
117
+ "description": "Expo platform for React Native",
118
+ "provider": "Expo",
119
+ "type": "mobile-platform",
120
+ "notes": "React Native mobile app builds and OTA updates"
121
+ },
122
+ {
123
+ "code": "APP",
124
+ "label": "App Stores (iOS/Android)",
125
+ "description": "Apple App Store and Google Play Store",
126
+ "provider": "Apple/Google",
127
+ "type": "mobile-distribution",
128
+ "notes": "Native app distribution"
129
+ },
130
+ {
131
+ "code": "OP",
132
+ "label": "On-Prem (customer environment)",
133
+ "description": "Customer-managed infrastructure",
134
+ "provider": "Customer",
135
+ "type": "on-premise",
136
+ "notes": "Deployed to customer's own infrastructure"
137
+ },
138
+ {
139
+ "code": "LH",
140
+ "label": "Local Host (dev/test deliverable)",
141
+ "description": "Local development or testing environment",
142
+ "provider": "Dev",
143
+ "type": "local",
144
+ "notes": "Not for production use"
145
+ },
146
+ {
147
+ "code": "PYPI",
148
+ "label": "Python Package Index",
149
+ "description": "Python package distribution",
150
+ "provider": "PyPI",
151
+ "type": "package-registry",
152
+ "notes": "For Python SDK/library distribution"
153
+ },
154
+ {
155
+ "code": "NPM",
156
+ "label": "npm Registry",
157
+ "description": "Node.js package distribution",
158
+ "provider": "npm",
159
+ "type": "package-registry",
160
+ "notes": "For JavaScript/TypeScript package distribution"
161
+ },
162
+ {
163
+ "code": "GH",
164
+ "label": "GitHub Releases",
165
+ "description": "GitHub release artifacts",
166
+ "provider": "GitHub",
167
+ "type": "artifact-distribution",
168
+ "notes": "For CLI tools, binaries, and releases"
169
+ },
170
+ {
171
+ "code": "GHA",
172
+ "label": "GitHub Actions",
173
+ "description": "CI/CD on GitHub Actions",
174
+ "provider": "GitHub",
175
+ "type": "ci-cd",
176
+ "notes": "For test suites and automation that runs in CI"
177
+ },
178
+ {
179
+ "code": "AWS",
180
+ "label": "Amazon Web Services (generic)",
181
+ "description": "Generic AWS deployment",
182
+ "provider": "AWS",
183
+ "type": "cloud-platform",
184
+ "notes": "Use when specific AWS service is unknown or mixed"
185
+ },
186
+ {
187
+ "code": "GCP",
188
+ "label": "Google Cloud Platform (generic)",
189
+ "description": "Generic GCP deployment",
190
+ "provider": "GCP",
191
+ "type": "cloud-platform",
192
+ "notes": "Use when specific GCP service is unknown or mixed"
193
+ },
194
+ {
195
+ "code": "AZR",
196
+ "label": "Microsoft Azure (generic)",
197
+ "description": "Generic Azure deployment",
198
+ "provider": "Azure",
199
+ "type": "cloud-platform",
200
+ "notes": "Use when specific Azure service is unknown or mixed"
201
+ }
202
+ ]
@@ -0,0 +1,130 @@
1
+ [
2
+ {
3
+ "code": "API",
4
+ "label": "API service / microservice",
5
+ "description": "RESTful or GraphQL API endpoints, backend services",
6
+ "examples": ["FastAPI backend", "Express API", "Django REST", "gRPC service"],
7
+ "typical_platforms": ["EKS", "GKE", "DK", "LM"],
8
+ "notes": "Use for any backend service exposing HTTP/RPC endpoints"
9
+ },
10
+ {
11
+ "code": "BOT",
12
+ "label": "Chatbot / Automation agent",
13
+ "description": "Event-driven automation, background workers, message processors, bots",
14
+ "examples": ["Discord bot", "Slack bot", "Queue worker", "Cron jobs", "Telegram bot"],
15
+ "typical_platforms": ["EKS", "GKE", "DK", "LM"],
16
+ "notes": "Includes both conversational bots and background automation workers"
17
+ },
18
+ {
19
+ "code": "MA",
20
+ "label": "Mobile App (native or hybrid)",
21
+ "description": "Native or hybrid mobile applications for iOS/Android",
22
+ "examples": ["React Native app", "Flutter app", "Swift/Kotlin native", "Expo app"],
23
+ "typical_platforms": ["EXP", "CF", "APP"],
24
+ "notes": "SKU tracks the app binary/release, not backend services"
25
+ },
26
+ {
27
+ "code": "WA",
28
+ "label": "Web Application (interactive web apps, portals)",
29
+ "description": "Frontend web applications (SPAs, SSR, SSG)",
30
+ "examples": ["React SPA", "Next.js app", "Vue app", "Angular", "Svelte"],
31
+ "typical_platforms": ["VE", "CF", "EKS", "FH"],
32
+ "notes": "Use for user-facing web applications"
33
+ },
34
+ {
35
+ "code": "RAG",
36
+ "label": "Retrieval-Augmented Generation (AI systems with retrieval)",
37
+ "description": "RAG systems with vector search, embeddings, and LLM integration",
38
+ "examples": ["LangChain RAG", "LlamaIndex app", "Pinecone integration", "Semantic search"],
39
+ "typical_platforms": ["EKS", "LM", "EC2"],
40
+ "notes": "Specifically for RAG architectures, not general AI"
41
+ },
42
+ {
43
+ "code": "DA",
44
+ "label": "Data Analytics / Dashboard",
45
+ "description": "Data processing, analytics pipelines, BI dashboards",
46
+ "examples": ["Metabase", "Superset", "dbt models", "Tableau integration"],
47
+ "typical_platforms": ["EKS", "EC2", "GKE"],
48
+ "notes": "Use for analytics/BI tools and data visualization"
49
+ },
50
+ {
51
+ "code": "AI",
52
+ "label": "AI pipeline/agent (non-RAG)",
53
+ "description": "Machine learning models, inference APIs, training pipelines",
54
+ "examples": ["TensorFlow Serving", "PyTorch models", "Hugging Face", "OpenAI integration"],
55
+ "typical_platforms": ["EKS", "LM", "EC2"],
56
+ "notes": "Use for AI/ML services that aren't RAG-specific"
57
+ },
58
+ {
59
+ "code": "CMS",
60
+ "label": "Content Management System",
61
+ "description": "Content management systems, headless CMS, documentation sites",
62
+ "examples": ["Strapi", "Docusaurus", "Sanity", "WordPress"],
63
+ "typical_platforms": ["EKS", "VE", "FH"],
64
+ "notes": "Includes both traditional and headless CMS"
65
+ },
66
+ {
67
+ "code": "DB",
68
+ "label": "Database system / data layer deliverable",
69
+ "description": "Managed databases, data stores, database migrations",
70
+ "examples": ["PostgreSQL", "MongoDB", "Redis", "Elasticsearch"],
71
+ "typical_platforms": ["AWS", "GCP", "AZR", "DK"],
72
+ "notes": "Use for database instances or migration projects"
73
+ },
74
+ {
75
+ "code": "OP",
76
+ "label": "Operations Tool",
77
+ "description": "Internal tooling, admin panels, DevOps utilities, on-prem systems",
78
+ "examples": ["Admin dashboard", "Deployment tool", "Monitoring agent", "CLI tool"],
79
+ "typical_platforms": ["EKS", "DK", "EC2", "OP"],
80
+ "notes": "Internal tools not exposed to end users"
81
+ },
82
+ {
83
+ "code": "ETL",
84
+ "label": "Data pipelines (extract/transform/load)",
85
+ "description": "ETL/ELT pipelines, data ingestion, data transformation",
86
+ "examples": ["Airflow DAGs", "dbt", "Fivetran sync", "Custom ETL"],
87
+ "typical_platforms": ["LM", "EKS", "GKE", "EC2"],
88
+ "notes": "Any data pipeline or transformation workflow"
89
+ },
90
+ {
91
+ "code": "INT",
92
+ "label": "Integration/Orchestration layer",
93
+ "description": "Third-party API integrations, middleware, webhooks",
94
+ "examples": ["Stripe integration", "Salesforce sync", "Zapier connector", "Webhook processor"],
95
+ "typical_platforms": ["LM", "EKS", "CF"],
96
+ "notes": "Services that connect or orchestrate other systems"
97
+ },
98
+ {
99
+ "code": "FX",
100
+ "label": "Feature module/add-on shipped as a distinct unit",
101
+ "description": "Serverless functions, edge functions, feature modules",
102
+ "examples": ["AWS Lambda function", "Cloudflare Workers", "Vercel Functions"],
103
+ "typical_platforms": ["LM", "CF", "VE"],
104
+ "notes": "Small, single-purpose functions or modules"
105
+ },
106
+ {
107
+ "code": "SDK",
108
+ "label": "Client/developer SDK deliverable",
109
+ "description": "Client libraries, SDKs, packages for developers",
110
+ "examples": ["Python package", "npm library", "Go module", "REST client"],
111
+ "typical_platforms": ["PYPI", "NPM", "GH"],
112
+ "notes": "Distributed as packages, not deployed services"
113
+ },
114
+ {
115
+ "code": "CLI",
116
+ "label": "Command-line tool",
117
+ "description": "Command-line interface tools and utilities",
118
+ "examples": ["Deployment CLI", "Admin CLI", "Data migration tool"],
119
+ "typical_platforms": ["GH", "PYPI", "NPM"],
120
+ "notes": "Downloadable/installable CLI tools"
121
+ },
122
+ {
123
+ "code": "QA",
124
+ "label": "QA/Test harness deliverable",
125
+ "description": "Test suites, E2E testing, load testing, QA automation",
126
+ "examples": ["Playwright tests", "k6 load tests", "Postman collections", "Selenium"],
127
+ "typical_platforms": ["GHA", "EKS", "GKE"],
128
+ "notes": "Testing infrastructure and automation"
129
+ }
130
+ ]