securenow 5.15.2 → 5.16.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/NPM_README.md CHANGED
@@ -148,7 +148,10 @@ npx securenow login
148
148
  # Or use a token for CI/headless environments
149
149
  npx securenow login --token <YOUR_JWT>
150
150
 
151
- # Check who you're logged in as
151
+ # Log in for this project only (per-project credentials)
152
+ npx securenow login --local
153
+
154
+ # Check who you're logged in as (shows auth source)
152
155
  npx securenow whoami
153
156
  ```
154
157
 
@@ -327,12 +330,15 @@ npx securenow config set format json
327
330
  npx securenow config path
328
331
  ```
329
332
 
330
- Config files are stored in `~/.securenow/`:
333
+ Config files are stored in `~/.securenow/` (global) or `.securenow/` in the project root (per-project):
331
334
 
332
335
  | File | Description |
333
336
  |------|-------------|
334
- | `config.json` | API URL, default app, output format |
335
- | `credentials.json` | Auth token (file permissions: 0600) |
337
+ | `~/.securenow/config.json` | API URL, default app, output format |
338
+ | `~/.securenow/credentials.json` | Auth token — global (file permissions: 0600) |
339
+ | `.securenow/credentials.json` | Auth token — project-local (use `login --local`) |
340
+
341
+ **Resolution order:** `SECURENOW_TOKEN` env var → project `.securenow/credentials.json` → global `~/.securenow/credentials.json`.
336
342
 
337
343
  ### Global Flags
338
344
 
@@ -348,14 +354,37 @@ Every command supports these flags:
348
354
 
349
355
  | Variable | Description |
350
356
  |----------|-------------|
357
+ | `SECURENOW_TOKEN` | JWT token — overrides all file-based credentials |
351
358
  | `SECURENOW_API_URL` | Override the API base URL |
352
359
  | `SECURENOW_DEBUG` | Show stack traces on errors |
353
360
  | `NO_COLOR` | Disable colored output |
354
361
 
362
+ ### Multi-Project Sessions
363
+
364
+ Use `--local` to maintain separate logins per project on the same machine:
365
+
366
+ ```bash
367
+ # In project A — log in as user-a@company.com
368
+ cd ~/projects/project-a
369
+ npx securenow login --local
370
+
371
+ # In project B — log in as user-b@company.com
372
+ cd ~/projects/project-b
373
+ npx securenow login --local
374
+
375
+ # Each project uses its own credentials independently
376
+ npx securenow whoami # Shows auth source: project (.securenow/)
377
+ ```
378
+
379
+ You can also use the `SECURENOW_TOKEN` env var for per-terminal sessions without touching any files.
380
+
355
381
  ### CI/CD Integration
356
382
 
