@lanonasis/cli 1.5.0 → 1.5.2

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.
Files changed (37) hide show
  1. package/README.md +284 -586
  2. package/dist/commands/api-keys.d.ts +3 -0
  3. package/dist/commands/api-keys.js +812 -0
  4. package/dist/commands/auth.d.ts +2 -0
  5. package/dist/commands/auth.js +127 -138
  6. package/dist/commands/completion.d.ts +33 -0
  7. package/dist/commands/completion.js +378 -0
  8. package/dist/commands/guide.d.ts +19 -0
  9. package/dist/commands/guide.js +446 -0
  10. package/dist/commands/mcp.js +30 -37
  11. package/dist/commands/memory.js +53 -78
  12. package/dist/completions/bash-completion.sh +88 -0
  13. package/dist/completions/fish-completion.fish +132 -0
  14. package/dist/completions/zsh-completion.zsh +196 -0
  15. package/dist/index-simple.js +633 -183
  16. package/dist/index.js +327 -221
  17. package/dist/mcp-server.d.ts +38 -0
  18. package/dist/mcp-server.js +154 -0
  19. package/dist/utils/api.d.ts +12 -2
  20. package/dist/utils/api.js +38 -4
  21. package/dist/utils/config.d.ts +5 -2
  22. package/dist/utils/config.js +39 -15
  23. package/dist/utils/formatting.d.ts +2 -0
  24. package/dist/utils/formatting.js +13 -0
  25. package/dist/utils/mcp-client.d.ts +49 -6
  26. package/dist/utils/mcp-client.js +159 -82
  27. package/package.json +22 -12
  28. package/dist/utils/completions.d.ts +0 -28
  29. package/dist/utils/completions.js +0 -276
  30. package/dist/utils/mcp-client.test.d.ts +0 -1
  31. package/dist/utils/mcp-client.test.js +0 -125
  32. package/dist/utils/output.d.ts +0 -23
  33. package/dist/utils/output.js +0 -97
  34. package/dist/utils/websocket-mcp-client.d.ts +0 -60
  35. package/dist/utils/websocket-mcp-client.js +0 -182
  36. package/dist/utils/websocket-mcp-client.test.d.ts +0 -1
  37. package/dist/utils/websocket-mcp-client.test.js +0 -126
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI-Embedded MCP Server
4
+ * Uses the same configuration and authentication as @lanonasis/cli v1.5.2+
5
+ * Can run standalone or be invoked by CLI commands
6
+ */
7
+ interface MCPServerOptions {
8
+ mode?: 'stdio' | 'http';
9
+ port?: number;
10
+ verbose?: boolean;
11
+ useRemote?: boolean;
12
+ }
13
+ export declare class CLIMCPServer {
14
+ private config;
15
+ constructor();
16
+ /**
17
+ * Start MCP server using CLI configuration
18
+ */
19
+ start(options?: MCPServerOptions): Promise<void>;
20
+ /**
21
+ * Start local MCP server using CLI auth config
22
+ */
23
+ private startLocalMCP;
24
+ /**
25
+ * Connect to remote MCP server
26
+ */
27
+ private startRemoteMCP;
28
+ /**
29
+ * Check if MCP server is available and configured
30
+ */
31
+ checkStatus(): Promise<{
32
+ available: boolean;
33
+ configured: boolean;
34
+ authMethod: string;
35
+ mode: 'local' | 'remote' | 'auto';
36
+ }>;
37
+ }
38
+ export default CLIMCPServer;
@@ -0,0 +1,154 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI-Embedded MCP Server
4
+ * Uses the same configuration and authentication as @lanonasis/cli v1.5.2+
5
+ * Can run standalone or be invoked by CLI commands
6
+ */
7
+ import { fileURLToPath } from 'url';
8
+ import { dirname, join } from 'path';
9
+ import { spawn } from 'child_process';
10
+ import { CLIConfig } from './utils/config.js';
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = dirname(__filename);
13
+ export class CLIMCPServer {
14
+ config;
15
+ constructor() {
16
+ this.config = new CLIConfig();
17
+ }
18
+ /**
19
+ * Start MCP server using CLI configuration
20
+ */
21
+ async start(options = {}) {
22
+ await this.config.init();
23
+ const { mode = 'stdio', port = 3001, verbose = false, useRemote = this.config.shouldUseRemoteMCP() } = options;
24
+ if (useRemote) {
25
+ await this.startRemoteMCP(options);
26
+ }
27
+ else {
28
+ await this.startLocalMCP(options);
29
+ }
30
+ }
31
+ /**
32
+ * Start local MCP server using CLI auth config
33
+ */
34
+ async startLocalMCP(options) {
35
+ const { mode, port, verbose } = options;
36
+ // Path to CLI-aligned MCP server in submodule
37
+ const mcpServerPath = join(__dirname, '../../../mcp-server/dist/cli-aligned-mcp-server.js');
38
+ const args = mode === 'http' ? ['--http'] : ['--stdio'];
39
+ if (verbose) {
40
+ console.error('🚀 Starting CLI-aligned MCP Server...');
41
+ console.error(`Mode: ${mode}`);
42
+ console.error(`Config: ~/.maas/config.json`);
43
+ console.error(`Auth: ${this.config.hasVendorKey() ? 'Vendor Key' : 'JWT Token'}`);
44
+ }
45
+ // Set environment variables from CLI config
46
+ const env = {
47
+ ...process.env,
48
+ PORT: port?.toString(),
49
+ MEMORY_API_URL: this.config.getApiUrl(),
50
+ LANONASIS_VENDOR_KEY: this.config.getVendorKey(),
51
+ LANONASIS_TOKEN: this.config.getToken(),
52
+ MCP_VERBOSE: verbose ? 'true' : 'false',
53
+ CLI_ALIGNED: 'true'
54
+ };
55
+ const child = spawn('node', [mcpServerPath, ...args], {
56
+ env,
57
+ stdio: mode === 'stdio' ? ['pipe', 'pipe', 'inherit'] : 'inherit'
58
+ });
59
+ if (mode === 'stdio') {
60
+ // For stdio mode, pipe stdin/stdout for MCP protocol
61
+ process.stdin.pipe(child.stdin);
62
+ child.stdout.pipe(process.stdout);
63
+ }
64
+ child.on('error', (error) => {
65
+ console.error('❌ MCP Server failed to start:', error.message);
66
+ process.exit(1);
67
+ });
68
+ child.on('exit', (code) => {
69
+ if (code !== 0) {
70
+ console.error(`❌ MCP Server exited with code ${code}`);
71
+ process.exit(code || 1);
72
+ }
73
+ });
74
+ // Handle graceful shutdown
75
+ process.on('SIGINT', () => {
76
+ child.kill('SIGINT');
77
+ });
78
+ process.on('SIGTERM', () => {
79
+ child.kill('SIGTERM');
80
+ });
81
+ }
82
+ /**
83
+ * Connect to remote MCP server
84
+ */
85
+ async startRemoteMCP(options) {
86
+ const { verbose } = options;
87
+ if (verbose) {
88
+ console.error('🌐 Connecting to remote MCP server...');
89
+ console.error(`URL: ${this.config.getMCPServerUrl()}`);
90
+ }
91
+ // For remote MCP, we'd need to implement a proxy or client
92
+ // For now, fall back to local mode
93
+ console.error('⚠️ Remote MCP not yet implemented, falling back to local mode');
94
+ await this.startLocalMCP({ ...options, useRemote: false });
95
+ }
96
+ /**
97
+ * Check if MCP server is available and configured
98
+ */
99
+ async checkStatus() {
100
+ await this.config.init();
101
+ return {
102
+ available: true, // CLI always has MCP server available
103
+ configured: this.config.hasVendorKey() || !!this.config.getToken(),
104
+ authMethod: this.config.hasVendorKey() ? 'vendor_key' :
105
+ this.config.getToken() ? 'jwt' : 'none',
106
+ mode: this.config.shouldUseRemoteMCP() ? 'remote' : 'local'
107
+ };
108
+ }
109
+ }
110
+ // Main execution when run as standalone script
111
+ async function main() {
112
+ const args = process.argv.slice(2);
113
+ const server = new CLIMCPServer();
114
+ // Parse command line arguments
115
+ const options = {
116
+ mode: args.includes('--http') ? 'http' : 'stdio',
117
+ port: parseInt(args.find(arg => arg.startsWith('--port='))?.split('=')[1] || '3001'),
118
+ verbose: args.includes('--verbose') || args.includes('-v'),
119
+ useRemote: args.includes('--remote')
120
+ };
121
+ if (args.includes('--status')) {
122
+ const status = await server.checkStatus();
123
+ console.log(JSON.stringify(status, null, 2));
124
+ return;
125
+ }
126
+ if (args.includes('--help')) {
127
+ console.log(`
128
+ CLI MCP Server - CLI-aligned Model Context Protocol server
129
+
130
+ Usage:
131
+ lanonasis-mcp-server [options]
132
+
133
+ Options:
134
+ --stdio Use stdio transport (default)
135
+ --http Use HTTP transport
136
+ --port=3001 HTTP port (default: 3001)
137
+ --remote Use remote MCP server
138
+ --verbose, -v Verbose logging
139
+ --status Check server status
140
+ --help Show this help
141
+
142
+ Examples:
143
+ lanonasis-mcp-server # Start stdio server
144
+ lanonasis-mcp-server --http --port=3002 # Start HTTP server
145
+ lanonasis-mcp-server --status # Check status
146
+ `);
147
+ return;
148
+ }
149
+ await server.start(options);
150
+ }
151
+ if (import.meta.url === `file://${process.argv[1]}`) {
152
+ main().catch(console.error);
153
+ }
154
+ export default CLIMCPServer;
@@ -57,7 +57,7 @@ export interface GetMemoriesParams {
57
57
  limit?: number;
58
58
  offset?: number;
59
59
  memory_type?: MemoryType;
60
- tags?: string[];
60
+ tags?: string[] | string;
61
61
  topic_id?: string;
62
62
  sort_by?: 'created_at' | 'updated_at' | 'last_accessed' | 'access_count';
63
63
  sort_order?: 'asc' | 'desc';
@@ -125,13 +125,19 @@ export interface HealthStatus {
125
125
  }>;
126
126
  }
