@lanonasis/cli 3.7.1 → 3.7.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/README.md +11 -0
- package/dist/commands/auth.js +79 -10
- package/dist/utils/api.js +14 -14
- package/dist/utils/config.d.ts +1 -0
- package/dist/utils/config.js +49 -15
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -34,6 +34,17 @@ onasis health # Verify system health
|
|
|
34
34
|
onasis memory create --title "Welcome" --content "My first memory"
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
## 🤖 Claude Desktop Integration
|
|
38
|
+
|
|
39
|
+
For instant Claude Desktop MCP integration with OAuth2 authentication, see our [Claude Desktop Setup Guide](./CLAUDE_DESKTOP_SETUP.md).
|
|
40
|
+
|
|
41
|
+
**Quick Links**:
|
|
42
|
+
- **Authorization Endpoint**: `https://auth.lanonasis.com/oauth/authorize`
|
|
43
|
+
- **Client ID**: `claude-desktop`
|
|
44
|
+
- **Scopes**: `mcp:full memories:read memories:write`
|
|
45
|
+
|
|
46
|
+
The guide includes complete OAuth2 configuration, available MCP tools, and troubleshooting steps.
|
|
47
|
+
|
|
37
48
|
## 🎯 Command Aliases
|
|
38
49
|
|
|
39
50
|
The CLI supports multiple command aliases for different use cases:
|
package/dist/commands/auth.js
CHANGED
|
@@ -305,6 +305,40 @@ async function refreshOAuth2Token(config) {
|
|
|
305
305
|
return false;
|
|
306
306
|
}
|
|
307
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* Exchange Supabase JWT token for auth-gateway API key
|
|
310
|
+
* This enables CLI to work with MCP WebSocket and all services seamlessly
|
|
311
|
+
*/
|
|
312
|
+
async function exchangeSupabaseTokenForApiKey(supabaseToken, config) {
|
|
313
|
+
try {
|
|
314
|
+
const discoveredServices = config.get('discoveredServices');
|
|
315
|
+
const authBase = discoveredServices?.auth_base || 'https://auth.lanonasis.com';
|
|
316
|
+
if (process.env.CLI_VERBOSE === 'true') {
|
|
317
|
+
console.log(chalk.dim(` Exchanging token at: ${authBase}/v1/auth/token/exchange`));
|
|
318
|
+
}
|
|
319
|
+
const response = await axios.post(`${authBase}/v1/auth/token/exchange`, {
|
|
320
|
+
project_scope: 'lanonasis-maas',
|
|
321
|
+
platform: 'cli'
|
|
322
|
+
}, {
|
|
323
|
+
headers: {
|
|
324
|
+
'Authorization': `Bearer ${supabaseToken}`,
|
|
325
|
+
'Content-Type': 'application/json',
|
|
326
|
+
'X-Project-Scope': 'lanonasis-maas'
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
return {
|
|
330
|
+
access_token: response.data.access_token,
|
|
331
|
+
user: response.data.user
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
console.error(chalk.yellow('⚠️ Token exchange failed:', error.message));
|
|
336
|
+
if (process.env.CLI_VERBOSE === 'true' && error.response) {
|
|
337
|
+
console.error(chalk.dim(' Response:', JSON.stringify(error.response.data, null, 2)));
|
|
338
|
+
}
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
308
342
|
export async function diagnoseCommand() {
|
|
309
343
|
const config = new CLIConfig();
|
|
310
344
|
await config.init();
|
|
@@ -674,14 +708,33 @@ async function handleOAuthFlow(config) {
|
|
|
674
708
|
}
|
|
675
709
|
const tokens = await exchangeCodeForTokens(code, pkce.verifier, authBase, redirectUri);
|
|
676
710
|
spinner.succeed('Access tokens received');
|
|
677
|
-
// Store tokens
|
|
711
|
+
// Store OAuth tokens
|
|
678
712
|
await config.setToken(tokens.access_token);
|
|
679
713
|
await config.set('refresh_token', tokens.refresh_token);
|
|
680
714
|
await config.set('token_expires_at', Date.now() + (tokens.expires_in * 1000));
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
715
|
+
// Exchange for unified API key
|
|
716
|
+
spinner.text = 'Configuring unified access...';
|
|
717
|
+
spinner.start();
|
|
718
|
+
const exchangeResult = await exchangeSupabaseTokenForApiKey(tokens.access_token, config);
|
|
719
|
+
if (exchangeResult) {
|
|
720
|
+
// Store the auth-gateway API key for MCP and other services
|
|
721
|
+
await config.setVendorKey(exchangeResult.access_token);
|
|
722
|
+
await config.set('authMethod', 'oauth2');
|
|
723
|
+
spinner.succeed('Unified authentication configured');
|
|
724
|
+
console.log();
|
|
725
|
+
console.log(chalk.green('✓ OAuth2 authentication successful'));
|
|
726
|
+
console.log(colors.info('You can now use all Lanonasis services'));
|
|
727
|
+
console.log(chalk.gray('✓ MCP, API, and CLI access configured'));
|
|
728
|
+
}
|
|
729
|
+
else {
|
|
730
|
+
// Fallback
|
|
731
|
+
await config.set('authMethod', 'oauth2');
|
|
732
|
+
spinner.warn('Token exchange failed, OAuth token stored');
|
|
733
|
+
console.log();
|
|
734
|
+
console.log(chalk.green('✓ OAuth2 authentication successful'));
|
|
735
|
+
console.log(colors.info('You can now use Lanonasis services'));
|
|
736
|
+
console.log(chalk.yellow('⚠️ Some services may require re-authentication'));
|
|
737
|
+
}
|
|
685
738
|
process.exit(0);
|
|
686
739
|
}
|
|
687
740
|
catch (error) {
|
|
@@ -766,16 +819,32 @@ async function handleCredentialsFlow(options, config) {
|
|
|
766
819
|
const spinner = ora('Authenticating...').start();
|
|
767
820
|
try {
|
|
768
821
|
const response = await apiClient.login(email, password);
|
|
769
|
-
|
|
770
|
-
|
|
822
|
+
if (process.env.CLI_VERBOSE === 'true') {
|
|
823
|
+
console.log(chalk.dim(' Login response:'), JSON.stringify(response, null, 2));
|
|
824
|
+
}
|
|
825
|
+
// The auth-gateway login endpoint already returns the correct token format
|
|
826
|
+
// No need to exchange - this token works with all services (MCP, API, CLI)
|
|
827
|
+
const authToken = response.token || response.access_token;
|
|
828
|
+
if (!authToken) {
|
|
829
|
+
throw new Error('No token received from login response');
|
|
830
|
+
}
|
|
831
|
+
if (process.env.CLI_VERBOSE === 'true') {
|
|
832
|
+
console.log(chalk.dim(` JWT received (length: ${authToken.length})`));
|
|
833
|
+
}
|
|
834
|
+
// Store JWT token for API authentication
|
|
835
|
+
await config.setToken(authToken);
|
|
836
|
+
await config.set('authMethod', 'jwt');
|
|
771
837
|
spinner.succeed('Login successful');
|
|
772
838
|
console.log();
|
|
773
839
|
console.log(chalk.green('✓ Authenticated successfully'));
|
|
774
840
|
console.log(`Welcome, ${response.user.email}!`);
|
|
775
|
-
if (response.user.
|
|
776
|
-
console.log(`
|
|
841
|
+
if (response.user.role) {
|
|
842
|
+
console.log(`Role: ${response.user.role}`);
|
|
777
843
|
}
|
|
778
|
-
console.log(
|
|
844
|
+
console.log(chalk.gray('✓ API access configured'));
|
|
845
|
+
console.log();
|
|
846
|
+
console.log(chalk.dim('Note: MCP WebSocket commands require a vendor key.'));
|
|
847
|
+
console.log(chalk.dim('Run'), chalk.white('onasis auth vendor-key <key>'), chalk.dim('to configure MCP access.'));
|
|
779
848
|
}
|
|
780
849
|
catch (error) {
|
|
781
850
|
spinner.fail('Login failed');
|
package/dist/utils/api.js
CHANGED
|
@@ -101,64 +101,64 @@ export class APIClient {
|
|
|
101
101
|
}
|
|
102
102
|
// Memory operations - aligned with existing schema
|
|
103
103
|
async createMemory(data) {
|
|
104
|
-
const response = await this.client.post('/
|
|
104
|
+
const response = await this.client.post('/memory', data);
|
|
105
105
|
return response.data;
|
|
106
106
|
}
|
|
107
107
|
async getMemories(params = {}) {
|
|
108
|
-
const response = await this.client.get('/
|
|
108
|
+
const response = await this.client.get('/memory', { params });
|
|
109
109
|
return response.data;
|
|
110
110
|
}
|
|
111
111
|
async getMemory(id) {
|
|
112
|
-
const response = await this.client.get(`/
|
|
112
|
+
const response = await this.client.get(`/memory/${id}`);
|
|
113
113
|
return response.data;
|
|
114
114
|
}
|
|
115
115
|
async updateMemory(id, data) {
|
|
116
|
-
const response = await this.client.put(`/
|
|
116
|
+
const response = await this.client.put(`/memory/${id}`, data);
|
|
117
117
|
return response.data;
|
|
118
118
|
}
|
|
119
119
|
async deleteMemory(id) {
|
|
120
|
-
await this.client.delete(`/
|
|
120
|
+
await this.client.delete(`/memory/${id}`);
|
|
121
121
|
}
|
|
122
122
|
async searchMemories(query, options = {}) {
|
|
123
|
-
const response = await this.client.post('/
|
|
123
|
+
const response = await this.client.post('/memory/search', {
|
|
124
124
|
query,
|
|
125
125
|
...options
|
|
126
126
|
});
|
|
127
127
|
return response.data;
|
|
128
128
|
}
|
|
129
129
|
async getMemoryStats() {
|
|
130
|
-
const response = await this.client.get('/
|
|
130
|
+
const response = await this.client.get('/memory/stats');
|
|
131
131
|
return response.data;
|
|
132
132
|
}
|
|
133
133
|
async bulkDeleteMemories(memoryIds) {
|
|
134
|
-
const response = await this.client.post('/
|
|
134
|
+
const response = await this.client.post('/memory/bulk/delete', {
|
|
135
135
|
memory_ids: memoryIds
|
|
136
136
|
});
|
|
137
137
|
return response.data;
|
|
138
138
|
}
|
|
139
139
|
// Topic operations - working with existing memory_topics table
|
|
140
140
|
async createTopic(data) {
|
|
141
|
-
const response = await this.client.post('/
|
|
141
|
+
const response = await this.client.post('/topics', data);
|
|
142
142
|
return response.data;
|
|
143
143
|
}
|
|
144
144
|
async getTopics() {
|
|
145
|
-
const response = await this.client.get('/
|
|
145
|
+
const response = await this.client.get('/topics');
|
|
146
146
|
return response.data;
|
|
147
147
|
}
|
|
148
148
|
async getTopic(id) {
|
|
149
|
-
const response = await this.client.get(`/
|
|
149
|
+
const response = await this.client.get(`/topics/${id}`);
|
|
150
150
|
return response.data;
|
|
151
151
|
}
|
|
152
152
|
async updateTopic(id, data) {
|
|
153
|
-
const response = await this.client.put(`/
|
|
153
|
+
const response = await this.client.put(`/topics/${id}`, data);
|
|
154
154
|
return response.data;
|
|
155
155
|
}
|
|
156
156
|
async deleteTopic(id) {
|
|
157
|
-
await this.client.delete(`/
|
|
157
|
+
await this.client.delete(`/topics/${id}`);
|
|
158
158
|
}
|
|
159
159
|
// Health check
|
|
160
160
|
async getHealth() {
|
|
161
|
-
const response = await this.client.get('/
|
|
161
|
+
const response = await this.client.get('/health');
|
|
162
162
|
return response.data;
|
|
163
163
|
}
|
|
164
164
|
// Generic HTTP methods
|
package/dist/utils/config.d.ts
CHANGED
|
@@ -61,6 +61,7 @@ export declare class CLIConfig {
|
|
|
61
61
|
private acquireLock;
|
|
62
62
|
private releaseLock;
|
|
63
63
|
getApiUrl(): string;
|
|
64
|
+
getApiUrlsWithFallbacks(): string[];
|
|
64
65
|
discoverServices(verbose?: boolean): Promise<void>;
|
|
65
66
|
private handleServiceDiscoveryFailure;
|
|
66
67
|
private categorizeServiceDiscoveryError;
|
package/dist/utils/config.js
CHANGED
|
@@ -169,7 +169,17 @@ export class CLIConfig {
|
|
|
169
169
|
getApiUrl() {
|
|
170
170
|
return process.env.MEMORY_API_URL ||
|
|
171
171
|
this.config.apiUrl ||
|
|
172
|
-
'https://
|
|
172
|
+
'https://api.lanonasis.com';
|
|
173
|
+
}
|
|
174
|
+
// Get API URLs with fallbacks - try multiple endpoints
|
|
175
|
+
getApiUrlsWithFallbacks() {
|
|
176
|
+
const primary = this.getApiUrl();
|
|
177
|
+
const fallbacks = [
|
|
178
|
+
'https://api.lanonasis.com',
|
|
179
|
+
'https://mcp.lanonasis.com'
|
|
180
|
+
];
|
|
181
|
+
// Remove duplicates and return primary first
|
|
182
|
+
return [primary, ...fallbacks.filter(url => url !== primary)];
|
|
173
183
|
}
|
|
174
184
|
// Enhanced Service Discovery Integration
|
|
175
185
|
async discoverServices(verbose = false) {
|
|
@@ -187,21 +197,45 @@ export class CLIConfig {
|
|
|
187
197
|
}
|
|
188
198
|
return;
|
|
189
199
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
200
|
+
// Try multiple discovery URLs with fallbacks
|
|
201
|
+
const discoveryUrls = [
|
|
202
|
+
'https://api.lanonasis.com/.well-known/onasis.json',
|
|
203
|
+
'https://mcp.lanonasis.com/.well-known/onasis.json'
|
|
204
|
+
];
|
|
205
|
+
let response = null;
|
|
206
|
+
let lastError = null;
|
|
207
|
+
// Use axios instead of fetch for consistency
|
|
208
|
+
const axios = (await import('axios')).default;
|
|
209
|
+
for (const discoveryUrl of discoveryUrls) {
|
|
210
|
+
try {
|
|
211
|
+
if (verbose) {
|
|
212
|
+
console.log(`🔍 Discovering services from ${discoveryUrl}...`);
|
|
203
213
|
}
|
|
204
|
-
|
|
214
|
+
response = await axios.get(discoveryUrl, {
|
|
215
|
+
timeout: 10000,
|
|
216
|
+
maxRedirects: 5,
|
|
217
|
+
proxy: false, // Bypass proxy to avoid redirect loops
|
|
218
|
+
headers: {
|
|
219
|
+
'User-Agent': 'Lanonasis-CLI/3.0.13'
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
if (verbose) {
|
|
223
|
+
console.log(`✓ Successfully discovered services from ${discoveryUrl}`);
|
|
224
|
+
}
|
|
225
|
+
break; // Success, exit loop
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
lastError = err;
|
|
229
|
+
if (verbose) {
|
|
230
|
+
console.log(`⚠️ Failed to discover from ${discoveryUrl}, trying next...`);
|
|
231
|
+
}
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (!response) {
|
|
236
|
+
throw lastError || new Error('All service discovery URLs failed');
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
205
239
|
// Map discovery response to our config format
|
|
206
240
|
const discovered = response.data;
|
|
207
241
|
// Extract auth base, but filter out localhost URLs
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lanonasis/cli",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"LICENSE"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@lanonasis/oauth-client": "^1.
|
|
25
|
+
"@lanonasis/oauth-client": "^1.2.1",
|
|
26
26
|
"@lanonasis/security-sdk": "^1.0.1",
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.1.1",
|
|
28
28
|
"axios": "^1.7.7",
|