357
383
  ```bash
358
- # Authenticate with a token in CI
384
+ # Authenticate with a token in CI (env var — no file needed)
385
+ SECURENOW_TOKEN=$MY_SECRET npx securenow issues --json
386
+
387
+ # Or use login with explicit token
359
388
  npx securenow login --token $SECURENOW_TOKEN
360
389
 
361
390
  # Use --json for machine-readable output
@@ -375,9 +404,9 @@ fi
375
404
  | Category | Command | Description |
376
405
  |----------|---------|-------------|
377
406
  | **Setup** | `init` | Auto-scaffold instrumentation for your framework |
378
- | **Auth** | `login` | Authenticate via browser or `--token` |
379
- | | `logout` | Clear credentials |
380
- | | `whoami` | Show session info |
407
+ | **Auth** | `login` | Authenticate via browser, `--token`, or `--local` |
408
+ | | `logout` | Clear credentials (`--local` for project only) |
409
+ | | `whoami` | Show session info and auth source |
381
410
  | **Apps** | `apps` | List applications |
382
411
  | | `apps create <name>` | Create application |
383
412
  | | `apps info <id>` | Application details |
package/README.md CHANGED
@@ -264,8 +264,10 @@ Most users won't need this — just add `-r securenow/register` to your existing
264
264
  |---------|-------------|
265
265
  | `securenow login` | Log in via browser (opens OAuth flow) |
266
266
  | `securenow login --token <TOKEN>` | Log in with a token (for CI/headless) |
267
+ | `securenow login --local` | Log in and save credentials to the current project only |
267
268
  | `securenow logout` | Clear stored credentials |
268
- | `securenow whoami` | Show current session info |
269
+ | `securenow logout --local` | Clear project-local credentials only |
270
+ | `securenow whoami` | Show current session info (including auth source) |
269
271
 
270
272
  ### Applications
271
273
 
@@ -356,15 +358,19 @@ Most users won't need this — just add `-r securenow/register` to your existing
356
358
  | `--json` | Output as JSON (works on every command) |
357
359
  | `--help` | Show help for any command |
358
360
  | `--app <key>` | Specify app key (or set default with `config set defaultApp`) |
361
+ | `--local` | Save/clear credentials per-project (login/logout only) |
359
362
 
360
363
  ### Configuration
361
364
 
362
- Credentials and settings are stored in `~/.securenow/`:
365
+ Credentials and settings are stored in `~/.securenow/` (global) or `.securenow/` (per-project):
363
366
 
364
367
  | File | Purpose |
365
368
  |------|---------|
366
369
  | `~/.securenow/config.json` | API URL, default app, preferences |
367
- | `~/.securenow/credentials.json` | Auth token (restricted permissions) |
370
+ | `~/.securenow/credentials.json` | Auth token — global (restricted permissions) |
371
+ | `.securenow/credentials.json` | Auth token — project-local (use `login --local`) |
372
+
373
+ **Credential resolution order:** `SECURENOW_TOKEN` env var → project `.securenow/credentials.json` → global `~/.securenow/credentials.json`.
368
374
 
369
375
  Override the API URL with `securenow config set apiUrl <url>` or the `SECURENOW_API_URL` environment variable.
370
376
 
package/SKILL-CLI.md CHANGED
@@ -18,9 +18,12 @@ npx securenow <command>
18
18
  ```bash
19
19
  securenow login # opens browser OAuth; stores JWT in ~/.securenow/credentials.json
20
20
  securenow login --token <JWT> # headless / CI login (get token from dashboard Settings)
21
- securenow whoami # verify session
21
+ securenow login --local # save credentials to this project only (.securenow/)
22
+ securenow whoami # verify session (shows auth source)
22
23
  ```
23
24
 
25
+ **Per-project credentials:** Use `--local` to keep separate logins in different project directories on the same machine. Credentials resolve in order: `SECURENOW_TOKEN` env var → project `.securenow/credentials.json` → global `~/.securenow/credentials.json`.
26
+
24
27
  ### Integrate With Your App
25
28
 
26
29
  The CLI can also instrument any Node.js app at launch — no code changes:
@@ -47,22 +50,25 @@ Save this file as `.cursor/skills/securenow-cli/SKILL.md` in your project. Your
47
50
 
48
51
  ## Configuration
49
52
 
50
- Config lives in `~/.securenow/`:
53
+ Config lives in `~/.securenow/` (global) and optionally `.securenow/` (per-project):
51
54
 
52
55
  | File | Content |
53
56
  |------|---------|
54
- | `config.json` | `apiUrl`, `appUrl`, `defaultApp`, `output` |
55
- | `credentials.json` | `token`, `email`, `expiresAt` |
57
+ | `~/.securenow/config.json` | `apiUrl`, `appUrl`, `defaultApp`, `output` |
58
+ | `~/.securenow/credentials.json` | `token`, `email`, `expiresAt` (global) |
59
+ | `.securenow/credentials.json` | `token`, `email`, `expiresAt` (project-local, use `login --local`) |
60
+
61
+ **Credential resolution order:** `SECURENOW_TOKEN` env var → `.securenow/credentials.json` (project) → `~/.securenow/credentials.json` (global).
56
62
 
57
63
  ```bash
58
64
  securenow config set apiUrl https://api.securenow.ai
59
65
  securenow config set defaultApp my-app-key
60
66
  securenow config get # show all
61
67
  securenow config get defaultApp # show one
62
- securenow config path # print file paths
68
+ securenow config path # print file paths + active auth source
63
69
  ```
64
70
 
65
- Environment overrides: `SECURENOW_API_URL`, `SECURENOW_APP_URL`, `SECURENOW_APP` (default app key).
71
+ Environment overrides: `SECURENOW_TOKEN` (JWT), `SECURENOW_API_URL`, `SECURENOW_APP_URL`, `SECURENOW_APP` (default app key).
66
72
 
67
73
  ## Global Flags
68
74
 
@@ -94,10 +100,12 @@ Spawns `node --require securenow/register [--import otel/hook.mjs] <script>`. ES
94
100
  ### Authentication
95
101
 
96
102
  ```bash
