@lanonasis/cli 1.5.0 ā 1.5.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/README.md +80 -357
- package/dist/commands/api-keys.d.ts +3 -0
- package/dist/commands/api-keys.js +812 -0
- package/dist/commands/auth.js +1 -151
- package/dist/commands/mcp.js +30 -37
- package/dist/commands/memory.js +53 -78
- package/dist/index-simple.js +522 -189
- package/dist/index.js +327 -221
- package/dist/mcp-server.d.ts +2 -0
- package/dist/mcp-server.js +519 -0
- package/dist/utils/api.d.ts +12 -2
- package/dist/utils/api.js +17 -0
- package/dist/utils/config.d.ts +0 -2
- package/dist/utils/config.js +2 -15
- package/dist/utils/formatting.d.ts +2 -0
- package/dist/utils/formatting.js +13 -0
- package/dist/utils/mcp-client.d.ts +49 -6
- package/dist/utils/mcp-client.js +161 -82
- package/package.json +17 -10
- package/dist/utils/completions.d.ts +0 -28
- package/dist/utils/completions.js +0 -276
- package/dist/utils/mcp-client.test.d.ts +0 -1
- package/dist/utils/mcp-client.test.js +0 -125
- package/dist/utils/output.d.ts +0 -23
- package/dist/utils/output.js +0 -97
- package/dist/utils/websocket-mcp-client.d.ts +0 -60
- package/dist/utils/websocket-mcp-client.js +0 -182
- package/dist/utils/websocket-mcp-client.test.d.ts +0 -1
- package/dist/utils/websocket-mcp-client.test.js +0 -126
package/dist/commands/auth.js
CHANGED
|
@@ -1,41 +1,13 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import inquirer from 'inquirer';
|
|
3
3
|
import ora from 'ora';
|
|
4
|
-
import { createServer } from 'http';
|
|
5
|
-
import { URL } from 'url';
|
|
6
|
-
import open from 'open';
|
|
7
4
|
import { apiClient } from '../utils/api.js';
|
|
8
5
|
import { CLIConfig } from '../utils/config.js';
|
|
9
6
|
export async function loginCommand(options) {
|
|
10
7
|
const config = new CLIConfig();
|
|
11
8
|
await config.init();
|
|
12
|
-
console.log(chalk.blue.bold('š Login to
|
|
9
|
+
console.log(chalk.blue.bold('š Login to MaaS (Supabase Auth)'));
|
|
13
10
|
console.log();
|
|
14
|
-
// Check if API key is provided via environment or option
|
|
15
|
-
const apiKey = process.env.LANONASIS_API_KEY;
|
|
16
|
-
if (apiKey) {
|
|
17
|
-
console.log(chalk.green('ā Using API key authentication'));
|
|
18
|
-
await config.setApiKey(apiKey);
|
|
19
|
-
console.log(chalk.green('ā Authentication configured successfully'));
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
// Choose authentication method
|
|
23
|
-
const authMethod = await inquirer.prompt([
|
|
24
|
-
{
|
|
25
|
-
type: 'list',
|
|
26
|
-
name: 'method',
|
|
27
|
-
message: 'Choose authentication method:',
|
|
28
|
-
choices: [
|
|
29
|
-
{ name: 'š Username/Password (direct login)', value: 'password' },
|
|
30
|
-
{ name: 'š Web Browser (OAuth providers)', value: 'oauth' }
|
|
31
|
-
]
|
|
32
|
-
}
|
|
33
|
-
]);
|
|
34
|
-
if (authMethod.method === 'oauth') {
|
|
35
|
-
await oauthLoginFlow(config);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
// Password-based login flow
|
|
39
11
|
let { email, password } = options;
|
|
40
12
|
// Get credentials if not provided
|
|
41
13
|
if (!email || !password) {
|
|
@@ -159,125 +131,3 @@ async function registerFlow(defaultEmail) {
|
|
|
159
131
|
process.exit(1);
|
|
160
132
|
}
|
|
161
133
|
}
|
|
162
|
-
async function oauthLoginFlow(config) {
|
|
163
|
-
console.log(chalk.blue('š OAuth Authentication'));
|
|
164
|
-
console.log(chalk.gray('This will open your browser for secure authentication'));
|
|
165
|
-
console.log();
|
|
166
|
-
const port = 3721; // CLI callback port
|
|
167
|
-
const redirectUri = `http://localhost:${port}/callback`;
|
|
168
|
-
const state = Math.random().toString(36).substring(2, 15);
|
|
169
|
-
// Construct OAuth URL
|
|
170
|
-
const authUrl = new URL('https://api.lanonasis.com/v1/auth/oauth');
|
|
171
|
-
authUrl.searchParams.set('redirect_uri', redirectUri);
|
|
172
|
-
authUrl.searchParams.set('state', state);
|
|
173
|
-
authUrl.searchParams.set('project_scope', 'maas');
|
|
174
|
-
authUrl.searchParams.set('response_type', 'code');
|
|
175
|
-
const spinner = ora('Starting OAuth flow...').start();
|
|
176
|
-
try {
|
|
177
|
-
// Start local callback server
|
|
178
|
-
const server = createServer();
|
|
179
|
-
let authCode = null;
|
|
180
|
-
let authError = null;
|
|
181
|
-
server.on('request', (req, res) => {
|
|
182
|
-
const url = new URL(req.url, `http://localhost:${port}`);
|
|
183
|
-
if (url.pathname === '/callback') {
|
|
184
|
-
const code = url.searchParams.get('code');
|
|
185
|
-
const error = url.searchParams.get('error');
|
|
186
|
-
const returnedState = url.searchParams.get('state');
|
|
187
|
-
// Validate state parameter
|
|
188
|
-
if (returnedState !== state) {
|
|
189
|
-
authError = 'Invalid state parameter';
|
|
190
|
-
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
191
|
-
res.end('<h1>Authentication Failed</h1><p>Invalid state parameter</p>');
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
if (error) {
|
|
195
|
-
authError = url.searchParams.get('error_description') || error;
|
|
196
|
-
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
197
|
-
res.end(`<h1>Authentication Failed</h1><p>${authError}</p>`);
|
|
198
|
-
}
|
|
199
|
-
else if (code) {
|
|
200
|
-
authCode = code;
|
|
201
|
-
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
202
|
-
res.end('<h1>Authentication Successful</h1><p>You can close this window and return to the CLI.</p>');
|
|
203
|
-
}
|
|
204
|
-
else {
|
|
205
|
-
authError = 'No authorization code received';
|
|
206
|
-
res.writeHead(400, { 'Content-Type': 'text/html' });
|
|
207
|
-
res.end('<h1>Authentication Failed</h1><p>No authorization code received</p>');
|
|
208
|
-
}
|
|
209
|
-
server.close();
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
// Start server
|
|
213
|
-
await new Promise((resolve, reject) => {
|
|
214
|
-
server.listen(port, (err) => {
|
|
215
|
-
if (err)
|
|
216
|
-
reject(err);
|
|
217
|
-
else
|
|
218
|
-
resolve();
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
spinner.text = 'Opening browser for authentication...';
|
|
222
|
-
// Open browser
|
|
223
|
-
await open(authUrl.toString());
|
|
224
|
-
console.log();
|
|
225
|
-
console.log(chalk.yellow('Browser opened for authentication'));
|
|
226
|
-
console.log(chalk.gray(`If browser doesn't open, visit: ${authUrl.toString()}`));
|
|
227
|
-
console.log();
|
|
228
|
-
spinner.text = 'Waiting for authentication...';
|
|
229
|
-
// Wait for callback
|
|
230
|
-
await new Promise((resolve, reject) => {
|
|
231
|
-
server.on('close', () => {
|
|
232
|
-
if (authCode)
|
|
233
|
-
resolve();
|
|
234
|
-
else
|
|
235
|
-
reject(new Error(authError || 'Authentication failed'));
|
|
236
|
-
});
|
|
237
|
-
// Timeout after 5 minutes
|
|
238
|
-
setTimeout(() => {
|
|
239
|
-
server.close();
|
|
240
|
-
reject(new Error('Authentication timeout'));
|
|
241
|
-
}, 300000);
|
|
242
|
-
});
|
|
243
|
-
if (!authCode) {
|
|
244
|
-
throw new Error(authError || 'No authorization code received');
|
|
245
|
-
}
|
|
246
|
-
spinner.text = 'Exchanging authorization code for session...';
|
|
247
|
-
// Exchange code for session
|
|
248
|
-
const response = await fetch('https://api.lanonasis.com/v1/auth/callback', {
|
|
249
|
-
method: 'POST',
|
|
250
|
-
headers: {
|
|
251
|
-
'Content-Type': 'application/json',
|
|
252
|
-
'x-project-scope': 'maas'
|
|
253
|
-
},
|
|
254
|
-
body: JSON.stringify({
|
|
255
|
-
code: authCode,
|
|
256
|
-
state: state,
|
|
257
|
-
project_scope: 'maas'
|
|
258
|
-
}),
|
|
259
|
-
credentials: 'include'
|
|
260
|
-
});
|
|
261
|
-
if (!response.ok) {
|
|
262
|
-
const errorData = await response.json().catch(() => ({ error: 'Unknown error' }));
|
|
263
|
-
throw new Error(errorData.error || `HTTP ${response.status}`);
|
|
264
|
-
}
|
|
265
|
-
const sessionData = await response.json();
|
|
266
|
-
// Store session token
|
|
267
|
-
await config.setToken(sessionData.access_token);
|
|
268
|
-
spinner.succeed('OAuth authentication successful');
|
|
269
|
-
console.log();
|
|
270
|
-
console.log(chalk.green('ā Authenticated successfully via OAuth'));
|
|
271
|
-
console.log(`Welcome, ${sessionData.user.email}!`);
|
|
272
|
-
if (sessionData.user.organization_id) {
|
|
273
|
-
console.log(`Organization: ${sessionData.user.organization_id}`);
|
|
274
|
-
}
|
|
275
|
-
console.log(`Plan: ${sessionData.user.plan || 'free'}`);
|
|
276
|
-
}
|
|
277
|
-
catch (error) {
|
|
278
|
-
spinner.fail('OAuth authentication failed');
|
|
279
|
-
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
280
|
-
console.error(chalk.red('ā OAuth failed:'), errorMessage);
|
|
281
|
-
process.exit(1);
|
|
282
|
-
}
|
|
283
|
-
}
|
package/dist/commands/mcp.js
CHANGED
|
@@ -19,10 +19,9 @@ export function mcpCommands(program) {
|
|
|
19
19
|
const config = new CLIConfig();
|
|
20
20
|
const isAuthenticated = !!config.get('token');
|
|
21
21
|
if (isAuthenticated) {
|
|
22
|
-
console.log(chalk.green('ā Authenticated - Using
|
|
23
|
-
console.log('
|
|
24
|
-
console.log('
|
|
25
|
-
console.log(' with real-time updates enabled');
|
|
22
|
+
console.log(chalk.green('ā Authenticated - Using remote MCP mode'));
|
|
23
|
+
console.log(' Your memory operations will use api.lanonasis.com');
|
|
24
|
+
console.log(' with real-time SSE updates enabled');
|
|
26
25
|
}
|
|
27
26
|
else {
|
|
28
27
|
console.log(chalk.yellow('ā ļø Not authenticated - Using local MCP mode'));
|
|
@@ -31,8 +30,7 @@ export function mcpCommands(program) {
|
|
|
31
30
|
console.log('');
|
|
32
31
|
console.log(chalk.cyan('Available MCP Commands:'));
|
|
33
32
|
console.log(' lanonasis mcp connect # Auto-connect to best mode');
|
|
34
|
-
console.log(' lanonasis mcp connect -r # Force remote
|
|
35
|
-
console.log(' lanonasis mcp connect -w # Force WebSocket mode (enterprise)');
|
|
33
|
+
console.log(' lanonasis mcp connect -r # Force remote mode');
|
|
36
34
|
console.log(' lanonasis mcp connect -l # Force local mode');
|
|
37
35
|
console.log(' lanonasis mcp status # Check connection status');
|
|
38
36
|
console.log(' lanonasis mcp tools # List available tools');
|
|
@@ -50,79 +48,74 @@ export function mcpCommands(program) {
|
|
|
50
48
|
spinner.fail('Failed to auto-connect to MCP');
|
|
51
49
|
}
|
|
52
50
|
}
|
|
53
|
-
catch
|
|
51
|
+
catch {
|
|
54
52
|
spinner.fail('MCP auto-connect failed');
|
|
55
53
|
}
|
|
56
54
|
});
|
|
57
55
|
// Connect command
|
|
58
56
|
mcp.command('connect')
|
|
59
|
-
.description('Connect to MCP server (local, remote
|
|
57
|
+
.description('Connect to MCP server (local, remote, or WebSocket)')
|
|
60
58
|
.option('-l, --local', 'Connect to local MCP server')
|
|
61
|
-
.option('-r, --remote', 'Connect to remote
|
|
62
|
-
.option('-w, --websocket', 'Connect
|
|
59
|
+
.option('-r, --remote', 'Connect to remote MCP server (api.lanonasis.com)')
|
|
60
|
+
.option('-w, --websocket', 'Connect using WebSocket mode for enterprise users')
|
|
63
61
|
.option('-s, --server <path>', 'Local MCP server path')
|
|
64
|
-
.option('-u, --url <url>', 'Remote
|
|
62
|
+
.option('-u, --url <url>', 'Remote/WebSocket server URL')
|
|
65
63
|
.action(async (options) => {
|
|
66
64
|
const spinner = ora('Connecting to MCP server...').start();
|
|
67
65
|
const config = new CLIConfig();
|
|
68
66
|
try {
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
let connectionMode;
|
|
68
|
+
// Determine connection mode - WebSocket takes precedence over remote and local
|
|
71
69
|
if (options.websocket) {
|
|
72
70
|
connectionMode = 'websocket';
|
|
73
71
|
}
|
|
74
72
|
else if (options.remote) {
|
|
75
73
|
connectionMode = 'remote';
|
|
76
74
|
}
|
|
77
|
-
else if (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
75
|
+
else if (options.local) {
|
|
76
|
+
connectionMode = 'local';
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
// Default to remote if authenticated, otherwise local
|
|
80
|
+
connectionMode = !!config.get('token') ? 'remote' : 'local';
|
|
84
81
|
}
|
|
85
|
-
// Save
|
|
82
|
+
// Save preferences
|
|
86
83
|
config.set('mcpConnectionMode', connectionMode);
|
|
87
84
|
if (options.server) {
|
|
88
85
|
config.set('mcpServerPath', options.server);
|
|
89
86
|
}
|
|
90
87
|
if (options.url) {
|
|
91
|
-
|
|
88
|
+
if (connectionMode === 'websocket') {
|
|
89
|
+
config.set('mcpWebSocketUrl', options.url);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
config.set('mcpServerUrl', options.url);
|
|
93
|
+
}
|
|
92
94
|
}
|
|
93
95
|
const client = getMCPClient();
|
|
94
96
|
const connected = await client.connect({
|
|
95
|
-
|
|
97
|
+
connectionMode,
|
|
96
98
|
serverPath: options.server,
|
|
97
99
|
serverUrl: options.url
|
|
98
100
|
});
|
|
99
101
|
if (connected) {
|
|
100
|
-
|
|
101
|
-
local: 'local',
|
|
102
|
-
remote: 'remote SSE',
|
|
103
|
-
websocket: 'WebSocket (Enterprise)'
|
|
104
|
-
};
|
|
105
|
-
spinner.succeed(chalk.green(`Connected to ${modeLabels[connectionMode]} MCP server`));
|
|
102
|
+
spinner.succeed(chalk.green(`Connected to MCP server in ${connectionMode} mode`));
|
|
106
103
|
if (connectionMode === 'remote') {
|
|
107
104
|
console.log(chalk.cyan('ā¹ļø Using remote MCP via api.lanonasis.com'));
|
|
108
105
|
console.log(chalk.cyan('š” SSE endpoint active for real-time updates'));
|
|
109
106
|
}
|
|
110
107
|
else if (connectionMode === 'websocket') {
|
|
111
|
-
console.log(chalk.cyan('
|
|
112
|
-
console.log(chalk.cyan('
|
|
113
|
-
console.log(chalk.cyan('š¢ Enterprise features enabled'));
|
|
108
|
+
console.log(chalk.cyan('ā¹ļø Using enterprise WebSocket MCP server'));
|
|
109
|
+
console.log(chalk.cyan('š” WebSocket connection active with auto-reconnect'));
|
|
114
110
|
}
|
|
115
111
|
}
|
|
116
112
|
else {
|
|
117
113
|
spinner.fail('Failed to connect to MCP server');
|
|
118
|
-
if (connectionMode === 'websocket') {
|
|
119
|
-
console.log(chalk.yellow('š” WebSocket mode requires enterprise access'));
|
|
120
|
-
console.log(chalk.yellow(' Try: lanonasis mcp connect -r (for SSE mode)'));
|
|
121
|
-
}
|
|
122
114
|
}
|
|
123
115
|
}
|
|
124
116
|
catch (error) {
|
|
125
117
|
spinner.fail(`Connection failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
118
|
+
process.exit(1);
|
|
126
119
|
}
|
|
127
120
|
});
|
|
128
121
|
// Disconnect command
|
|
@@ -222,7 +215,7 @@ export function mcpCommands(program) {
|
|
|
222
215
|
try {
|
|
223
216
|
args = JSON.parse(options.args);
|
|
224
217
|
}
|
|
225
|
-
catch
|
|
218
|
+
catch {
|
|
226
219
|
spinner.fail('Invalid JSON arguments');
|
|
227
220
|
process.exit(1);
|
|
228
221
|
}
|
package/dist/commands/memory.js
CHANGED
|
@@ -6,8 +6,6 @@ import wrap from 'word-wrap';
|
|
|
6
6
|
import { format } from 'date-fns';
|
|
7
7
|
import { apiClient } from '../utils/api.js';
|
|
8
8
|
import { formatBytes, truncateText } from '../utils/formatting.js';
|
|
9
|
-
import { output } from '../utils/output.js';
|
|
10
|
-
// Using GetMemoriesParams from api.ts
|
|
11
9
|
export function memoryCommands(program) {
|
|
12
10
|
// Create memory
|
|
13
11
|
program
|
|
@@ -23,11 +21,7 @@ export function memoryCommands(program) {
|
|
|
23
21
|
.action(async (options) => {
|
|
24
22
|
try {
|
|
25
23
|
let { title, content, type, tags, topicId, interactive } = options;
|
|
26
|
-
|
|
27
|
-
if (output.isSilent() && (!title || !content)) {
|
|
28
|
-
throw new Error('Title and content are required when using --output json or --silent');
|
|
29
|
-
}
|
|
30
|
-
if ((interactive || (!title || !content)) && !output.isSilent()) {
|
|
24
|
+
if (interactive || (!title || !content)) {
|
|
31
25
|
const answers = await inquirer.prompt([
|
|
32
26
|
{
|
|
33
27
|
type: 'input',
|
|
@@ -62,7 +56,7 @@ export function memoryCommands(program) {
|
|
|
62
56
|
type = answers.type;
|
|
63
57
|
tags = answers.tags;
|
|
64
58
|
}
|
|
65
|
-
const spinner =
|
|
59
|
+
const spinner = ora('Creating memory...').start();
|
|
66
60
|
const memoryData = {
|
|
67
61
|
title,
|
|
68
62
|
content,
|
|
@@ -75,25 +69,19 @@ export function memoryCommands(program) {
|
|
|
75
69
|
memoryData.topic_id = topicId;
|
|
76
70
|
}
|
|
77
71
|
const memory = await apiClient.createMemory(memoryData);
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
console.log(
|
|
86
|
-
console.log(` ID: ${chalk.cyan(memory.id)}`);
|
|
87
|
-
console.log(` Title: ${memory.title}`);
|
|
88
|
-
console.log(` Type: ${memory.memory_type}`);
|
|
89
|
-
if (memory.tags && memory.tags.length > 0) {
|
|
90
|
-
console.log(` Tags: ${memory.tags.join(', ')}`);
|
|
91
|
-
}
|
|
72
|
+
spinner.succeed('Memory created successfully');
|
|
73
|
+
console.log();
|
|
74
|
+
console.log(chalk.green('ā Memory created:'));
|
|
75
|
+
console.log(` ID: ${chalk.cyan(memory.id)}`);
|
|
76
|
+
console.log(` Title: ${memory.title}`);
|
|
77
|
+
console.log(` Type: ${memory.memory_type}`);
|
|
78
|
+
if (memory.tags && memory.tags.length > 0) {
|
|
79
|
+
console.log(` Tags: ${memory.tags.join(', ')}`);
|
|
92
80
|
}
|
|
93
81
|
}
|
|
94
82
|
catch (error) {
|
|
95
83
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
96
|
-
|
|
84
|
+
console.error(chalk.red('ā Failed to create memory:'), errorMessage);
|
|
97
85
|
process.exit(1);
|
|
98
86
|
}
|
|
99
87
|
});
|
|
@@ -111,41 +99,36 @@ export function memoryCommands(program) {
|
|
|
111
99
|
.option('--order <order>', 'sort order (asc, desc)', 'desc')
|
|
112
100
|
.action(async (options) => {
|
|
113
101
|
try {
|
|
114
|
-
const spinner =
|
|
102
|
+
const spinner = ora('Fetching memories...').start();
|
|
115
103
|
const params = {
|
|
116
|
-
|
|
104
|
+
page: parseInt(options.page || '1'),
|
|
117
105
|
limit: parseInt(options.limit || '20'),
|
|
118
|
-
|
|
119
|
-
|
|
106
|
+
sort: options.sort || 'created_at',
|
|
107
|
+
order: options.order || 'desc'
|
|
120
108
|
};
|
|
121
109
|
if (options.type)
|
|
122
110
|
params.memory_type = options.type;
|
|
123
111
|
if (options.tags)
|
|
124
|
-
params.tags = options.tags
|
|
125
|
-
|
|
112
|
+
params.tags = options.tags;
|
|
113
|
+
if (options.userId)
|
|
114
|
+
params.user_id = options.userId;
|
|
126
115
|
const result = await apiClient.getMemories(params);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
output.json({ data: [], pagination: result.pagination });
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
console.log(chalk.yellow('No memories found'));
|
|
135
|
-
}
|
|
116
|
+
spinner.stop();
|
|
117
|
+
const memories = result.memories || result.data || [];
|
|
118
|
+
if (memories.length === 0) {
|
|
119
|
+
console.log(chalk.yellow('No memories found'));
|
|
136
120
|
return;
|
|
137
121
|
}
|
|
138
|
-
|
|
139
|
-
|
|
122
|
+
console.log(chalk.blue.bold(`\nš Memories (${result.pagination.total} total)`));
|
|
123
|
+
console.log(chalk.gray(`Page ${result.pagination.page || 1} of ${result.pagination.pages || Math.ceil(result.pagination.total / result.pagination.limit)}`));
|
|
124
|
+
console.log();
|
|
125
|
+
const outputFormat = process.env.CLI_OUTPUT_FORMAT || 'table';
|
|
126
|
+
if (outputFormat === 'json') {
|
|
127
|
+
console.log(JSON.stringify(result, null, 2));
|
|
140
128
|
}
|
|
141
129
|
else {
|
|
142
|
-
console.log(chalk.blue.bold(`\nš Memories (${result.pagination.total} total)`));
|
|
143
|
-
const currentPage = Math.floor(result.pagination.offset / result.pagination.limit) + 1;
|
|
144
|
-
const totalPages = Math.ceil(result.pagination.total / result.pagination.limit);
|
|
145
|
-
console.log(chalk.gray(`Page ${currentPage} of ${totalPages}`));
|
|
146
|
-
console.log();
|
|
147
130
|
// Table format
|
|
148
|
-
const tableData =
|
|
131
|
+
const tableData = memories.map((memory) => [
|
|
149
132
|
truncateText(memory.title, 30),
|
|
150
133
|
memory.memory_type,
|
|
151
134
|
memory.tags.slice(0, 3).join(', '),
|
|
@@ -153,6 +136,7 @@ export function memoryCommands(program) {
|
|
|
153
136
|
memory.access_count
|
|
154
137
|
]);
|
|
155
138
|
const tableConfig = {
|
|
139
|
+
header: ['Title', 'Type', 'Tags', 'Created', 'Access'],
|
|
156
140
|
columnDefault: {
|
|
157
141
|
width: 20,
|
|
158
142
|
wrapWord: true
|
|
@@ -165,12 +149,13 @@ export function memoryCommands(program) {
|
|
|
165
149
|
{ width: 8 }
|
|
166
150
|
]
|
|
167
151
|
};
|
|
168
|
-
|
|
169
|
-
|
|
152
|
+
console.log(table([tableConfig.header, ...tableData], {
|
|
153
|
+
columnDefault: tableConfig.columnDefault,
|
|
154
|
+
columns: tableConfig.columns
|
|
155
|
+
}));
|
|
170
156
|
// Pagination info
|
|
171
|
-
if (result.pagination.
|
|
172
|
-
|
|
173
|
-
console.log(chalk.gray(`\nUse --page ${nextPage} for next page`));
|
|
157
|
+
if (result.pagination.pages > 1) {
|
|
158
|
+
console.log(chalk.gray(`\nUse --page ${result.pagination.page + 1} for next page`));
|
|
174
159
|
}
|
|
175
160
|
}
|
|
176
161
|
}
|
|
@@ -191,7 +176,7 @@ export function memoryCommands(program) {
|
|
|
191
176
|
.option('--tags <tags>', 'filter by tags (comma-separated)')
|
|
192
177
|
.action(async (query, options) => {
|
|
193
178
|
try {
|
|
194
|
-
const spinner =
|
|
179
|
+
const spinner = ora(`Searching for "${query}"...`).start();
|
|
195
180
|
const searchOptions = {
|
|
196
181
|
limit: parseInt(options.limit || '20'),
|
|
197
182
|
threshold: parseFloat(options.threshold || '0.7')
|
|
@@ -203,35 +188,25 @@ export function memoryCommands(program) {
|
|
|
203
188
|
searchOptions.tags = options.tags.split(',').map((t) => t.trim());
|
|
204
189
|
}
|
|
205
190
|
const result = await apiClient.searchMemories(query, searchOptions);
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
if (
|
|
209
|
-
|
|
210
|
-
output.json({ data: [], pagination: result.pagination, query });
|
|
211
|
-
}
|
|
212
|
-
else {
|
|
213
|
-
console.log(chalk.yellow('No memories found matching your search'));
|
|
214
|
-
}
|
|
191
|
+
spinner.stop();
|
|
192
|
+
const results = result.results || result.data || [];
|
|
193
|
+
if (results.length === 0) {
|
|
194
|
+
console.log(chalk.yellow('No memories found matching your search'));
|
|
215
195
|
return;
|
|
216
196
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
console.log(chalk.gray(`
|
|
197
|
+
console.log(chalk.blue.bold(`\nš Search Results (${result.total_results || results.length} found)`));
|
|
198
|
+
console.log(chalk.gray(`Query: "${query}" | Search time: ${result.search_time_ms || 0}ms`));
|
|
199
|
+
console.log();
|
|
200
|
+
results.forEach((memory, index) => {
|
|
201
|
+
const score = (memory.relevance_score * 100).toFixed(1);
|
|
202
|
+
console.log(chalk.green(`${index + 1}. ${memory.title}`) + chalk.gray(` (${score}% match)`));
|
|
203
|
+
console.log(chalk.white(` ${truncateText(memory.content, 100)}`));
|
|
204
|
+
console.log(chalk.cyan(` ID: ${memory.id}`) + chalk.gray(` | Type: ${memory.memory_type}`));
|
|
205
|
+
if (memory.tags.length > 0) {
|
|
206
|
+
console.log(chalk.yellow(` Tags: ${memory.tags.join(', ')}`));
|
|
207
|
+
}
|
|
223
208
|
console.log();
|
|
224
|
-
|
|
225
|
-
const score = (memory.relevance_score * 100).toFixed(1);
|
|
226
|
-
console.log(chalk.green(`${index + 1}. ${memory.title}`) + chalk.gray(` (${score}% match)`));
|
|
227
|
-
console.log(chalk.white(` ${truncateText(memory.content, 100)}`));
|
|
228
|
-
console.log(chalk.cyan(` ID: ${memory.id}`) + chalk.gray(` | Type: ${memory.memory_type}`));
|
|
229
|
-
if (memory.tags.length > 0) {
|
|
230
|
-
console.log(chalk.yellow(` Tags: ${memory.tags.join(', ')}`));
|
|
231
|
-
}
|
|
232
|
-
console.log();
|
|
233
|
-
});
|
|
234
|
-
}
|
|
209
|
+
});
|
|
235
210
|
}
|
|
236
211
|
catch (error) {
|
|
237
212
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|