127
127
  export interface PaginatedResponse<T> {
128
- data: T[];
128
+ data?: T[];
129
+ memories?: T[];
130
+ results?: T[];
129
131
  pagination: {
130
132
  total: number;
131
133
  limit: number;
132
134
  offset: number;
133
135
  has_more: boolean;
136
+ page?: number;
137
+ pages?: number;
134
138
  };
139
+ total_results?: number;
140
+ search_time_ms?: number;
135
141
  }
136
142
  export interface ApiErrorResponse {
137
143
  error: string;
@@ -159,6 +165,10 @@ export declare class APIClient {
159
165
  updateTopic(id: string, data: UpdateTopicRequest): Promise<MemoryTopic>;
160
166
  deleteTopic(id: string): Promise<void>;
161
167
  getHealth(): Promise<HealthStatus>;
168
+ get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
169
+ post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
170
+ put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T>;
171
+ delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<T>;
162
172
  request<T = Record<string, unknown>>(config: AxiosRequestConfig): Promise<T>;
163
173
  }
164
174
  export declare const apiClient: APIClient;
package/dist/utils/api.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import axios from 'axios';
2
2
  import chalk from 'chalk';
3
+ import { randomUUID } from 'crypto';
3
4
  import { CLIConfig } from './config.js';
4
5
  export class APIClient {
5
6
  client;
@@ -7,16 +8,32 @@ export class APIClient {
7
8
  constructor() {
8
9
  this.config = new CLIConfig();
9
10
  this.client = axios.create();
10
- // Setup request interceptor to add auth token
11
+ // Setup request interceptor to add auth token and headers
11
12
  this.client.interceptors.request.use(async (config) => {
12
13
  await this.config.init();
14
+ // Service Discovery
15
+ await this.config.discoverServices();
16
+ config.baseURL = this.config.getDiscoveredApiUrl();
17
+ // Enhanced Authentication Support
13
18
  const token = this.config.getToken();
14
- if (token) {
19
+ const vendorKey = this.config.getVendorKey();
20
+ if (vendorKey) {
21
+ // Vendor key authentication (pk_*.sk_* format)
22
+ config.headers['X-API-Key'] = vendorKey;
23
+ config.headers['X-Auth-Method'] = 'vendor_key';
24
+ }
25
+ else if (token) {
26
+ // JWT token authentication
15
27
  config.headers.Authorization = `Bearer ${token}`;
28
+ config.headers['X-Auth-Method'] = 'jwt';
16
29
  }
17
- config.baseURL = this.config.getApiUrl();
30
+ // Add request ID for correlation
31
+ const requestId = randomUUID();
32
+ config.headers['X-Request-ID'] = requestId;
33
+ // Add project scope for Golden Contract compliance
34
+ config.headers['X-Project-Scope'] = 'lanonasis-maas';
18
35
  if (process.env.CLI_VERBOSE === 'true') {
19
- console.log(chalk.dim(`→ ${config.method?.toUpperCase()} ${config.url}`));
36
+ console.log(chalk.dim(`→ ${config.method?.toUpperCase()} ${config.url} [${requestId}]`));
20
37
  }
21
38
  return config;
22
39
  });
@@ -132,6 +149,23 @@ export class APIClient {
132
149
  const response = await this.client.get('/api/v1/health');
133
150
  return response.data;
134
151
  }
152
+ // Generic HTTP methods
153
+ async get(url, config) {
154
+ const response = await this.client.get(url, config);
155
+ return response.data;
156
+ }
157
+ async post(url, data, config) {
158
+ const response = await this.client.post(url, data, config);
159
+ return response.data;
160
+ }
161
+ async put(url, data, config) {
162
+ const response = await this.client.put(url, data, config);
163
+ return response.data;
164
+ }
165
+ async delete(url, config) {
166
+ const response = await this.client.delete(url, config);
167
+ return response.data;
168
+ }
135
169
  // Generic request method
136
170
  async request(config) {
137
171
  const response = await this.client.request(config);
@@ -13,11 +13,14 @@ export declare class CLIConfig {
13
13
  load(): Promise<void>;
14
14
  save(): Promise<void>;
15
15
  getApiUrl(): string;
16
+ discoverServices(): Promise<void>;
17
+ getDiscoveredApiUrl(): string;
18
+ setVendorKey(vendorKey: string): Promise<void>;
19
+ getVendorKey(): string | undefined;
20
+ hasVendorKey(): boolean;
16
21
  setApiUrl(url: string): Promise<void>;
17
22
  setToken(token: string): Promise<void>;
18
23
  getToken(): string | undefined;
19
- setApiKey(apiKey: string): Promise<void>;
20
- getApiKey(): string | undefined;
21
24
  getCurrentUser(): Promise<UserProfile | undefined>;
22
25
  isAuthenticated(): Promise<boolean>;
23
26
  logout(): Promise<void>;
@@ -39,7 +39,42 @@ export class CLIConfig {
39
39
  getApiUrl() {
40
40
  return process.env.MEMORY_API_URL ||
41
41
  this.config.apiUrl ||
42
- 'http://localhost:3000/api/v1';
42
+ 'https://api.lanonasis.com/api/v1';
43
+ }
44
+ // Service Discovery Integration
45
+ async discoverServices() {
46
+ try {
47
+ // Use axios instead of fetch for consistency
48
+ const axios = (await import('axios')).default;
49
+ const discoveryUrl = 'https://api.lanonasis.com/.well-known/onasis.json';
50
+ const response = await axios.get(discoveryUrl);
51
+ this.config.discoveredServices = response.data;
52
+ await this.save();
53
+ }
54
+ catch (error) {
55
+ // Service discovery failed, use defaults
56
+ if (process.env.CLI_VERBOSE === 'true') {
57
+ console.log('Service discovery failed, using defaults');
58
+ }
59
+ }
60
+ }
61
+ getDiscoveredApiUrl() {
62
+ return this.config.discoveredServices?.auth_base || this.getApiUrl();
63
+ }
64
+ // Enhanced authentication support
65
+ async setVendorKey(vendorKey) {
66
+ // Validate vendor key format (pk_*.sk_*)
67
+ if (!vendorKey.match(/^pk_[a-zA-Z0-9]+\.sk_[a-zA-Z0-9]+$/)) {
68
+ throw new Error('Invalid vendor key format. Expected: pk_xxx.sk_xxx');
69
+ }
70
+ this.config.vendorKey = vendorKey;
71
+ await this.save();
72
+ }
73
+ getVendorKey() {
74
+ return this.config.vendorKey;
75
+ }
76
+ hasVendorKey() {
77
+ return !!this.config.vendorKey;
43
78
  }
44
79
  async setApiUrl(url) {
45
80
  this.config.apiUrl = url;
@@ -67,22 +102,10 @@ export class CLIConfig {
67
102
  getToken() {
68
103
  return this.config.token;
69
104
  }
70
- async setApiKey(apiKey) {
71
- this.config.apiKey = apiKey;
72
- await this.save();
73
- }
74
- getApiKey() {
75
- return this.config.apiKey || process.env.LANONASIS_API_KEY;
76
- }
77
105
  async getCurrentUser() {
78
106
  return this.config.user;
79
107
  }
80
108
  async isAuthenticated() {
81
- // Check for API key first
82
- const apiKey = this.getApiKey();
83
- if (apiKey)
84
- return true;
85
- // Then check for valid JWT token
86
109
  const token = this.getToken();
87
110
  if (!token)
88
111
  return false;
@@ -97,7 +120,6 @@ export class CLIConfig {
97
120
  }
98
121
  async logout() {
99
122
  this.config.token = undefined;
100
- this.config.apiKey = undefined;
101
123
  this.config.user = undefined;
102
124
  await this.save();
103
125
  }
@@ -133,7 +155,9 @@ export class CLIConfig {
133
155
  return this.config.mcpServerPath || path.join(__dirname, '../../../../onasis-gateway/mcp-server/server.js');
134
156
  }
135
157
  getMCPServerUrl() {
136
- return this.config.mcpServerUrl || 'https://api.lanonasis.com';
158
+ return this.config.discoveredServices?.mcp_ws_base ||
159
+ this.config.mcpServerUrl ||
160
+ 'https://api.lanonasis.com';
137
161
  }
138
162
  shouldUseRemoteMCP() {
139
163
  const preference = this.config.mcpPreference || 'auto';
@@ -2,3 +2,5 @@ export declare function formatOutput(data: unknown, format?: string): void;
2
2
  export declare function formatBytes(bytes: number): string;
3
3
  export declare function truncateText(text: string, maxLength: number): string;
4
4
  export declare function formatDuration(ms: number): string;
5
+ export declare function formatDate(date: string | Date): string;
6
+ export declare function formatTableData(data: unknown[]): string[][];
@@ -32,3 +32,16 @@ export function formatDuration(ms) {
32
32
  return `${(ms / 1000).toFixed(1)}s`;
33
33
  return `${(ms / 60000).toFixed(1)}m`;
34
34
  }
35
+ export function formatDate(date) {
36
+ const d = new Date(date);
37
+ return d.toLocaleString();
38
+ }
39
+ export function formatTableData(data) {
40
+ return data.map(item => {
41
+ if (Array.isArray(item))
42
+ return item.map(String);
43
+ if (typeof item === 'object' && item !== null)
44
+ return Object.values(item).map(String);
45
+ return [String(item)];
46
+ });
47
+ }
@@ -2,27 +2,70 @@ interface MCPConnectionOptions {
2
2
  serverPath?: string;
3
3
  serverUrl?: string;
4
4
  useRemote?: boolean;
5
- mode?: 'local' | 'remote' | 'websocket';
5
+ useWebSocket?: boolean;
6
+ connectionMode?: 'local' | 'remote' | 'websocket';
7
+ }
8
+ /**
9
+ * Interface for MCP tool arguments
10
+ */
11
+ interface MCPToolArgs {
12
+ [key: string]: unknown;
13
+ }
14
+ /**
15
+ * Interface for MCP tool response
16
+ */
17
+ export interface MCPToolResponse {
18
+ result?: unknown;
19
+ error?: {
20
+ code: number;
21
+ message: string;
22
+ };
23
+ id?: string;
24
+ title?: string;
25
+ memory_type?: string;
26
+ length?: number;
27
+ forEach?: (callback: (item: any, index: number) => void) => void;
28
+ code?: number;
29
+ message?: string;
30
+ response?: any;
31
+ }
32
+ /**
33
+ * Interface for MCP WebSocket messages
34
+ */
35
+ export interface MCPWebSocketMessage {
36
+ id: number;
37
+ method?: string;
38
+ params?: Record<string, unknown>;
39
+ result?: Record<string, unknown>;
40
+ error?: {
41
+ code: number;
42
+ message: string;
43
+ data?: unknown;
44
+ };
6
45
  }
7
46
  export declare class MCPClient {
8
47
  private client;
9
48
  private config;
10
49
  private isConnected;
11
50
  private sseConnection;
12
- private wsClient;
51
+ private wsConnection;
13
52
  constructor();
14
53
  /**
15
54
  * Connect to MCP server (local or remote)
16
55
  */
17
56
  connect(options?: MCPConnectionOptions): Promise<boolean>;
18
57
  /**
19
- * Initialize WebSocket MCP connection for enterprise users
58
+ * Initialize SSE connection for real-time updates
59
+ */
60
+ private initializeSSE;
61
+ /**
62
+ * Initialize WebSocket connection for enterprise MCP server
20
63
  */
21
64
  private initializeWebSocket;
22
65
  /**
23
- * Initialize SSE connection for real-time updates
66
+ * Send a message over the WebSocket connection
24
67
  */
25
- private initializeSSE;
68
+ private sendWebSocketMessage;
26
69
  /**
27
70
  * Disconnect from MCP server
28
71
  */
@@ -30,7 +73,7 @@ export declare class MCPClient {
30
73
  /**
31
74
  * Call an MCP tool
32
75
  */
33
- callTool(toolName: string, args: any): Promise<any>;
76
+ callTool(toolName: string, args: MCPToolArgs): Promise<MCPToolResponse>;
34
77
  /**
35
78
  * Call remote tool via REST API with MCP interface
36
79
  */