97
- securenow login # browser-based OAuth (starts local callback server)
103
+ securenow login # browser-based OAuth (stores in global ~/.securenow/)
98
104
  securenow login --token <JWT> # headless / CI login
99
- securenow logout # clear ~/.securenow/credentials.json
100
- securenow whoami # show email, user ID, API URL, expiry, default app
105
+ securenow login --local # save credentials to project .securenow/ (per-project session)
106
+ securenow logout # clear active credentials (local if present, else global)
107
+ securenow logout --local # clear project-local credentials only
108
+ securenow whoami # show email, user ID, API URL, auth source, expiry, default app
101
109
  ```
102
110
 
103
111
  ### Applications
@@ -389,8 +397,8 @@ All commands support `--json` for structured output. When piping to other tools
389
397
 
390
398
  | Exit code / Error | Meaning | Recovery |
391
399
  |------------------|---------|----------|
392
- | `Session expired` | JWT expired | `securenow login` |
393
- | `Not logged in` | No token in `~/.securenow/credentials.json` | `securenow login` |
400
+ | `Session expired` | JWT expired | `securenow login` (or `login --local`) |
401
+ | `Not logged in` | No token found | `securenow login` or set `SECURENOW_TOKEN` env var |
394
402
  | `Access denied (403)` | Insufficient plan or permissions | Upgrade plan or check user role |
395
403
  | `Cannot connect` | API unreachable | Check `SECURENOW_API_URL` or network |
396
404
  | `Unknown command` | Typo or unrecognized command | `securenow help` |
package/cli/auth.js CHANGED
@@ -58,7 +58,16 @@ async function loginWithBrowser() {
58
58
  }
59
59
 
60
60
  if (token) {
61
- res.end('<html><body style="font-family:system-ui;text-align:center;padding:60px"><h2 style="color:#22c55e">✓ Logged in to SecureNow</h2><p>You can close this window and return to the terminal.</p></body></html>');
61
+ const payload = decodeJwtPayload(token);
62
+ const email = payload?.email || '';
63
+ const emailHtml = email
64
+ ? `<p style="font-size:18px;margin:16px 0 4px">Signed in as <strong>${email.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</strong></p>`
65
+ : '';
66
+ res.end(`<html><body style="font-family:system-ui;text-align:center;padding:60px">` +
67
+ `<h2 style="color:#22c55e">\u2713 Logged in to SecureNow</h2>` +
68
+ emailHtml +
69
+ `<p style="color:#666">You can close this window and return to the terminal.</p>` +
70
+ `</body></html>`);
62
71
  server.close();
63
72
  resolve(token);
64
73
  return;
@@ -120,6 +129,8 @@ async function loginWithToken(token) {
120
129
  }
121
130
 
122
131
  async function login(args, flags) {
132
+ const local = !!flags.local;
133
+
123
134
  if (flags.token) {
124
135
  const token = flags.token;
125
136
  await loginWithToken(token);
@@ -127,9 +138,11 @@ async function login(args, flags) {
127
138
  const email = payload?.email || 'unknown';
128
139
  const exp = payload?.exp ? payload.exp * 1000 : null;
129
140
 
130
- config.setAuth(token, email, exp);
141
+ config.setAuth(token, email, exp, { local });
142
+ if (local) config.ensureLocalGitignore();
131
143
  console.log('');
132
144
  ui.success(`Logged in as ${ui.c.bold(email)}`);
145
+ if (local) ui.info('Credentials saved to project .securenow/ (local)');
133
146
  if (exp) {
134
147
  const days = Math.ceil((exp - Date.now()) / (1000 * 60 * 60 * 24));
135
148
  ui.info(`Session expires in ${days} days`);
@@ -143,9 +156,11 @@ async function login(args, flags) {
143
156
  const email = payload?.email || 'unknown';
144
157
  const exp = payload?.exp ? payload.exp * 1000 : null;
145
158
 
146
- config.setAuth(token, email, exp);
159
+ config.setAuth(token, email, exp, { local });
160
+ if (local) config.ensureLocalGitignore();
147
161
  console.log('');
148
162
  ui.success(`Logged in as ${ui.c.bold(email)}`);
163
+ if (local) ui.info('Credentials saved to project .securenow/ (local)');
149
164
  if (exp) {
150
165
  const days = Math.ceil((exp - Date.now()) / (1000 * 60 * 60 * 24));
151
166
  ui.info(`Session expires in ${days} days`);
@@ -165,14 +180,16 @@ async function login(args, flags) {
165
180
  }
166
181
  }
167
182
 
168
- async function logout() {
183
+ async function logout(args, flags) {
184
+ const local = flags ? flags.local : undefined;
169
185
  const creds = config.loadCredentials();
170
- config.clearCredentials();
186
+ config.clearCredentials({ local });
171
187
  if (creds.email) {
172
188
  ui.success(`Logged out from ${ui.c.bold(creds.email)}`);
173
189
  } else {
174
190
  ui.success('Logged out');
175
191
  }
192
+ if (local) ui.info('Cleared project-local credentials');
176
193
  }
177
194
 
178
195
  async function whoami() {
@@ -192,6 +209,7 @@ async function whoami() {
192
209
  ['Email', creds.email || payload?.email || 'unknown'],
193
210
  ['User ID', payload?.sub || 'unknown'],
194
211
  ['API', config.getApiUrl()],
212
+ ['Auth Source', config.getAuthSource()],
195
213
  ];
196
214
  if (creds.expiresAt) {
197
215
  const days = Math.ceil((creds.expiresAt - Date.now()) / (1000 * 60 * 60 * 24));
package/cli/config.js CHANGED
@@ -8,6 +8,10 @@ const CONFIG_DIR = path.join(os.homedir(), '.securenow');
8
8
  const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
9
9
  const CREDENTIALS_FILE = path.join(CONFIG_DIR, 'credentials.json');
10
10
 
11
+ const LOCAL_CONFIG_DIR = path.join(process.cwd(), '.securenow');
12
+ const LOCAL_CONFIG_FILE = path.join(LOCAL_CONFIG_DIR, 'config.json');
13
+ const LOCAL_CREDENTIALS_FILE = path.join(LOCAL_CONFIG_DIR, 'credentials.json');
14
+
11
15
  const DEFAULTS = {
12
16
  apiUrl: 'https://api.securenow.ai',
13
17
  appUrl: 'https://app.securenow.ai',
@@ -15,9 +19,9 @@ const DEFAULTS = {
15
19
  output: 'table',
16
20
  };
17
21
 
18
- function ensureDir() {
19
- if (!fs.existsSync(CONFIG_DIR)) {
20
- fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
22
+ function ensureDir(dir) {
23
+ if (!fs.existsSync(dir || CONFIG_DIR)) {
24
+ fs.mkdirSync(dir || CONFIG_DIR, { recursive: true, mode: 0o700 });
21
25
  }
22
26
  }
23
27
 
@@ -30,7 +34,7 @@ function loadJSON(filepath) {
30
34
  }
31
35
 
32
36
  function saveJSON(filepath, data) {
33
- ensureDir();
37
+ ensureDir(path.dirname(filepath));
34
38
  fs.writeFileSync(filepath, JSON.stringify(data, null, 2), { encoding: 'utf8', mode: 0o600 });
35
39
  if (process.platform === 'win32') {
36
40
  try {
@@ -40,8 +44,25 @@ function saveJSON(filepath, data) {
40
44
  }
41
45
  }
42
46
 
47
+ function hasLocalCredentials() {
48
+ return fs.existsSync(LOCAL_CREDENTIALS_FILE);
49
+ }
50
+
51
+ function resolveCredentialsFile() {
52
+ if (fs.existsSync(LOCAL_CREDENTIALS_FILE)) return LOCAL_CREDENTIALS_FILE;
53
+ return CREDENTIALS_FILE;
54
+ }
55
+
56
+ function getAuthSource() {
57
+ if (process.env.SECURENOW_TOKEN) return 'env (SECURENOW_TOKEN)';
58
+ if (fs.existsSync(LOCAL_CREDENTIALS_FILE)) return 'project (.securenow/)';
59
+ return 'global (~/.securenow/)';
60
+ }
61
+
43
62
  function loadConfig() {
44
- return { ...DEFAULTS, ...loadJSON(CONFIG_FILE) };
63
+ const global = loadJSON(CONFIG_FILE);
64
+ const local = fs.existsSync(LOCAL_CONFIG_FILE) ? loadJSON(LOCAL_CONFIG_FILE) : {};
65
+ return { ...DEFAULTS, ...global, ...local };
45
66
  }
46
67
 
47
68
  function saveConfig(config) {
@@ -59,20 +80,29 @@ function setConfigValue(key, value) {
59
80
  }
60
81
 
61
82
  function loadCredentials() {
62
- return loadJSON(CREDENTIALS_FILE);
83
+ return loadJSON(resolveCredentialsFile());
63
84
  }
64
85
 
65
- function saveCredentials(creds) {
66
- saveJSON(CREDENTIALS_FILE, creds);
86
+ function saveCredentials(creds, { local = false } = {}) {
87
+ const targetFile = local ? LOCAL_CREDENTIALS_FILE : CREDENTIALS_FILE;
88
+ saveJSON(targetFile, creds);
67
89
  }
68
90
 
69
- function clearCredentials() {
91
+ function clearCredentials({ local } = {}) {
70
92
  try {
71
- fs.unlinkSync(CREDENTIALS_FILE);
93
+ if (local === true) {
94
+ fs.unlinkSync(LOCAL_CREDENTIALS_FILE);
95
+ } else if (local === false || !hasLocalCredentials()) {
96
+ fs.unlinkSync(CREDENTIALS_FILE);
97
+ } else {
98
+ fs.unlinkSync(LOCAL_CREDENTIALS_FILE);
99
+ }
72
100
  } catch {}
73
101
  }
74
102
 
75
103
  function getToken() {
104
+ if (process.env.SECURENOW_TOKEN) return process.env.SECURENOW_TOKEN;
105
+
76
106
  const creds = loadCredentials();
77
107
  if (!creds.token) return null;
78
108
 
@@ -82,8 +112,23 @@ function getToken() {
82
112
  return creds.token;
83
113
  }
84
114
 
85
- function setAuth(token, email, expiresAt) {
86
- saveCredentials({ token, email, expiresAt });
115
+ function setAuth(token, email, expiresAt, { local = false } = {}) {
116
+ saveCredentials({ token, email, expiresAt }, { local });
117
+ }
118
+
119
+ function ensureLocalGitignore() {
120
+ const gitignorePath = path.join(process.cwd(), '.gitignore');
121
+ const entry = '.securenow/';
122
+ try {
123
+ if (fs.existsSync(gitignorePath)) {
124
+ const content = fs.readFileSync(gitignorePath, 'utf8');
125
+ if (!content.split('\n').some(line => line.trim() === entry)) {
126
+ fs.appendFileSync(gitignorePath, `\n# SecureNow local credentials\n${entry}\n`);
127
+ }
128
+ } else {
129
+ fs.writeFileSync(gitignorePath, `# SecureNow local credentials\n${entry}\n`);
130
+ }
131
+ } catch {}
87
132
  }
