@lanonasis/cli 3.9.6 → 3.9.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,39 @@
1
1
  # Changelog - @lanonasis/cli
2
2
 
3
+ ## [3.9.7] - 2026-02-21
4
+
5
+ ### ✨ New Features
6
+
7
+ - **`onasis whoami` command**: Display full authenticated user profile including email, name, role, OAuth provider, project scope, and last login time. Fetches live data from `GET /v1/auth/me`.
8
+ - **Live profile on `auth status`**: `onasis auth status` now fetches the real user profile from the auth gateway and displays email, role, and plan — no longer relies solely on cached local state.
9
+ - **Live memory API probe on `auth status`**: After local auth check passes, `auth status` issues a real memory list request to confirm end-to-end API access, reporting `✓ accessible` or `✖ rejected (401)` with actionable guidance.
10
+ - **Manual endpoint override warning**: `auth status` now warns when `manualEndpointOverrides` is active and shows the configured endpoint URLs.
11
+
12
+ ### 🐛 Bug Fixes
13
+
14
+ - **OAuth sessions no longer show "Not Authenticated"**: Fixed `auth status` incorrectly reporting unauthenticated for valid OAuth PKCE sessions — was checking `if (isAuth && user)` when `user` may be undefined for OAuth sessions.
15
+ - **`process.exit(1)` no longer kills status probe**: Added `noExit` flag to `APIClient` so callers like `auth status` can catch 401/403 from the memory probe without the interceptor terminating the process.
16
+ - **Stale auth cache cleared on 401**: When the memory API returns 401, the CLI now calls `invalidateAuthCache()` to clear the 5-minute in-memory cache and the persisted `lastValidated` timestamp, preventing the 24-hour grace bypass.
17
+ - **24-hour `lastValidated` skip removed**: Eliminated a security hole that bypassed server re-validation for 24 hours after any successful auth check.
18
+ - **7-day offline grace restricted to network errors**: The offline grace period no longer applies to explicit 401/403 auth rejections — only genuine network failures.
19
+ - **Bogus vendor key always passed auth check**: `pingAuthHealth()` was hitting the unauthenticated `/health` endpoint. Replaced with `probeVendorKeyAuth()` which calls `POST /api/v1/memories/search` — a real protected endpoint. Interprets 401/403 as auth rejection, any other response (400, 405, 5xx) as auth accepted with a backend concern.
20
+ - **`discoverServices()` overwrote manual overrides**: Fixed auto-discovery ignoring `manualEndpointOverrides`; discovery now short-circuits when manual overrides are active.
21
+ - **Stale JWT cleared on vendor key switch**: When `setVendorKey()` sets `authMethod: 'vendor_key'`, any existing JWT tokens are now removed from config to prevent auth-method confusion in the API client.
22
+ - **Zod v4 compatibility**: Fixed `z.record(z.any())` → `z.record(z.string(), z.any())` (5 instances in `tool-schemas.ts`) and `error.errors` → `error.issues` (2 instances in schema validator).
23
+ - **Inquirer v9 compatibility**: Fixed deprecated `type: 'list'` → `type: 'select'` prompt type in `welcome.ts`.
24
+
25
+ ### 📡 Auth Gateway Integration (coordinated release)
26
+
27
+ These CLI changes are paired with server-side fixes in the same release:
28
+ - **Auth Gateway `requireAuth`**: Added opaque OAuth PKCE token introspection path — OAuth CLI sessions can now access `GET /v1/auth/me` and other protected endpoints.
29
+ - **Central API Gateway (`onasis-gateway`)**: `validateJWTToken()` now falls back to `POST /verify-token` when the session endpoint returns 401, enabling OAuth token passthrough for all proxied services. Added `get-me` tool to the auth-gateway MCP adapter.
30
+
31
+ ### 📚 Documentation
32
+
33
+ - Updated README with `onasis whoami` command reference.
34
+ - Added `auth status` live probe behavior to authentication section.
35
+ - Added `--no-mcp` flag to memory command examples.
36
+
3
37
  ## [3.9.6] - 2026-02-21
4
38
 
5
39
  ### 🐛 Bug Fixes
package/README.md CHANGED
@@ -1,11 +1,11 @@
1
- # @lanonasis/cli v3.9.6 - Auth Routing & Memory Reliability
1
+ # @lanonasis/cli v3.9.7 - OAuth PKCE Auth & whoami
2
2
 
