securenow 7.6.1 → 7.6.3

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/app-config.js CHANGED
@@ -164,10 +164,84 @@ function readJsonSafe(filepath) {
164
164
  }
165
165
  }
166
166
 
167
+ function fileIfReadable(filepath) {
168
+ if (!filepath) return null;
169
+ try {
170
+ return fs.existsSync(filepath) && fs.statSync(filepath).isFile() ? filepath : null;
171
+ } catch {
172
+ return null;
173
+ }
174
+ }
175
+
176
+ function findUpFile(startDir, relativePath) {
177
+ if (!startDir) return null;
178
+ let dir;
179
+ try {
180
+ dir = path.resolve(startDir);
181
+ } catch {
182
+ return null;
183
+ }
184
+
185
+ while (true) {
186
+ const found = fileIfReadable(path.join(dir, relativePath));
187
+ if (found) return found;
188
+
189
+ const parent = path.dirname(dir);
190
+ if (!parent || parent === dir) return null;
191
+ dir = parent;
192
+ }
193
+ }
194
+
195
+ function uniq(values) {
196
+ const seen = new Set();
197
+ const out = [];
198
+ for (const value of values) {
199
+ if (!value || seen.has(value)) continue;
200
+ seen.add(value);
201
+ out.push(value);
202
+ }
203
+ return out;
204
+ }
205
+
206
+ function resolveLocalCredentialsFile() {
207
+ const starts = [];
208
+ try {
209
+ if (typeof process !== 'undefined' && process.cwd) starts.push(process.cwd());
210
+ } catch {}
211
+
212
+ if (process.env.INIT_CWD) starts.push(process.env.INIT_CWD);
213
+ if (process.argv && process.argv[1]) starts.push(path.dirname(process.argv[1]));
214
+ if (require.main && require.main.filename) starts.push(path.dirname(require.main.filename));
215
+
216
+ for (const start of uniq(starts)) {
217
+ const found = findUpFile(start, path.join('.securenow', 'credentials.json'));
218
+ if (found) return found;
219
+ }
220
+
221
+ return null;
222
+ }
223
+
224
+ function resolvePackageJsonFile() {
225
+ const starts = [];
226
+ try {
227
+ if (typeof process !== 'undefined' && process.cwd) starts.push(process.cwd());
228
+ } catch {}
229
+
230
+ if (process.env.INIT_CWD) starts.push(process.env.INIT_CWD);
231
+ if (process.argv && process.argv[1]) starts.push(path.dirname(process.argv[1]));
232
+ if (require.main && require.main.filename) starts.push(path.dirname(require.main.filename));
233
+
234
+ for (const start of uniq(starts)) {
235
+ const found = findUpFile(start, 'package.json');
236
+ if (found) return found;
237
+ }
238
+
239
+ return null;
240
+ }
241
+
167
242
  function loadLocalCredentials() {
168
243
  try {
169
- const cwd = typeof process !== 'undefined' && process.cwd ? process.cwd() : '.';
170
- return withCredentialDefaults(readJsonSafe(path.join(cwd, '.securenow', 'credentials.json')));
244
+ return withCredentialDefaults(readJsonSafe(resolveLocalCredentialsFile()));
171
245
  } catch {
172
246
  return null;
173
247
  }
@@ -183,8 +257,7 @@ function loadGlobalCredentials() {
183
257
 
184
258
  function loadCredentials() {
185
259
  try {
186
- const cwd = typeof process !== 'undefined' && process.cwd ? process.cwd() : '.';
187
- const local = readJsonSafe(path.join(cwd, '.securenow', 'credentials.json'));
260
+ const local = readJsonSafe(resolveLocalCredentialsFile());
188
261
  const global = readJsonSafe(path.join(os.homedir(), '.securenow', 'credentials.json'));
189
262
  return withCredentialDefaults(mergeCredentials(global, local));
190
263
  } catch {
@@ -194,8 +267,7 @@ function loadCredentials() {
194
267
 
195
268
  function loadPackageJsonName() {
196
269
  try {
197
- const cwd = typeof process !== 'undefined' && process.cwd ? process.cwd() : '.';
198
- const pkg = readJsonSafe(path.join(cwd, 'package.json'));
270
+ const pkg = readJsonSafe(resolvePackageJsonFile());
199
271
  if (pkg && typeof pkg.name === 'string' && pkg.name.trim()) {
200
272
  return pkg.name.trim().replace(/^@[^/]+\//, '');
201
273
  }
package/cli/init.js CHANGED
@@ -114,7 +114,7 @@ async function initNextJs(dir, project, flags) {
114
114
  const existing = findInstrumentationFile(dir);
115
115
  if (existing) {
116
116
  ui.info(`instrumentation file already exists: ${path.relative(dir, existing)}`);
117
- printAgentPrompt('instrumentation', path.basename(existing), nextMajor(project));
117
+ printAgentPrompt('instrumentation', path.basename(existing), nextMajor(project), project);
118
118
  } else {
119
119
  const filePath = path.join(dir, `instrumentation.${ext}`);
120
120
  fs.writeFileSync(filePath, INSTRUMENTATION, 'utf8');
@@ -130,7 +130,7 @@ async function initNextJs(dir, project, flags) {
130
130
  ui.success(`Updated ${path.basename(configPath)} with SecureNow server externalization`);
131
131
  } else {
132
132
  ui.warn(`Could not safely edit ${path.basename(configPath)} automatically.`);
133
- printAgentPrompt('next-config', path.basename(configPath), nextMajor(project));
133
+ printAgentPrompt('next-config', path.basename(configPath), nextMajor(project), project);
134
134
  }
135
135
  } else {
136
136
  const newConfigPath = path.join(dir, 'next.config.mjs');
@@ -180,12 +180,34 @@ function patchNextConfig(configPath, major) {
180
180
  return 'manual';
181
181
  }
182
182
 
183
- function printAgentPrompt(kind, filename, major) {
183
+ function scriptCommand(project, name) {
184
+ return project && project.pkg && project.pkg.scripts && project.pkg.scripts[name]
185
+ ? `npm run ${name}`
186
+ : null;
187
+ }
188
+
189
+ function availableVerificationCommands(project) {
190
+ const commands = [
191
+ scriptCommand(project, 'build'),
192
+ scriptCommand(project, 'test'),
193
+ scriptCommand(project, 'lint'),
194
+ scriptCommand(project, 'dev'),
195
+ project && project.pkg && project.pkg.scripts && project.pkg.scripts.start ? 'npm start' : null,
196
+ ].filter(Boolean);
197
+
198
+ return commands.length ? commands.join(', ') : null;
199
+ }
200
+
201
+ function printAgentPrompt(kind, filename, major, project) {
202
+ const commands = availableVerificationCommands(project);
184
203
  console.log('');
185
204
  ui.heading('Codex/Claude prompt');
186
205
  console.log([
187
206
  'Set up SecureNow in this existing Next.js project without using .env files.',
188
207
  'Use .securenow/credentials.json for local and production configuration; do not add .env files.',
208
+ commands
209
+ ? `Use the project scripts for verification when appropriate: ${commands}. Ask before starting long-running dev/start servers, and ask which command to use if these scripts are not the right customer workflow.`
210
+ : 'Ask the customer which command starts, builds, and tests this app because package.json does not expose an obvious script.',
189
211
  kind === 'instrumentation'
190
212
  ? `Merge this into ${filename}: in register(), return unless process.env.NEXT_RUNTIME === "nodejs"; then dynamically import "securenow/nextjs" and "securenow/nextjs-auto-capture" with /* webpackIgnore: true */ so Next does not bundle OpenTelemetry internals. Preserve all existing instrumentation.`
191
213
  : null,
@@ -195,7 +217,8 @@ function printAgentPrompt(kind, filename, major) {
195
217
  kind === 'next-config' && major < 15
196
218
  ? `Update ${filename} while preserving existing config: enable experimental.instrumentationHook and add securenow to experimental.serverComponentsExternalPackages.`
197
219
  : null,
198
- 'Verify with: npx securenow env, npx securenow test-span, npx securenow status, and the project build command.',
220
+ 'Verify with: npx securenow env, npx securenow test-span, npx securenow status, plus the selected build/test command when available.',
221
+ 'Only smoke-test an HTTP URL when the user provides a local or production URL, the running app prints one, or project config clearly defines one. Do not invent localhost ports or production domains; ask for the URL and skip URL probes if none is available.',
199
222
  ].filter(Boolean).join('\n'));
200
223
  console.log('');
201
224
  }
package/mcp/catalog.js CHANGED
@@ -24,7 +24,10 @@ Safety rules:
24
24
  - Preserve existing middleware, proxy, instrumentation, Docker, PM2, and start scripts.
25
25
 
26
26
  Runbook:
27
- 1. Identify the project root, package manager, framework, start/build/test scripts, process manager files, Docker files, and existing middleware/proxy/instrumentation.
27
+ 1. Identify the project root, package manager, framework, start/build/test scripts, process manager files, Docker files, and existing middleware/proxy/instrumentation.
28
+ - Use the repo's own scripts for verification. If the correct dev/start/build/test command is missing or ambiguous, ask the customer which command to use.
29
+ - Ask before starting long-running dev/start servers. Build/test/lint commands can be run when they are standard project scripts and safe for the current task.
30
+ - Do not invent HTTP URLs. Only probe a local or production URL when the customer provides one, the running app prints one, or project/deploy config clearly defines one.
28
31
  2. Install or upgrade SecureNow with the detected package manager, using securenow@latest. Verify the actual installed version with:
29
32
  node -p "require('./node_modules/securenow/package.json').version"
30
33
  npx securenow version
@@ -60,14 +63,16 @@ Runbook:
60
63
  - Run npx securenow firewall apps and npx securenow firewall status.
61
64
  - Confirm the selected app is present, firewallEnabled is true, and the SecureNow IPDB confidence threshold is visible.
62
65
  - If firewallEnabled is false, run the documented per-app enable command, for example npx securenow firewall enable --app <appKey>, then verify again.
63
- 10. End-to-end proof:
64
- - Run npx securenow doctor.
66
+ 10. End-to-end proof:
67
+ - Run npx securenow doctor.
65
68
  - Run npx securenow env and confirm loggingEnabled, captureBody, captureMultipart, and firewallEnabled resolve to true or 1 from credentials/defaults, unless I explicitly requested firewall-only.
66
- - If available and not in firewall-only mode, send telemetry:
67
- npx securenow test-span securenow.onboarding
68
- npx securenow log send "SecureNow onboarding test" --level info
69
- - Run the repo build/test command if available.
70
- - For MCP-capable clients, optionally smoke-test npx securenow mcp with the securenow_auth_status tool.
69
+ - If available and not in firewall-only mode, send telemetry:
70
+ npx securenow test-span securenow.onboarding
71
+ npx securenow log send "SecureNow onboarding test" --level info
72
+ - Run the repo build/test/lint command when available. If multiple scripts look plausible or no script exists, ask the customer for the intended command instead of guessing.
73
+ - If a local app URL is available, make one simple GET request to a real route to generate request telemetry, then check npx securenow status and traces for the selected environment. If no local URL is available, say that URL smoke testing was skipped.
74
+ - If a production URL is provided or clearly discoverable, make one safe GET request to production and check npx securenow status/traces with --env production. If no production URL is available, do not fabricate one; ask for it or skip production URL smoke testing.
75
+ - For MCP-capable clients, optionally smoke-test npx securenow mcp with the securenow_auth_status tool.
71
76
 
72
77
  Final response:
73
78
  - List every changed file.
@@ -670,6 +675,11 @@ const PROMPTS = [
670
675
  description: 'Generic framework setup prompt for SecureNow tracing, logs, body capture, multipart, and firewall defaults.',
671
676
  arguments: [
672
677
  { name: 'projectRoot', description: 'Project root to configure.', required: false },
678
+ { name: 'startCommand', description: 'Optional customer-approved command to start the app.', required: false },
679
+ { name: 'buildCommand', description: 'Optional customer-approved build command.', required: false },
680
+ { name: 'testCommand', description: 'Optional customer-approved test command.', required: false },
681
+ { name: 'localUrl', description: 'Optional local URL to smoke-test after the app is running.', required: false },
682
+ { name: 'productionUrl', description: 'Optional production URL to smoke-test with --env production.', required: false },
673
683
  ],
674
684
  },
675
685
  {
@@ -701,6 +711,11 @@ function promptMessages(name, args = {}) {
701
711
  type: 'text',
702
712
  text: [
703
713
  args.projectRoot ? `Project root: ${args.projectRoot}` : null,
714
+ args.startCommand ? `Customer-approved start command: ${args.startCommand}` : null,
715
+ args.buildCommand ? `Customer-approved build command: ${args.buildCommand}` : null,
716
+ args.testCommand ? `Customer-approved test command: ${args.testCommand}` : null,
717
+ args.localUrl ? `Local URL for smoke testing: ${args.localUrl}` : null,
718
+ args.productionUrl ? `Production URL for smoke testing: ${args.productionUrl}` : null,
704
719
  UNIVERSAL_SECURENOW_SETUP_PROMPT,
705
720
  ].filter(Boolean).join('\n\n'),
706
721
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securenow",
3
- "version": "7.6.1",
3
+ "version": "7.6.3",
4
4
  "description": "OpenTelemetry instrumentation for Node.js, Next.js, and Nuxt - Send traces and logs to any OTLP-compatible backend",
5
5
  "type": "commonjs",
6
6
  "main": "register.js",