88
133
 
89
134
  function getApiUrl() {
@@ -102,6 +147,8 @@ module.exports = {
102
147
  CONFIG_DIR,
103
148
  CONFIG_FILE,
104
149
  CREDENTIALS_FILE,
150
+ LOCAL_CONFIG_DIR,
151
+ LOCAL_CREDENTIALS_FILE,
105
152
  loadConfig,
106
153
  saveConfig,
107
154
  getConfigValue,
@@ -111,6 +158,9 @@ module.exports = {
111
158
  clearCredentials,
112
159
  getToken,
113
160
  setAuth,
161
+ getAuthSource,
162
+ hasLocalCredentials,
163
+ ensureLocalGitignore,
114
164
  getApiUrl,
115
165
  getAppUrl,
116
166
  getDefaultApp,
package/cli.js CHANGED
@@ -47,14 +47,15 @@ const COMMANDS = {
47
47
  },
48
48
  login: {
49
49
  desc: 'Authenticate with SecureNow',
50
- usage: 'securenow login [--token <TOKEN>]',
51
- flags: { token: 'Authenticate with a token directly' },
50
+ usage: 'securenow login [--token <TOKEN>] [--local]',
51
+ flags: { token: 'Authenticate with a token directly', local: 'Save credentials to this project only (.securenow/)' },
52
52
  run: (a, f) => require('./cli/auth').login(a, f),
53
53
  },
54
54
  logout: {
55
55
  desc: 'Clear stored credentials',
56
- usage: 'securenow logout',
57
- run: () => require('./cli/auth').logout(),
56
+ usage: 'securenow logout [--local]',
57
+ flags: { local: 'Clear project-local credentials only' },
58
+ run: (a, f) => require('./cli/auth').logout(a, f),
58
59
  },
59
60
  whoami: {
60
61
  desc: 'Show current session info',
@@ -276,8 +277,12 @@ const COMMANDS = {
276
277
  desc: 'Show config file path',
277
278
  run: () => {
278
279
  const conf = require('./cli/config');
279
- console.log(`Config: ${conf.CONFIG_FILE}`);
280
- console.log(`Credentials: ${conf.CREDENTIALS_FILE}`);
280
+ console.log(`Config: ${conf.CONFIG_FILE}`);
281
+ console.log(`Credentials: ${conf.CREDENTIALS_FILE}`);
282
+ if (conf.hasLocalCredentials()) {
283
+ console.log(`Local creds: ${conf.LOCAL_CREDENTIALS_FILE} ${require('./cli/ui').c.green('(active)')}`);
284
+ }
285
+ console.log(`Auth source: ${conf.getAuthSource()}`);
281
286
  },
282
287
  },
283
288
  },
@@ -764,10 +764,14 @@ The SecureNow CLI is your terminal command center. Below is every command organi
764
764
 
765
765
  | Command | What It Does |
766
766
  |---------|-------------|
767
- | `securenow login` | Opens browser to authenticate |
767
+ | `securenow login` | Opens browser to authenticate (global session) |
768
768
  | `securenow login --token <T>` | Authenticate with a token (for CI/CD or headless servers) |
769
+ | `securenow login --local` | Save credentials to this project only (per-project session) |
769
770
  | `securenow logout` | Clear stored credentials |
770
- | `securenow whoami` | Show current session (email, API URL, expiry, default app) |
771
+ | `securenow logout --local` | Clear project-local credentials only |
772
+ | `securenow whoami` | Show current session (email, API URL, auth source, expiry, default app) |
773
+
774
+ **Per-project credentials:** Use `--local` to maintain separate logins for different projects on the same machine. The CLI resolves credentials in order: `SECURENOW_TOKEN` env var → project `.securenow/credentials.json` → global `~/.securenow/credentials.json`.
771
775
 
772
776
  ### App Management
773
777
 
@@ -1313,9 +1317,15 @@ Or re-authenticate with a token:
1313
1317
  npx securenow login --token <YOUR_TOKEN>
1314
1318
  ```
1315
1319
 
1320
+ Or set the env var directly:
1321
+
1322
+ ```bash
1323
+ SECURENOW_TOKEN=<YOUR_JWT> npx securenow whoami
1324
+ ```
1325
+
1316
1326
  ### CLI says "Session expired"
1317
1327
 
1318
- Tokens expire after a set period. Re-run `securenow login` to get a fresh session.
1328
+ Tokens expire after a set period. Re-run `securenow login` to get a fresh session. Use `securenow whoami` to check which credential source is active.
1319
1329
 
1320
1330
  ---
1321
1331
 
@@ -128,7 +128,7 @@ The firewall SDK reads this automatically on startup.
128
128
  ### In CI/CD
129
129
 
130
130
  ```yaml
131
- # GitHub Actions example
131
+ # GitHub Actions example — SDK firewall key
132
132
  env:
133
133
  SECURENOW_API_KEY: ${{ secrets.SECURENOW_API_KEY }}
134
134
 
@@ -139,6 +139,22 @@ steps:
139
139
  echo "$ISSUES" | jq '.issues | length'
140
140
  ```
141
141
 
142
+ ### CLI Authentication in CI/CD
143
+
144
+ For CLI commands in CI, use the `SECURENOW_TOKEN` env var to skip file-based login:
145
+
146
+ ```yaml
147
+ # GitHub Actions example — CLI auth via env var
148
+ env:
149
+ SECURENOW_TOKEN: ${{ secrets.SECURENOW_CLI_TOKEN }}
150
+
151
+ steps:
152
+ - run: npx securenow issues --json --status open
153
+ - run: npx securenow forensics "critical attacks in last 24h" --json
154
+ ```
155
+
156
+ The `SECURENOW_TOKEN` env var takes priority over any stored credentials.
157
+
142
158
  ---
143
159
 
144
160
  ## Key Management
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "securenow",
3
- "version": "5.15.2",
3
+ "version": "5.16.1",
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",