3
3
  [![NPM Version](https://img.shields.io/npm/v/@lanonasis/cli)](https://www.npmjs.com/package/@lanonasis/cli)
4
4
  [![Downloads](https://img.shields.io/npm/dt/@lanonasis/cli)](https://www.npmjs.com/package/@lanonasis/cli)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
  [![Golden Contract](https://img.shields.io/badge/Onasis--Core-v0.1%20Compliant-gold)](https://api.lanonasis.com/.well-known/onasis.json)
7
7
 
8
- 🎉 **NEW IN v3.9.6**: Fixed memory auth routing to the API gateway, added compatibility fallbacks for legacy memory endpoints, improved auth status verification, and stabilized OAuth/JWT refresh behavior during CLI memory operations.
8
+ 🎉 **NEW IN v3.9.7**: Full OAuth PKCE session support across CLI and API gateway. New `onasis whoami` command. `auth status` now shows live user profile and probes real memory API access. Seven auth verification fixes eliminate false-positive "Authenticated: Yes" reports.
9
9
 
10
10
  ## 🚀 Quick Start
11
11
 
@@ -172,13 +172,28 @@ Traditional username/password authentication:
172
172
  onasis login # Will prompt for email and password
173
173
  ```
174
174
 
175
- ### Authentication Status
175
+ ### Authentication Status & Profile
176
176
 
177
177
  ```bash
178
- onasis auth status # Check current authentication
178
+ onasis auth status # Check current authentication (probes live memory API access)
179
179
  onasis auth logout # Logout from current session
180
+ onasis whoami # Display full authenticated user profile
180
181
  ```
181
182
 
183
+ `auth status` now performs a live end-to-end check:
184
+ 1. Validates the local credential (vendor key probe hits a real protected endpoint, not `/health`)
185
+ 2. Fetches and displays your user profile from `GET /v1/auth/me`
186
+ 3. Issues a real memory list request to confirm API access is working
187
+ 4. Warns if manual endpoint overrides are active
188
+
189
+ `onasis whoami` displays:
190
+ - Email address and display name
191
+ - Role (admin, user, authenticated)
192
+ - Plan tier (free, pro, enterprise)
193
+ - OAuth provider (if applicable)
194
+ - Project scope
195
+ - Last login time
196
+
182
197
  **Auth Login Options:**
183
198
  | Short | Long | Description |
184
199
  |-------|------|-------------|
@@ -222,6 +237,7 @@ echo 'onasis --completion fish | source' >> ~/.config/fish/config.fish
222
237
  ```bash
223
238
  onasis health # Comprehensive system health check
224
239
  onasis status # Quick status overview
240
+ onasis whoami # Display authenticated user profile (email, role, plan, provider)
225
241
  onasis init # Initialize CLI configuration
226
242
  onasis guide # Interactive setup guide
227
243
  onasis quickstart # Essential commands reference
@@ -73,7 +73,7 @@ export class WelcomeExperience {
73
73
  const choices = isAuthenticated ? existingUserOptions : newUserOptions;
74
74
  const { choice } = await inquirer.prompt([
75
75
  {
76
- type: 'list',
76
+ type: 'select',
77
77
  name: 'choice',
78
78
  message: isAuthenticated ?
79
79
  'Welcome back! What would you like to do?' :
@@ -229,7 +229,7 @@ export class InteractiveSetup {
229
229
  console.log("Let's connect to your Onasis service\n");
230
230
  const { connectionType } = await inquirer.prompt([
231
231
  {
232
- type: 'list',
232
+ type: 'select',
233
233
  name: 'connectionType',
234
234
  message: 'Where is your Onasis service hosted?',
235
235
  choices: [
@@ -291,7 +291,7 @@ export class InteractiveSetup {
291
291
  };
292
292
  const { authMethod } = await inquirer.prompt([
293
293
  {
294
- type: 'list',
294
+ type: 'select',
295
295
  name: 'authMethod',
296
296
  message: 'Select authentication method:',
297
297
  choices: [
@@ -395,7 +395,7 @@ export class InteractiveSetup {
395
395
  console.log("Let's personalize your experience\n");
396
396
  const answers = await inquirer.prompt([
397
397
  {
398
- type: 'list',
398
+ type: 'select',
399
399
  name: 'outputFormat',
400
400
  message: 'Preferred output format:',
401
401
  choices: [
package/dist/index.js CHANGED
@@ -11,6 +11,7 @@ import { orgCommands } from './commands/organization.js';
11
11
  import { mcpCommands } from './commands/mcp.js';
12
12
  import apiKeysCommand from './commands/api-keys.js';
13
13
  import { CLIConfig } from './utils/config.js';
14
+ import { APIClient } from './utils/api.js';
14
15
  import { getMCPClient } from './utils/mcp-client.js';
15
16
  import { dirname, join } from 'path';
16
17
  import { createOnboardingFlow } from './ux/index.js';
@@ -253,29 +254,77 @@ authCmd
253
254
  .description('Show authentication status')
254
255
  .action(async () => {
255
256
  const isAuth = await cliConfig.isAuthenticated();
256
- const user = await cliConfig.getCurrentUser();
257
257
  const failureCount = cliConfig.getFailureCount();
258
258
  const lastFailure = cliConfig.getLastAuthFailure();
259
259
  const authMethod = cliConfig.get('authMethod');
260
260
  const lastValidated = cliConfig.get('lastValidated');
261
261
  console.log(chalk.blue.bold('🔐 Authentication Status'));
262
262
  console.log('━'.repeat(40));
263
- if (isAuth && user) {
263
+ if (isAuth) {
264
264
  console.log(chalk.green('✓ Authenticated'));
265
- console.log(`Email: ${user.email}`);
266
- console.log(`Organization: ${user.organization_id}`);
267
- console.log(`Plan: ${user.plan}`);
268
265
  if (authMethod) {
269
266
  console.log(`Method: ${authMethod}`);
270
267
  }
271
268
  if (lastValidated) {
272
- const validatedDate = new Date(lastValidated);
273
- console.log(`Last validated: ${validatedDate.toLocaleString()}`);
269
+ console.log(`Last validated: ${new Date(lastValidated).toLocaleString()}`);
270
+ }
271
+ // Fetch live profile from auth gateway
272
+ try {
273
+ const profileClient = new APIClient();
274
+ profileClient.noExit = true;
275
+ const profile = await profileClient.getUserProfile();
276
+ console.log(`Email: ${profile.email}`);
277
+ if (profile.name)
278
+ console.log(`Name: ${profile.name}`);
279
+ console.log(`Role: ${profile.role}`);
280
+ if (profile.provider)
281
+ console.log(`Provider: ${profile.provider}`);
282
+ if (profile.last_sign_in_at) {
283
+ console.log(`Last sign-in: ${new Date(profile.last_sign_in_at).toLocaleString()}`);
284
+ }
285
+ }
286
+ catch {
287
+ // Profile fetch failed (e.g. auth gateway offline) — show cached info if available
288
+ const cached = await cliConfig.getCurrentUser();
289
+ if (cached?.email)
290
+ console.log(`Email: ${cached.email} (cached)`);
274
291
  }
275
292
  }
276
293
  else {
277
294
  console.log(chalk.red('✖ Not authenticated'));
278
- console.log(chalk.yellow('Run:'), chalk.white('memory login'));
295
+ console.log(chalk.yellow('Run:'), chalk.white('lanonasis auth login'));
296
+ }
297
+ // Warn when manual endpoint overrides are active
298
+ if (cliConfig.hasManualEndpointOverrides()) {
299
+ console.log();
300
+ console.log(chalk.yellow('⚠️ Manual endpoint overrides are active (manualEndpointOverrides=true)'));
301
+ const services = cliConfig.get('discoveredServices');
302
+ if (services) {
303
+ console.log(chalk.gray(` auth: ${services['auth_base']}`));
304
+ console.log(chalk.gray(` memory: ${services['memory_base']}`));
305
+ }
306
+ console.log(chalk.gray(' Run: lanonasis config clear-overrides to restore auto-discovery'));
307
+ }
308
+ // Live memory API probe — shows whether credentials actually work end-to-end
309
+ if (isAuth) {
310
+ console.log();
311
+ process.stdout.write('Memory API access: ');
312
+ try {
313
+ const apiClient = new APIClient();
314
+ apiClient.noExit = true; // catch 401 in our try/catch below instead of process.exit
315
+ await apiClient.getMemories({ limit: 1 });
316
+ console.log(chalk.green('✓ accessible'));
317
+ }
318
+ catch (err) {
319
+ const status = err?.response?.status;
320
+ if (status === 401 || status === 403) {
321
+ console.log(chalk.red(`✖ rejected (${status}) — credentials are stale or revoked`));
322
+ console.log(chalk.yellow(' Run: lanonasis auth login'));
323
+ }
324
+ else {
325
+ console.log(chalk.yellow(`⚠ reachable (status: ${status ?? 'network error'})`));
326
+ }
327
+ }
279
328
  }
280
329
  // Show failure tracking information
281
330
  if (failureCount > 0) {
@@ -645,16 +694,72 @@ program
645
694
  console.log(`Verified via: ${verification.endpoint}`);
646
695
  }
647
696
  if (isAuth) {
648
- const user = await cliConfig.getCurrentUser();
649
- if (user) {
650
- console.log(`User: ${user.email}`);
651
- console.log(`Plan: ${user.plan}`);
697
+ try {
698
+ const profileClient = new APIClient();
699
+ profileClient.noExit = true;
700
+ const profile = await profileClient.getUserProfile();
701
+ console.log(`User: ${profile.email}`);
702
+ if (profile.name)
703
+ console.log(`Name: ${profile.name}`);
704
+ console.log(`Role: ${profile.role}`);
705
+ }
706
+ catch {
707
+ const cached = await cliConfig.getCurrentUser();
708
+ if (cached?.email)
709
+ console.log(`User: ${cached.email} (cached)`);
652
710
  }
653
711
  return;
654
712
  }
655
713
  console.log(chalk.yellow(`Auth check: ${verification.reason || 'Credential validation failed'}`));
656
714
  console.log(chalk.yellow('Please run:'), chalk.white('lanonasis auth login'));
657
715
  });
716
+ // Whoami command — live profile from auth gateway
717
+ program
718
+ .command('whoami')
719
+ .description('Show the currently authenticated user profile')
720
+ .action(async () => {
721
+ await cliConfig.init();
722
+ const isAuth = await cliConfig.isAuthenticated();
723
+ if (!isAuth) {
724
+ console.log(chalk.red('✖ Not authenticated'));
725
+ console.log(chalk.yellow('Run:'), chalk.white('lanonasis auth login'));
726
+ process.exit(1);
727
+ }
728
+ try {
729
+ const profileClient = new APIClient();
730
+ profileClient.noExit = true;
731
+ const profile = await profileClient.getUserProfile();
732
+ console.log(chalk.blue.bold('👤 Current User'));
733
+ console.log('━'.repeat(40));
734
+ console.log(`Email: ${chalk.white(profile.email)}`);
735
+ if (profile.name) {
736
+ console.log(`Name: ${chalk.white(profile.name)}`);
737
+ }
738
+ console.log(`Role: ${chalk.white(profile.role)}`);
739
+ if (profile.provider) {
740
+ console.log(`Provider: ${chalk.white(profile.provider)}`);
741
+ }
742
+ if (profile.project_scope) {
743
+ console.log(`Scope: ${chalk.white(profile.project_scope)}`);
744
+ }
745
+ if (profile.last_sign_in_at) {
746
+ console.log(`Last login: ${chalk.white(new Date(profile.last_sign_in_at).toLocaleString())}`);
747
+ }
748
+ if (profile.created_at) {
749
+ console.log(`Member since: ${chalk.white(new Date(profile.created_at).toLocaleString())}`);
750
+ }
751
+ }
752
+ catch (err) {
753
+ const status = err?.response?.status;
754
+ if (status === 401 || status === 403) {
755
+ console.log(chalk.red('✖ Session expired — please log in again'));
756
+ console.log(chalk.yellow('Run:'), chalk.white('lanonasis auth login'));
757
+ process.exit(1);
758
+ }
759
+ console.error(chalk.red('✖ Failed to fetch profile:'), err instanceof Error ? err.message : String(err));
760
+ process.exit(1);
761
+ }
762
+ });
658
763
  // Health command using the healthCheck function
659
764
  program
660
765
  .command('health')