@lanonasis/cli 3.9.2 → 3.9.4
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 +29 -0
- package/README.md +35 -10
- package/dist/index.js +1 -0
- package/dist/utils/api.js +29 -5
- package/dist/utils/mcp-client.js +16 -8
- package/dist/ux/implementations/TextInputHandlerImpl.js +100 -26
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog - @lanonasis/cli
|
|
2
2
|
|
|
3
|
+
## [3.9.3] - 2026-02-02
|
|
4
|
+
|
|
5
|
+
### ✨ Features
|
|
6
|
+
|
|
7
|
+
- **Non-Interactive Vendor Key Auth**: Added `-k, --vendor-key <key>` option to `auth login` command
|
|
8
|
+
- Enables non-interactive authentication in CI/CD pipelines and automation scripts
|
|
9
|
+
- Example: `onasis auth login --vendor-key <your-key>`
|
|
10
|
+
|
|
11
|
+
### 🐛 Bug Fixes
|
|
12
|
+
|
|
13
|
+
- **JWT Authentication Routing**: Fixed API routing for JWT/OAuth authenticated sessions
|
|
14
|
+
- JWT tokens from username/password or OAuth login now correctly route to MCP server
|
|
15
|
+
- Memory operations (list, create, search, update, delete) work with JWT authentication
|
|
16
|
+
- Path translation handles endpoint differences between API and MCP servers
|
|
17
|
+
- Vendor key authentication continues to route to main API server
|
|
18
|
+
|
|
19
|
+
- **Frozen Terminal During Text Input**: Fixed SSE/WebSocket event handlers interfering with inline text editor
|
|
20
|
+
- Real-time update messages (📡) now only display in verbose mode
|
|
21
|
+
- Prevents terminal freeze during interactive prompts (memory create, update)
|
|
22
|
+
- Raw terminal mode no longer conflicts with background MCP events
|
|
23
|
+
|
|
24
|
+
- **Missing CLI Option**: The `--vendor-key` option was defined in code but not exposed in CLI
|
|
25
|
+
- Now properly registered in command-line interface
|
|
26
|
+
|
|
27
|
+
### ⚠️ Known Limitations
|
|
28
|
+
|
|
29
|
+
- `memory stats` command not available with JWT authentication (MCP server limitation)
|
|
30
|
+
- For full API access including stats, use vendor key authentication
|
|
31
|
+
|
|
3
32
|
## [3.9.2] - 2026-02-02
|
|
4
33
|
|
|
5
34
|
### 🐛 Bug Fixes
|
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# @lanonasis/cli v3.9.
|
|
1
|
+
# @lanonasis/cli v3.9.3 - Enterprise Security & Professional UX
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@lanonasis/cli)
|
|
4
4
|
[](https://www.npmjs.com/package/@lanonasis/cli)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
[](https://api.lanonasis.com/.well-known/onasis.json)
|
|
7
7
|
|
|
8
|
-
🎉 **NEW IN v3.9**:
|
|
8
|
+
🎉 **NEW IN v3.9.3**: Fixed JWT authentication routing for username/password login, resolved frozen terminal during interactive input, and added non-interactive vendor key authentication (`-k` flag). Professional CLI UX with seamless inline text editing, intelligent MCP connection management, and first-run onboarding.
|
|
9
9
|
|
|
10
10
|
## 🚀 Quick Start
|
|
11
11
|
|
|
@@ -151,7 +151,11 @@ onasis login --vendor-key pk_xxxxx.sk_xxxxx // ✅ Automatically hashed
|
|
|
151
151
|
Best for API integrations and automation. Copy the vendor key value exactly as shown in your LanOnasis dashboard (keys may vary in format):
|
|
152
152
|
|
|
153
153
|
```bash
|
|
154
|
-
|
|
154
|
+
# Full option
|
|
155
|
+
onasis auth login --vendor-key <your-vendor-key>
|
|
156
|
+
|
|
157
|
+
# Short form (for scripts and CI/CD)
|
|
158
|
+
onasis auth login -k <your-vendor-key>
|
|
155
159
|
```
|
|
156
160
|
|
|
157
161
|
### 2. OAuth Browser Authentication
|
|
@@ -179,6 +183,13 @@ onasis auth status # Check current authentication
|
|
|
179
183
|
onasis auth logout # Logout from current session
|
|
180
184
|
```
|
|
181
185
|
|
|
186
|
+
**Auth Login Options:**
|
|
187
|
+
| Short | Long | Description |
|
|
188
|
+
|-------|------|-------------|
|
|
189
|
+
| `-k` | `--vendor-key <key>` | Authenticate with vendor key (non-interactive) |
|
|
190
|
+
| `-e` | `--email <email>` | Email for credentials login |
|
|
191
|
+
| `-p` | `--password <pass>` | Password for credentials login |
|
|
192
|
+
|
|
182
193
|
## 💻 Shell Completions
|
|
183
194
|
|
|
184
195
|
### Installation Guide
|
|
@@ -224,24 +235,38 @@ onasis quickstart # Essential commands reference
|
|
|
224
235
|
|
|
225
236
|
```bash
|
|
226
237
|
# List memories
|
|
227
|
-
onasis memory list
|
|
228
|
-
onasis memory list --
|
|
238
|
+
onasis memory list # or: onasis memory ls
|
|
239
|
+
onasis memory list --type context --limit 20
|
|
240
|
+
|
|
241
|
+
# Create memories (non-interactive)
|
|
242
|
+
onasis memory create -t "Project Notes" -c "Important information"
|
|
243
|
+
onasis memory create -t "Reference" --type reference --tags "docs,api"
|
|
229
244
|
|
|
230
|
-
# Create memories
|
|
231
|
-
onasis memory create
|
|
232
|
-
onasis memory create
|
|
245
|
+
# Create memories (interactive)
|
|
246
|
+
onasis memory create -i # Interactive mode with inline editor
|
|
247
|
+
onasis memory create # Prompts for missing fields
|
|
233
248
|
|
|
234
249
|
# Search memories
|
|
235
250
|
onasis memory search "api integration"
|
|
236
|
-
onasis memory search "meeting notes" --
|
|
251
|
+
onasis memory search "meeting notes" --type context
|
|
237
252
|
|
|
238
253
|
# Memory operations
|
|
239
254
|
onasis memory get <id> # Get specific memory
|
|
240
|
-
onasis memory update <id>
|
|
255
|
+
onasis memory update <id> -t "New Title" # Update title
|
|
256
|
+
onasis memory update <id> -i # Interactive update
|
|
241
257
|
onasis memory delete <id> # Delete memory
|
|
242
258
|
onasis memory stats # Memory statistics
|
|
243
259
|
```
|
|
244
260
|
|
|
261
|
+
**Create/Update Options:**
|
|
262
|
+
| Short | Long | Description |
|
|
263
|
+
|-------|------|-------------|
|
|
264
|
+
| `-t` | `--title` | Memory title |
|
|
265
|
+
| `-c` | `--content` | Memory content |
|
|
266
|
+
| `-i` | `--interactive` | Interactive mode |
|
|
267
|
+
| | `--type` | Memory type (context, project, knowledge, etc.) |
|
|
268
|
+
| | `--tags` | Comma-separated tags |
|
|
269
|
+
|
|
245
270
|
### Topic Management
|
|
246
271
|
|
|
247
272
|
```bash
|
package/dist/index.js
CHANGED
|
@@ -221,6 +221,7 @@ authCmd
|
|
|
221
221
|
.description('Login to your MaaS account')
|
|
222
222
|
.option('-e, --email <email>', 'email address')
|
|
223
223
|
.option('-p, --password <password>', 'password')
|
|
224
|
+
.option('-k, --vendor-key <key>', 'vendor key for API access')
|
|
224
225
|
.action(loginCommand);
|
|
225
226
|
authCmd
|
|
226
227
|
.command('logout')
|
package/dist/utils/api.js
CHANGED
|
@@ -15,19 +15,43 @@ export class APIClient {
|
|
|
15
15
|
await this.config.init();
|
|
16
16
|
// Service Discovery
|
|
17
17
|
await this.config.discoverServices();
|
|
18
|
-
// Use appropriate base URL based on endpoint
|
|
18
|
+
// Use appropriate base URL based on endpoint and auth method
|
|
19
19
|
const isAuthEndpoint = config.url?.includes('/auth/') || config.url?.includes('/login') || config.url?.includes('/register') || config.url?.includes('/oauth/');
|
|
20
20
|
const discoveredServices = this.config.get('discoveredServices');
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
const authMethod = this.config.get('authMethod');
|
|
22
|
+
const vendorKey = await this.config.getVendorKeyAsync();
|
|
23
|
+
// Determine the correct API base URL:
|
|
24
|
+
// - Auth endpoints -> auth.lanonasis.com
|
|
25
|
+
// - JWT auth (no vendor key) -> mcp.lanonasis.com (supports JWT tokens)
|
|
26
|
+
// - Vendor key auth -> api.lanonasis.com (requires vendor key)
|
|
27
|
+
let apiBaseUrl;
|
|
28
|
+
const useMcpServer = !vendorKey && (authMethod === 'jwt' || authMethod === 'oauth' || authMethod === 'oauth2');
|
|
29
|
+
if (isAuthEndpoint) {
|
|
30
|
+
apiBaseUrl = discoveredServices?.auth_base || 'https://auth.lanonasis.com';
|
|
31
|
+
}
|
|
32
|
+
else if (vendorKey) {
|
|
33
|
+
// Vendor key works with api.lanonasis.com
|
|
34
|
+
apiBaseUrl = this.config.getApiUrl();
|
|
35
|
+
}
|
|
36
|
+
else if (useMcpServer) {
|
|
37
|
+
// JWT/OAuth tokens work with mcp.lanonasis.com
|
|
38
|
+
apiBaseUrl = 'https://mcp.lanonasis.com/api/v1';
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
apiBaseUrl = this.config.getApiUrl();
|
|
42
|
+
}
|
|
43
|
+
config.baseURL = apiBaseUrl;
|
|
44
|
+
// Path translation for MCP server:
|
|
45
|
+
// MCP uses /memory (singular) while main API uses /memories (plural)
|
|
46
|
+
if (useMcpServer && config.url) {
|
|
47
|
+
config.url = config.url.replace(/\/api\/v1\/memories/g, '/memory');
|
|
48
|
+
}
|
|
24
49
|
// Add project scope header for auth endpoints
|
|
25
50
|
if (isAuthEndpoint) {
|
|
26
51
|
config.headers['X-Project-Scope'] = 'lanonasis-maas';
|
|
27
52
|
}
|
|
28
53
|
// Enhanced Authentication Support
|
|
29
54
|
const token = this.config.getToken();
|
|
30
|
-
const vendorKey = await this.config.getVendorKeyAsync();
|
|
31
55
|
if (vendorKey) {
|
|
32
56
|
// Vendor key authentication (validated server-side)
|
|
33
57
|
// Send raw key - server handles hashing for comparison
|
package/dist/utils/mcp-client.js
CHANGED
|
@@ -469,7 +469,10 @@ export class MCPClient {
|
|
|
469
469
|
this.sseConnection.onmessage = (event) => {
|
|
470
470
|
try {
|
|
471
471
|
const data = JSON.parse(event.data);
|
|
472
|
-
|
|
472
|
+
// Only show SSE updates in verbose mode to avoid interfering with interactive prompts
|
|
473
|
+
if (process.env.CLI_VERBOSE === 'true') {
|
|
474
|
+
console.log(chalk.blue('📡 Real-time update:'), data.type);
|
|
475
|
+
}
|
|
473
476
|
}
|
|
474
477
|
catch {
|
|
475
478
|
// Ignore parse errors
|
|
@@ -526,15 +529,20 @@ export class MCPClient {
|
|
|
526
529
|
this.wsConnection.on('message', (data) => {
|
|
527
530
|
try {
|
|
528
531
|
const message = JSON.parse(data.toString());
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
532
|
+
// Only show WebSocket messages in verbose mode to avoid interfering with interactive prompts
|
|
533
|
+
if (process.env.CLI_VERBOSE === 'true') {
|
|
534
|
+
const messageId = message.id ?? 'event';
|
|
535
|
+
const messageType = message.method
|
|
536
|
+
|| (message.error ? 'error' : undefined)
|
|
537
|
+
|| (message.result ? 'result' : undefined)
|
|
538
|
+
|| 'response';
|
|
539
|
+
console.log(chalk.blue('📡 MCP message:'), messageId, messageType);
|
|
540
|
+
}
|
|
535
541
|
}
|
|
536
542
|
catch (error) {
|
|
537
|
-
|
|
543
|
+
if (process.env.CLI_VERBOSE === 'true') {
|
|
544
|
+
console.error('Failed to parse WebSocket message:', error);
|
|
545
|
+
}
|
|
538
546
|
}
|
|
539
547
|
});
|
|
540
548
|
this.wsConnection.on('error', (error) => {
|
|
@@ -46,46 +46,113 @@ export class TextInputHandlerImpl {
|
|
|
46
46
|
options: mergedOptions,
|
|
47
47
|
status: 'active',
|
|
48
48
|
};
|
|
49
|
-
return new Promise((resolve, reject) => {
|
|
49
|
+
return new Promise(async (resolve, reject) => {
|
|
50
50
|
let handleKeypress = null;
|
|
51
|
+
let sigintHandler = null;
|
|
51
52
|
const cleanup = () => {
|
|
52
53
|
if (handleKeypress) {
|
|
53
54
|
process.stdin.removeListener('data', handleKeypress);
|
|
55
|
+
handleKeypress = null;
|
|
56
|
+
}
|
|
57
|
+
if (sigintHandler) {
|
|
58
|
+
process.removeListener('SIGINT', sigintHandler);
|
|
59
|
+
sigintHandler = null;
|
|
54
60
|
}
|
|
55
61
|
this.disableRawMode();
|
|
62
|
+
// Restore terminal state
|
|
63
|
+
process.stdout.write('\x1b[?25h'); // Show cursor
|
|
56
64
|
};
|
|
65
|
+
// Set up completion handlers first
|
|
66
|
+
const complete = (result) => {
|
|
67
|
+
cleanup();
|
|
68
|
+
if (this.currentSession) {
|
|
69
|
+
this.currentSession.status = 'completed';
|
|
70
|
+
}
|
|
71
|
+
resolve(result);
|
|
72
|
+
};
|
|
73
|
+
const cancel = () => {
|
|
74
|
+
cleanup();
|
|
75
|
+
if (this.currentSession) {
|
|
76
|
+
this.currentSession.status = 'cancelled';
|
|
77
|
+
}
|
|
78
|
+
reject(new Error('Input cancelled by user'));
|
|
79
|
+
};
|
|
80
|
+
// Store handlers for special key processing
|
|
81
|
+
this._completeHandler = complete;
|
|
82
|
+
this._cancelHandler = cancel;
|
|
57
83
|
try {
|
|
84
|
+
// Check if we can use raw mode
|
|
85
|
+
if (!process.stdin.isTTY) {
|
|
86
|
+
// Fall back to simple readline for non-TTY environments
|
|
87
|
+
console.log('\nNote: Interactive text input not available. Using simple input mode.');
|
|
88
|
+
console.log('Enter your text (empty line to finish):');
|
|
89
|
+
const readline = require('readline');
|
|
90
|
+
const rl = readline.createInterface({
|
|
91
|
+
input: process.stdin,
|
|
92
|
+
output: process.stdout
|
|
93
|
+
});
|
|
94
|
+
const lines = [];
|
|
95
|
+
rl.on('line', (line) => {
|
|
96
|
+
if (line === '') {
|
|
97
|
+
rl.close();
|
|
98
|
+
complete(lines.join('\n'));
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
lines.push(line);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
rl.on('close', () => {
|
|
105
|
+
complete(lines.join('\n'));
|
|
106
|
+
});
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
// Add SIGINT handler as fallback for Ctrl+C
|
|
110
|
+
sigintHandler = () => {
|
|
111
|
+
cancel();
|
|
112
|
+
};
|
|
113
|
+
process.on('SIGINT', sigintHandler);
|
|
114
|
+
// Safety: Add a way to escape via triple-ESC (sends 3 escape chars quickly)
|
|
115
|
+
let escapeCount = 0;
|
|
116
|
+
let escapeTimer = null;
|
|
117
|
+
// Try to claim stdin from any previous handlers
|
|
118
|
+
process.stdin.removeAllListeners('data');
|
|
119
|
+
process.stdin.removeAllListeners('readable');
|
|
120
|
+
process.stdin.removeAllListeners('end');
|
|
58
121
|
this.enableRawMode();
|
|
122
|
+
// Verify raw mode is working
|
|
123
|
+
if (!this.isRawModeEnabled) {
|
|
124
|
+
console.log('\nWarning: Could not enable raw mode. Falling back to editor mode.');
|
|
125
|
+
cleanup();
|
|
126
|
+
const { content } = await (await import('inquirer')).default.prompt([
|
|
127
|
+
{ type: 'editor', name: 'content', message: prompt, default: mergedOptions.defaultContent }
|
|
128
|
+
]);
|
|
129
|
+
resolve(content);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
59
132
|
this.displayInputPrompt(this.getCurrentContent());
|
|
60
133
|
handleKeypress = (chunk) => {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
134
|
+
try {
|
|
135
|
+
const key = this.parseKeyEvent(chunk);
|
|
136
|
+
if (this.handleSpecialKeys(key)) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
// Handle regular character input
|
|
140
|
+
if (key.sequence && this.currentSession) {
|
|
141
|
+
// Filter out control characters that shouldn't be added as text
|
|
142
|
+
if (key.sequence.charCodeAt(0) >= 32 || key.sequence === '\t') {
|
|
143
|
+
this.addCharacterToInput(key.sequence);
|
|
144
|
+
this.displayInputPrompt(this.getCurrentContent());
|
|
145
|
+
}
|
|
146
|
+
}
|
|
69
147
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
cleanup();
|
|
74
|
-
if (this.currentSession) {
|
|
75
|
-
this.currentSession.status = 'completed';
|
|
148
|
+
catch (err) {
|
|
149
|
+
// Don't let errors in key handling crash the input
|
|
150
|
+
console.error('Key handling error:', err);
|
|
76
151
|
}
|
|
77
|
-
resolve(result);
|
|
78
152
|
};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
this.currentSession.status = 'cancelled';
|
|
83
|
-
}
|
|
84
|
-
reject(new Error('Input cancelled by user'));
|
|
85
|
-
};
|
|
86
|
-
// Store handlers for special key processing
|
|
87
|
-
this._completeHandler = complete;
|
|
88
|
-
this._cancelHandler = cancel;
|
|
153
|
+
// Ensure stdin is flowing before adding listener
|
|
154
|
+
// This is critical after inquirer prompts which may pause stdin
|
|
155
|
+
process.stdin.resume();
|
|
89
156
|
process.stdin.on('data', handleKeypress);
|
|
90
157
|
}
|
|
91
158
|
catch (error) {
|
|
@@ -101,8 +168,14 @@ export class TextInputHandlerImpl {
|
|
|
101
168
|
if (!this.isRawModeEnabled && process.stdin.isTTY) {
|
|
102
169
|
this.originalStdinMode = process.stdin.isRaw;
|
|
103
170
|
process.stdin.setRawMode(true);
|
|
171
|
+
process.stdin.resume(); // Ensure stdin is flowing to receive data events
|
|
104
172
|
this.isRawModeEnabled = true;
|
|
105
173
|
}
|
|
174
|
+
else if (!process.stdin.isTTY) {
|
|
175
|
+
// Non-TTY mode - can't use raw mode, fall back to line mode
|
|
176
|
+
console.error('Warning: Not a TTY, inline text input may not work correctly');
|
|
177
|
+
process.stdin.resume();
|
|
178
|
+
}
|
|
106
179
|
}
|
|
107
180
|
/**
|
|
108
181
|
* Disable raw mode and return to normal terminal behavior
|
|
@@ -112,6 +185,7 @@ export class TextInputHandlerImpl {
|
|
|
112
185
|
process.stdin.setRawMode(this.originalStdinMode || false);
|
|
113
186
|
this.isRawModeEnabled = false;
|
|
114
187
|
}
|
|
188
|
+
// Don't pause stdin here as other handlers may need it
|
|
115
189
|
}
|
|
116
190
|
/**
|
|
117
191
|
* Handle special keyboard events
|
package/package.json
CHANGED