securenow 5.2.2 → 5.3.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/cli.js CHANGED
@@ -1,267 +1,387 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
 
4
- /**
5
- * SecureNow CLI
6
- *
7
- * Usage: npx securenow init [options]
8
- */
4
+ const ui = require('./cli/ui');
9
5
 
10
- const fs = require('fs');
11
- const path = require('path');
6
+ // ── Argument Parser ──
12
7
 
13
- const commands = {
14
- init: initCommand,
15
- help: helpCommand,
16
- version: versionCommand,
17
- };
18
-
19
- // Templates
20
- const templates = {
21
- typescript: `import { registerSecureNow } from 'securenow/nextjs';
22
-
23
- export function register() {
24
- registerSecureNow();
25
- }
8
+ function parseArgs(argv) {
9
+ const positional = [];
10
+ const flags = {};
26
11
 
27
- /**
28
- * Configuration via .env.local:
29
- *
30
- * Required:
31
- * SECURENOW_APPID=my-nextjs-app
32
- *
33
- * Optional:
34
- * SECURENOW_INSTANCE=http://your-otlp-backend:4318
35
- * OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-key"
36
- * OTEL_LOG_LEVEL=info
37
- */
38
- `,
39
- javascript: `const { registerSecureNow } = require('securenow/nextjs');
12
+ for (let i = 0; i < argv.length; i++) {
13
+ const arg = argv[i];
14
+ if (arg.startsWith('--')) {
15
+ const eqIdx = arg.indexOf('=');
16
+ if (eqIdx !== -1) {
17
+ flags[arg.slice(2, eqIdx)] = arg.slice(eqIdx + 1);
18
+ } else {
19
+ const next = argv[i + 1];
20
+ if (next && !next.startsWith('-')) {
21
+ flags[arg.slice(2)] = next;
22
+ i++;
23
+ } else {
24
+ flags[arg.slice(2)] = true;
25
+ }
26
+ }
27
+ } else if (arg.startsWith('-') && arg.length === 2) {
28
+ const shortMap = { f: 'force', y: 'yes', v: 'verbose', j: 'json' };
29
+ const long = shortMap[arg[1]] || arg[1];
30
+ flags[long] = true;
31
+ } else {
32
+ positional.push(arg);
33
+ }
34
+ }
40
35
 
41
- export function register() {
42
- registerSecureNow();
36
+ return { positional, flags };
43
37
  }
44
38
 
45
- /**
46
- * Configuration via .env.local:
47
- *
48
- * Required:
49
- * SECURENOW_APPID=my-nextjs-app
50
- *
51
- * Optional:
52
- * SECURENOW_INSTANCE=http://your-otlp-backend:4318
53
- * OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-key"
54
- * OTEL_LOG_LEVEL=info
55
- */
56
- `,
57
- env: `# SecureNow Configuration
58
- # Required: Your application identifier
59
- SECURENOW_APPID=my-nextjs-app
60
-
61
- # Optional: Your OTLP-compatible backend / collector endpoint
62
- # Default: https://freetrial.securenow.ai:4318
63
- SECURENOW_INSTANCE=http://your-otlp-backend:4318
39
+ // ── Command Registry ──
40
+
41
+ const COMMANDS = {
42
+ login: {
43
+ desc: 'Authenticate with SecureNow',
44
+ usage: 'securenow login [--token <TOKEN>]',
45
+ flags: { token: 'Authenticate with a token directly' },
46
+ run: (a, f) => require('./cli/auth').login(a, f),
47
+ },
48
+ logout: {
49
+ desc: 'Clear stored credentials',
50
+ usage: 'securenow logout',
51
+ run: () => require('./cli/auth').logout(),
52
+ },
53
+ whoami: {
54
+ desc: 'Show current session info',
55
+ usage: 'securenow whoami',
56
+ run: () => require('./cli/auth').whoami(),
57
+ },
58
+ apps: {
59
+ desc: 'Manage applications',
60
+ usage: 'securenow apps <subcommand> [options]',
61
+ sub: {
62
+ list: { desc: 'List all applications', run: (a, f) => require('./cli/apps').list(a, f) },
63
+ create: { desc: 'Create a new application', usage: 'securenow apps create <name> [--hosts host1,host2] [--instance <id>]', run: (a, f) => require('./cli/apps').create(a, f) },
64
+ info: { desc: 'Show application details', usage: 'securenow apps info <id>', run: (a, f) => require('./cli/apps').info(a, f) },
65
+ delete: { desc: 'Delete an application', usage: 'securenow apps delete <id> [--force]', run: (a, f) => require('./cli/apps').remove(a, f) },
66
+ default: { desc: 'Set default application', usage: 'securenow apps default <key>', run: (a, f) => require('./cli/apps').setDefault(a, f) },
67
+ },
68
+ defaultSub: 'list',
69
+ },
70
+ traces: {
71
+ desc: 'View and analyze traces',
72
+ usage: 'securenow traces <subcommand> [options]',
73
+ sub: {
74
+ list: { desc: 'List recent traces', flags: { app: 'App key', limit: 'Max results', start: 'Start time', end: 'End time' }, run: (a, f) => require('./cli/monitor').tracesList(a, f) },
75
+ show: { desc: 'Show trace details', usage: 'securenow traces show <traceId>', run: (a, f) => require('./cli/monitor').tracesShow(a, f) },
76
+ analyze: { desc: 'AI-analyze a trace', usage: 'securenow traces analyze <traceId>', run: (a, f) => require('./cli/monitor').tracesAnalyze(a, f) },
77
+ },
78
+ defaultSub: 'list',
79
+ },
80
+ logs: {
81
+ desc: 'View application logs',
82
+ usage: 'securenow logs [options]',
83
+ sub: {
84
+ list: { desc: 'List recent logs', flags: { app: 'App key', limit: 'Max results', minutes: 'Time window in minutes', level: 'Filter by level' }, run: (a, f) => require('./cli/monitor').logsList(a, f) },
85
+ trace: { desc: 'Show logs for a trace', usage: 'securenow logs trace <traceId>', run: (a, f) => require('./cli/monitor').logsTrace(a, f) },
86
+ },
87
+ defaultSub: 'list',
88
+ },
89
+ issues: {
90
+ desc: 'Manage security issues',
91
+ usage: 'securenow issues <subcommand> [options]',
92
+ sub: {
93
+ list: { desc: 'List issues', flags: { app: 'App key', status: 'Filter by status' }, run: (a, f) => require('./cli/monitor').issuesList(a, f) },
94
+ show: { desc: 'Show issue details', usage: 'securenow issues show <id>', run: (a, f) => require('./cli/monitor').issuesShow(a, f) },
95
+ resolve: { desc: 'Resolve an issue', usage: 'securenow issues resolve <id>', run: (a, f) => require('./cli/monitor').issuesResolve(a, f) },
96
+ },
97
+ defaultSub: 'list',
98
+ },
99
+ notifications: {
100
+ desc: 'Manage notifications',
101
+ usage: 'securenow notifications <subcommand> [options]',
102
+ sub: {
103
+ list: { desc: 'List notifications', flags: { limit: 'Max results', page: 'Page number' }, run: (a, f) => require('./cli/monitor').notificationsList(a, f) },
104
+ read: { desc: 'Mark notification as read', usage: 'securenow notifications read <id>', run: (a, f) => require('./cli/monitor').notificationsRead(a, f) },
105
+ 'read-all': { desc: 'Mark all as read', run: () => require('./cli/monitor').notificationsReadAll() },
106
+ unread: { desc: 'Show unread count', run: () => require('./cli/monitor').notificationsUnread() },
107
+ },
108
+ defaultSub: 'list',
109
+ },
110
+ alerts: {
111
+ desc: 'Manage alerting',
112
+ usage: 'securenow alerts <subcommand> [options]',
113
+ sub: {
114
+ rules: { desc: 'List alert rules', run: (a, f) => require('./cli/security').alertRulesList(a, f) },
115
+ channels: { desc: 'List alert channels', run: (a, f) => require('./cli/security').alertChannelsList(a, f) },
116
+ history: { desc: 'View alert history', flags: { limit: 'Max results' }, run: (a, f) => require('./cli/security').alertHistoryList(a, f) },
117
+ },
118
+ defaultSub: 'rules',
119
+ },
120
+ blocklist: {
121
+ desc: 'Manage IP blocklist',
122
+ usage: 'securenow blocklist <subcommand> [options]',
123
+ sub: {
124
+ list: { desc: 'List blocked IPs', run: (a, f) => require('./cli/security').blocklistList(a, f) },
125
+ add: { desc: 'Block an IP', usage: 'securenow blocklist add <ip> [--reason <reason>]', run: (a, f) => require('./cli/security').blocklistAdd(a, f) },
126
+ remove: { desc: 'Unblock an IP', usage: 'securenow blocklist remove <id>', run: (a, f) => require('./cli/security').blocklistRemove(a, f) },
127
+ stats: { desc: 'Blocklist statistics', run: (a, f) => require('./cli/security').blocklistStats(a, f) },
128
+ },
129
+ defaultSub: 'list',
130
+ },
131
+ trusted: {
132
+ desc: 'Manage trusted IPs',
133
+ usage: 'securenow trusted <subcommand> [options]',
134
+ sub: {
135
+ list: { desc: 'List trusted IPs', run: (a, f) => require('./cli/security').trustedList(a, f) },
136
+ add: { desc: 'Add trusted IP', usage: 'securenow trusted add <ip> [--label <label>]', run: (a, f) => require('./cli/security').trustedAdd(a, f) },
137
+ remove: { desc: 'Remove trusted IP', usage: 'securenow trusted remove <id>', run: (a, f) => require('./cli/security').trustedRemove(a, f) },
138
+ },
139
+ defaultSub: 'list',
140
+ },
141
+ ip: {
142
+ desc: 'IP intelligence lookup',
143
+ usage: 'securenow ip <ip-address>',
144
+ sub: {
145
+ lookup: { desc: 'Look up IP intelligence', run: (a, f) => require('./cli/security').ipLookup(a, f) },
146
+ traces: { desc: 'Show traces for an IP', usage: 'securenow ip traces <ip>', run: (a, f) => require('./cli/security').ipTraces(a, f) },
147
+ },
148
+ defaultAction: (a, f) => require('./cli/security').ipLookup(a, f),
149
+ },
150
+ forensics: {
151
+ desc: 'Run forensic queries (natural language → SQL)',
152
+ usage: 'securenow forensics <query>',
153
+ sub: {
154
+ query: { desc: 'Run a forensic query', run: (a, f) => require('./cli/security').forensicsQuery(a, f) },
155
+ library: { desc: 'View saved queries', run: (a, f) => require('./cli/security').forensicsLibrary(a, f) },
156
+ },
157
+ defaultAction: (a, f) => require('./cli/security').forensicsQuery(a, f),
158
+ },
159
+ 'api-map': {
160
+ desc: 'View API map',
161
+ usage: 'securenow api-map [stats]',
162
+ sub: {
163
+ list: { desc: 'List discovered API endpoints', run: (a, f) => require('./cli/security').apiMapList(a, f) },
164
+ stats: { desc: 'API map statistics', run: (a, f) => require('./cli/security').apiMapStats(a, f) },
165
+ },
166
+ defaultSub: 'list',
167
+ },
168
+ instances: {
169
+ desc: 'Manage ClickHouse instances',
170
+ usage: 'securenow instances <subcommand> [options]',
171
+ sub: {
172
+ list: { desc: 'List instances', run: (a, f) => require('./cli/security').instancesList(a, f) },
173
+ test: { desc: 'Test instance connection', usage: 'securenow instances test <id>', run: (a, f) => require('./cli/security').instancesTest(a, f) },
174
+ },
175
+ defaultSub: 'list',
176
+ },
177
+ analytics: {
178
+ desc: 'View response analytics',
179
+ usage: 'securenow analytics [--app <key>]',
180
+ run: (a, f) => require('./cli/security').analytics(a, f),
181
+ },
182
+ status: {
183
+ desc: 'Dashboard overview',
184
+ usage: 'securenow status [--app <key>]',
185
+ run: (a, f) => require('./cli/monitor').status(a, f),
186
+ },
187
+ config: {
188
+ desc: 'Manage CLI configuration',
189
+ usage: 'securenow config <set|get> [key] [value]',
190
+ sub: {
191
+ set: {
192
+ desc: 'Set a config value',
193
+ usage: 'securenow config set <key> <value>',
194
+ run: (a) => {
195
+ const conf = require('./cli/config');
196
+ const [key, ...rest] = a;
197
+ const value = rest.join(' ');
198
+ if (!key || !value) { ui.error('Usage: securenow config set <key> <value>'); process.exit(1); }
199
+ conf.setConfigValue(key, value);
200
+ ui.success(`${key} = ${value}`);
201
+ },
202
+ },
203
+ get: {
204
+ desc: 'Get a config value',
205
+ usage: 'securenow config get [key]',
206
+ run: (a) => {
207
+ const conf = require('./cli/config');
208
+ const key = a[0];
209
+ if (key) {
210
+ const val = conf.getConfigValue(key);
211
+ console.log(val != null ? val : ui.c.dim('(not set)'));
212
+ } else {
213
+ const all = conf.loadConfig();
214
+ console.log('');
215
+ ui.keyValue(Object.entries(all).map(([k, v]) => [k, v != null ? String(v) : ui.c.dim('(not set)')]));
216
+ console.log('');
217
+ }
218
+ },
219
+ },
220
+ path: {
221
+ desc: 'Show config file path',
222
+ run: () => {
223
+ const conf = require('./cli/config');
224
+ console.log(`Config: ${conf.CONFIG_FILE}`);
225
+ console.log(`Credentials: ${conf.CREDENTIALS_FILE}`);
226
+ },
227
+ },
228
+ },
229
+ },
230
+ init: {
231
+ desc: 'Initialize SecureNow instrumentation',
232
+ usage: 'securenow init [--ts|--js] [--src|--root] [--force]',
233
+ flags: { typescript: 'Force TypeScript', javascript: 'Force JavaScript', src: 'Create in src/', root: 'Create in root', force: 'Overwrite existing' },
234
+ run: (a, f) => require('./cli/init').init(a, f),
235
+ },
236
+ version: {
237
+ desc: 'Show CLI version',
238
+ run: () => {
239
+ try {
240
+ const pkg = require('./package.json');
241
+ console.log(`securenow v${pkg.version}`);
242
+ } catch {
243
+ console.log('securenow (version unknown)');
244
+ }
245
+ },
246
+ },
247
+ };
64
248
 
65
- # Optional: API key or authentication headers
66
- # OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-api-key-here"
249
+ // ── Help System ──
67
250
 
68
- # Optional: Log level (debug|info|warn|error)
69
- # OTEL_LOG_LEVEL=info
70
- `,
71
- };
251
+ function showBanner() {
252
+ console.log('');
253
+ console.log(` ${ui.c.bold(ui.c.cyan('SecureNow CLI'))} ${ui.c.dim('— Security observability from the terminal')}`);
254
+ console.log('');
255
+ }
72
256
 
