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/apps.js +169 -0
- package/cli/auth.js +208 -0
- package/cli/client.js +115 -0
- package/cli/config.js +111 -0
- package/cli/init.js +100 -0
- package/cli/monitor.js +488 -0
- package/cli/security.js +681 -0
- package/cli/ui.js +312 -0
- package/cli.js +354 -234
- package/package.json +3 -1
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
|
-
|
|
11
|
-
const path = require('path');
|
|
6
|
+
// ── Argument Parser ──
|
|
12
7
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
42
|
-
registerSecureNow();
|
|
36
|
+
return { positional, flags };
|
|
43
37
|
}
|
|
44
38
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
66
|
-
# OTEL_EXPORTER_OTLP_HEADERS="x-api-key=your-api-key-here"
|
|
249
|
+
// ── Help System ──
|
|
67
250
|
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
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
|
-
|
|
157
|
-
console.log(`
|
|
158
|
-
|
|
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
|
-
|
|
161
|
-
npx securenow <command> [options]
|
|
322
|
+
// ── Main Router ──
|
|
162
323
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
|
|
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
|
-
|
|
180
|
-
|
|
340
|
+
if (cmd.run && !cmd.sub) {
|
|
341
|
+
await cmd.run(positional.slice(1), flags);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
181
344
|
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
186
|
-
|
|
358
|
+
if (cmd.defaultAction) {
|
|
359
|
+
const allArgs = subName ? [subName, ...subArgs] : [];
|
|
360
|
+
await cmd.defaultAction(allArgs, flags);
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
187
363
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
196
|
-
|
|
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
|
-
|
|
205
|
-
|
|
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
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
-
|
|
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.
|
|
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",
|