73
- function initCommand(args) {
74
- const flags = parseFlags(args);
75
- const cwd = process.cwd();
76
-
77
- console.log('\n🚀 SecureNow Setup\n');
78
-
79
- // Check if Next.js project
80
- const isNextJs = isNextJsProject(cwd);
81
- if (!isNextJs && !flags.force) {
82
- console.log('⚠️ This doesn\'t appear to be a Next.js project.');
83
- console.log(' If you want to proceed anyway, use: npx securenow init --force\n');
84
- process.exit(1);
85
- }
86
-
87
- // Determine TypeScript or JavaScript
88
- const useTypeScript = flags.typescript ||
89
- (!flags.javascript && fs.existsSync(path.join(cwd, 'tsconfig.json')));
90
-
91
- // Determine if using src folder
92
- const useSrc = flags.src ||
93
- (!flags.root && fs.existsSync(path.join(cwd, 'src')));
94
-
95
- // Construct file paths
96
- const fileName = useTypeScript ? 'instrumentation.ts' : 'instrumentation.js';
97
- const filePath = useSrc
98
- ? path.join(cwd, 'src', fileName)
99
- : path.join(cwd, fileName);
100
-
101
- // Check if file already exists
102
- if (fs.existsSync(filePath) && !flags.force) {
103
- console.log(`❌ ${useSrc ? 'src/' : ''}${fileName} already exists`);
104
- console.log(' Use --force to overwrite\n');
105
- process.exit(1);
106
- }
107
-
108
- // Create instrumentation file
109
- try {
110
- const template = useTypeScript ? templates.typescript : templates.javascript;
111
-
112
- // Ensure src directory exists if needed
113
- if (useSrc) {
114
- fs.mkdirSync(path.join(cwd, 'src'), { recursive: true });
257
+ function showHelp(commandName) {
258
+ if (commandName && COMMANDS[commandName]) {
259
+ const cmd = COMMANDS[commandName];
260
+ showBanner();
261
+ console.log(` ${ui.c.bold(commandName)} ${cmd.desc}`);
262
+ console.log('');
263
+ if (cmd.usage) {
264
+ console.log(` ${ui.c.bold('USAGE')}`);
265
+ console.log(` ${cmd.usage}`);
266
+ console.log('');
115
267
  }
116
-
117
- fs.writeFileSync(filePath, template, 'utf8');
118
- console.log(`✅ Created ${useSrc ? 'src/' : ''}${fileName}`);
119
- } catch (error) {
120
- console.error(`❌ Failed to create instrumentation file: ${error.message}\n`);
121
- process.exit(1);
268
+ if (cmd.sub) {
269
+ console.log(` ${ui.c.bold('SUBCOMMANDS')}`);
270
+ const entries = Object.entries(cmd.sub);
271
+ const maxLen = entries.reduce((m, [k]) => Math.max(m, k.length), 0);
272
+ for (const [name, sub] of entries) {
273
+ console.log(` ${ui.c.cyan(name.padEnd(maxLen + 2))} ${sub.desc}`);
274
+ }
275
+ console.log('');
276
+ }
277
+ if (cmd.flags) {
278
+ console.log(` ${ui.c.bold('FLAGS')}`);
279
+ for (const [flag, desc] of Object.entries(cmd.flags)) {
280
+ console.log(` --${ui.c.cyan(flag.padEnd(16))} ${desc}`);
281
+ }
282
+ console.log('');
283
+ }
284
+ console.log(` ${ui.c.bold('GLOBAL FLAGS')}`);
285
+ console.log(` --${ui.c.cyan('json'.padEnd(16))} Output as JSON`);
286
+ console.log(` --${ui.c.cyan('help'.padEnd(16))} Show help`);
287
+ console.log('');
288
+ return;
122
289
  }
123
-
124
- // Create .env.local if it doesn't exist
125
- const envPath = path.join(cwd, '.env.local');
126
- if (!fs.existsSync(envPath) || flags.force) {
127
- try {
128
- fs.writeFileSync(envPath, templates.env, 'utf8');
129
- console.log(' Created .env.local template');
130
- } catch (error) {
131
- console.warn(`⚠️ Could not create .env.local: ${error.message}`);
290
+
291
+ showBanner();
292
+
293
+ const groups = {
294
+ 'Authentication': ['login', 'logout', 'whoami'],
295
+ 'Applications': ['apps', 'init', 'status'],
296
+ 'Observe': ['traces', 'logs', 'analytics'],
297
+ 'Detect & Respond': ['issues', 'notifications', 'alerts'],
298
+ 'Investigate': ['ip', 'forensics', 'api-map'],
299
+ 'Remediation': ['blocklist', 'trusted'],
300
+ 'Settings': ['instances', 'config', 'version'],
301
+ };
302
+
303
+ for (const [group, cmds] of Object.entries(groups)) {
304
+ console.log(` ${ui.c.bold(group)}`);
305
+ for (const name of cmds) {
306
+ const cmd = COMMANDS[name];
307
+ if (cmd) {
308
+ console.log(` ${ui.c.cyan(name.padEnd(18))} ${cmd.desc}`);
309
+ }
132
310
  }
133
- } else {
134
- console.log('ℹ️ .env.local already exists (skipped)');
311
+ console.log('');
135
312
  }
136
-
137
- // Success message
138
- console.log('\n┌─────────────────────────────────────────────────┐');
139
- console.log('│ 🎉 Setup Complete! │');
140
- console.log('│ │');
141
- console.log('│ Next steps: │');
142
- console.log('│ │');
143
- console.log('│ 1. Edit .env.local and configure: │');
144
- console.log('│ SECURENOW_APPID=your-app-name │');
145
- console.log('│ SECURENOW_INSTANCE=http://your-otlp-backend:4318 │');
146
- console.log('│ │');
147
- console.log('│ 2. Start your Next.js app: npm run dev │');
148
- console.log('│ │');
149
- console.log('│ 3. Check SecureNow dashboard for traces! │');
150
- console.log('│ │');
151
- console.log('│ 📚 Documentation: │');
152
- console.log('│ node_modules/securenow/NEXTJS-GUIDE.md │');
153
- console.log('└─────────────────────────────────────────────────┘\n');
154
- }
155
313
 
156
- function helpCommand() {
157
- console.log(`
158
- SecureNow CLI - OpenTelemetry instrumentation for Next.js
314
+ console.log(` ${ui.c.bold('GLOBAL FLAGS')}`);
315
+ console.log(` --${ui.c.cyan('json'.padEnd(16))} Output as JSON`);
316
+ console.log(` --${ui.c.cyan('help'.padEnd(16))} Show help for a command`);
317
+ console.log('');
318
+ console.log(` ${ui.c.dim('Run')} securenow help <command> ${ui.c.dim('for detailed usage')}`);
319
+ console.log('');
320
+ }
159
321
 
160
- USAGE:
161
- npx securenow <command> [options]
322
+ // ── Main Router ──
162
323
 
163
- COMMANDS:
164
- init Initialize SecureNow in your Next.js project
165
- help Show this help message
166
- version Show package version
324
+ async function main() {
325
+ const { positional, flags } = parseArgs(process.argv.slice(2));
326
+ const cmdName = positional[0] || 'help';
167
327
 
168
- OPTIONS:
169
- --typescript, --ts Force TypeScript (creates instrumentation.ts)
170
- --javascript, --js Force JavaScript (creates instrumentation.js)
171
- --src Create file in src/ folder
172
- --root Create file in project root
173
- --force, -f Overwrite existing files
328
+ if (cmdName === 'help' || flags.help) {
329
+ showHelp(flags.help === true ? positional[0] : flags.help || positional[1] || (cmdName !== 'help' ? cmdName : null));
330
+ return;
331
+ }
174
332
 
175
- EXAMPLES:
176
- # Auto-detect and setup
177
- npx securenow init
333
+ const cmd = COMMANDS[cmdName];
334
+ if (!cmd) {
335
+ ui.error(`Unknown command: ${cmdName}`);
336
+ ui.info('Run `securenow help` for a list of commands.');
337
+ process.exit(1);
338
+ }
178
339
 
179
- # Force TypeScript in src folder
180
- npx securenow init --typescript --src
340
+ if (cmd.run && !cmd.sub) {
341
+ await cmd.run(positional.slice(1), flags);
342
+ return;
343
+ }
181
344
 
182
- # Force JavaScript in root
183
- npx securenow init --javascript --root
345
+ if (cmd.sub) {
346
+ let subName = positional[1];
347
+ let subArgs = positional.slice(2);
348
+
349
+ if (subName && cmd.sub[subName]) {
350
+ if (flags.help) {
351
+ showHelp(cmdName);
352
+ return;
353
+ }
354
+ await cmd.sub[subName].run(subArgs, flags);
355
+ return;
356
+ }
184
357
 
185
- # Overwrite existing files
186
- npx securenow init --force
358
+ if (cmd.defaultAction) {
359
+ const allArgs = subName ? [subName, ...subArgs] : [];
360
+ await cmd.defaultAction(allArgs, flags);
361
+ return;
362
+ }
187
363
 
188
- DOCUMENTATION:
189
- Quick Start: node_modules/securenow/NEXTJS-QUICKSTART.md
190
- Full Guide: node_modules/securenow/NEXTJS-GUIDE.md
191
- Examples: node_modules/securenow/examples/
192
- `);
193
- }
364
+ if (cmd.defaultSub) {
365
+ const allArgs = subName ? [subName, ...subArgs] : [];
366
+ await cmd.sub[cmd.defaultSub].run(allArgs, flags);
367
+ return;
368
+ }
194
369
 
195
- function versionCommand() {
196
- try {
197
- const packageJson = require('./package.json');
198
- console.log(`securenow v${packageJson.version}`);
199
- } catch (error) {
200
- console.log('securenow (version unknown)');
370
+ showHelp(cmdName);
371
+ return;
201
372
  }
202
- }
203
373
 
204
- // Utility functions
205
- function parseFlags(args) {
206
- const flags = {};
207
-
208
- for (let i = 0; i < args.length; i++) {
209
- const arg = args[i];
210
-
211
- if (arg === '--typescript' || arg === '--ts') {
212
- flags.typescript = true;
213
- } else if (arg === '--javascript' || arg === '--js') {
214
- flags.javascript = true;
215
- } else if (arg === '--src') {
216
- flags.src = true;
217
- } else if (arg === '--root') {
218
- flags.root = true;
219
- } else if (arg === '--force' || arg === '-f') {
220
- flags.force = true;
221
- }
374
+ if (cmd.run) {
375
+ await cmd.run(positional.slice(1), flags);
222
376
  }
223
-
224
- return flags;
225
377
  }
226
378
 
227
- function isNextJsProject(dir) {
228
- try {
229
- const packageJsonPath = path.join(dir, 'package.json');
230
- if (!fs.existsSync(packageJsonPath)) return false;
231
-
232
- const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
233
- const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
234
-
235
- return !!deps.next;
236
- } catch (error) {
237
- return false;
379
+ main().catch((err) => {
380
+ if (err.name !== 'CLIError') {
381
+ ui.error(err.message || 'An unexpected error occurred');
238
382
  }
239
- }
240
-
241
- // Main
242
- function main() {
243
- const args = process.argv.slice(2);
244
- const command = args[0] || 'help';
245
- const commandFn = commands[command];
246
-
247
- if (!commandFn) {
248
- console.error(`Unknown command: ${command}`);
249
- console.log('Run "npx securenow help" for usage\n');
250
- process.exit(1);
383
+ if (process.env.SECURENOW_DEBUG) {
384
+ console.error(err.stack || err);
251
385
  }
252
-
253
- commandFn(args.slice(1));
254
- }
255
-
256
- if (require.main === module) {
257
- main();
258
- }
259
-
260
- module.exports = { main };
261
-
262
-
263
-
264
-
265
-
266
-
267
-
386
+ process.exit(1);
387
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securenow",
3
- "version": "5.2.2",
3
+ "version": "5.3.1",
4
4
  "description": "OpenTelemetry instrumentation for Node.js and Next.js - Send traces and logs to any OTLP-compatible backend",
5
5
  "type": "commonjs",
6
6
  "main": "register.js",
@@ -20,6 +20,7 @@
20
20
  "observability",
21
21
  "apm",
22
22
  "monitoring",
23
+ "cli",
23
24
  "nextjs",
24
25
  "next.js",
25
26
  "instrumentation",
@@ -84,6 +85,7 @@
84
85
  "nextjs-wrapper.js",
85
86
  "nextjs-wrapper.d.ts",
86
87
  "cli.js",
88
+ "cli/",
87
89
  "free-trial-banner.js",
88
90
  "postinstall.js",
89
91
  "register-vite.js",